Tuesday, May 31, 2011

Automated Astrophotography with Python - Part 2a

THE cMOUNT CLASS
The next piece of code we will look at is the 'cMount' class. This class will interact with various objects exposed by The Sky6 planetarium and telescope control software. The Python code for 'cMount' is shown below. (Note: Docstrings have been removed from this listing for brevity and readability purposes.)
import time
import win32com.client

ERROR = True
NOERROR = False

##------------------------------------------------------------------------------
## Class: cMount
##------------------------------------------------------------------------------
class cMount:
    def __init__(self):
        self.__info = {}
        self.__objectName = ''
        print "Connecting to The Sky6..."
        self.__SKYCHART = win32com.client.Dispatch("TheSky6.StarChart")
        self.__SKYINFO = win32com.client.Dispatch("TheSky6.ObjectInformation")

    def findObject(self,objToFind):
        self.__objectName = objToFind
        if len(self.__objectName) > 0:
            try:
                self.__SKYCHART.Find(self.__objectName)
                for index in range(189):
                    skyProperty = self.__SKYINFO.Property(index)
                    if self.__SKYINFO.PropertyApplies(index):
                        self.__info[self.__SKYINFO.PropertyName(index)] = \
                                    skyProperty
                return NOERROR     
            except:
                return ERROR
        
    def getCoordinates(self):
        raDec = {}
        if self.__info.has_key('RA (current epoch)'):
            raDec['RA Now'] = self.__info['RA (current epoch)']
        if self.__info.has_key('Dec (current epoch)'):
            raDec['DEC Now'] = self.__info['Dec (current epoch)']
        if self.__info.has_key('RA (epoch 2000)'):
            raDec['RA J2000'] = self.__info['RA (epoch 2000)']
        if self.__info.has_key('Dec (epoch 2000)'):
            raDec['DEC J2000'] = self.__info['Dec (epoch 2000)']
        return raDec
    
    def getAltAzimuth(self):
        altAzimuth = {}
        if self.__info.has_key('Altitude'):
            altAzimuth['Altitude'] = self.__info['Altitude']
        if self.__info.has_key('Azimuth'):
            altAzimuth['Azimuth'] = self.__info['Azimuth']
        return altAzimuth
        
    def getTimes(self):
        objTimes = {}
        if self.__info.has_key('Date'):
            objTimes['Date Now'] = self.__info['Date']
        if self.__info.has_key('Time'):
            objTimes['Time Now'] = self.__info['Time']
        if self.__info.has_key('Transit time'):
            objTimes['Transit time'] = self.__info['Transit time']
        if self.__info.has_key('Rise time'):
            objTimes['Rise time'] = self.__info['Rise time']
        if self.__info.has_key('Set time'):
            objTimes['Set time'] = self.__info['Set time']
        return objTimes
        
    def getImagingLocation(self):
        return self.__SKYCHART.DocumentProperty(63)

    def checkObjectInWest(self):
        azimuth = self.getAltAzimuth()['Azimuth']
        if azimuth >= 180.00 and azimuth < 360.00:
            return True
        else:
            return False

##
##    END OF 'cMount' Class
##
The first two lines of the listing are the same 'import' statements that we saw in the 'cCamera' class. The listing for the 'cMount' class follows the 'import' statements and consists of seven methods. The '__init__' method is the constructor for 'cMount' and is executed whenever an instance of the 'cMount' object is created. This method creates a class attribute '__info' which is initialized as an empty dictionary data structure and '__objName' which will hold a string representation of the object that The Sky6 will find. This method also creates a '__SKYCHART' object that is bound to the Sky6's 'Skychart' object and a '__SKYINFO' object that is bound to the Sky6's 'Skyinfo' object. (See help for The Sky6 under scripting for more information regarding attributes and methods relating to these two objects.)

The next method is 'findObject' which takes as an argument the string representation of the object to find. After making sure the string 'objToFind' is not empty, the Sky6's 'Find' method is used to actually locate the object in its database. If the object is found, the remainder of the method loads all of the properties relating to that object into the '__info' dictionary. The contents of this dictionary will be used by most of the remaining methods of the 'cMount' class.

The next method, 'getCoordinates', strips out the RA and Declination information from the '__info' dictionary and builds a new dictionary called 'raDec' to hold this data. Likewise, the 'getAltAzimuth' and 'getTimes' methods similarly strip relevant information from the '__info' dictionary and build new dictionaries called 'altAzimuth' and 'objTimes' respectively.

Finally, the 'getImagingLocation' method extracts the current location in use by the Sky6's planetarium and returns it to the calling routine. Also, the 'checkObjectInWest' method uses the 'getAltAzimuth' method to determine if the located object is in the eastern or western sky. It returns TRUE if the object is in the western sky and FALSE if the object is in the eastern sky.

cMOUNT UNIT TEST
The following listing shows the unit test code for this version of the 'cMount' class. After creating an instance of the class, the user is prompted to enter an object for The Sky6 to locate. The test then prints out the current location for The Sky6 then locates the object in its database (or returns an error if it cannot be located.) Next, the script checks whether the object is in the eastern or western sky then prints out the current system time and date before printing out the object's rise, transit, and set times (or transit time only for circumpolar objects). The object's RA and Declination for the current epoch and for the year 2000 epoch are then printed. Finally, the 'getAltAzimuth' method is called and the object's current altitude and azimuth are printed to screen.
if __name__ == "__main__":
    
    # create an instance of the cMount object
    testMount = cMount()

    # prompt for name of object to locate
    print
    obj = raw_input("Enter object to find: ")
    
    # test getImagingLocation method
    print
    print "The Sky6 Location: %s" % testMount.getImagingLocation()

    # test findObject method
    print
    if not testMount.findObject(obj):
        # test checkObjectInWest method
        if testMount.checkObjectInWest():
            print "%s is west of the meridian" % obj
        else:
            print "%s is east of the meridian" % obj

        # test getTimes function
        times = testMount.getTimes()
        print "System Date = %s" % times['Date Now']
        print "System Time = %s" % times['Time Now']
        if times.has_key('Rise time'):
            print "%s rise time    = %0.3f" % (obj,times['Rise time'])
        if times.has_key('Transit time'):
            print "%s transit time = %0.3f" % (obj,times['Transit time'])
        if times.has_key('Set time'):
            print "%s set time     = %0.3f" % (obj,times['Set time'])
        
        # test getCoordinates function
        coords = testMount.getCoordinates()
        print
        print "%s RA  (current epoch) = %0.3f" % (obj,coords['RA Now'])
        print "%s Dec (current epoch) = %0.3f" % (obj,coords['DEC Now'])
        print "%s RA  (epoch 2000)    = %0.3f" % (obj,coords['RA J2000'])
        print "%s Dec (epoch 2000)    = %0.3f" % (obj,coords['DEC J2000'])
        
        # test getAltAzimuth function
        altazi = testMount.getAltAzimuth()
        print
        print "%s Altitude = %0.3f" % (obj,altazi['Altitude'])
        print "%s Azimuth  = %0.3f" % (obj,altazi['Azimuth'])
        print
    else:
        print "%s could not be found." % obj
RUNNING THE SCRIPT
Testing this version of the 'cMount' class is simply a matter of loading the source code into the Python IDE and running the module by hitting the F5 key. If the Sky6 is not already running, it will be loaded before the unit test executes. The source listing for 'cMount' can be downloaded from cMount_2a.zip. Experiment with the script by running it multiple times and selecting different objects for The Sky6 to locate and display information about.

WHAT'S NEXT?
In the next post I will continue to expand the 'cMount' class by introducing more methods and properties that utilize planetarium and telescope control features of the Sky6. I will also create a new unit test listing to verify correct operation of the features added to this class.

CLICK HERE FOR NEXT POST IN THIS SERIES

2 comments:

  1. Hi Jon,
    Really appreciate on the tips you have given so far. Great work.

    A question. The codes here are using theSky database for retrieving the DSO information. Will is be similar if I used MiniSAC database (like what Joe Ulowetz did in his example)?

    I am writing my own script based on yours and Joe's, and am not planning to use TheSky.

    Thanks

    ReplyDelete
  2. Hi Shah,
    Sorry for the delayed response to your question...

    I'm not only using the Sky for its DSO database. I'm also using it for its utility functions (conversion of equatorial coordinates from NOW to and from Epoch 2000, calculation of altitude and azimuth, etc.) and for command and control of the mount. As long as you have another scriptable software (or set of software) that roughly emulates the Sky's capabilities you shouldn't have any problems. (Although, obviously, your function calls will be very different from my calls to the Sky.)

    Hope that helps some. Good luck with your script...

    ReplyDelete