Saturday, June 11, 2011

Automated Astrophotography with Python - Part 3

USING CLASSES
In this post, I'll demonstrate how to use the classes (cCamera and cMount) developed in previous posts to generate a practical script that slews to a given object then starts imaging with or without autoguiding. In order for the classes and scripts to work together correctly, there are a few initial steps to perform.

SETUP
The first step is to set up a local directory that will contain source code for classes and scripts. This directory can be set up anywhere on your hard drive but I prefer to locate this directory just off of the root directory of my C: drive. On my computer I have set up a directory named 'c:\imaging_scripts'. The classes and script that go with with this post (chapter_3.zip) should be copied to this directory. The second step is to set up Python so that it knows to look in this new directory. This is done my setting an environment variable called 'PYTHONPATH' with the path to the directory. These are the steps to set up 'PYTHONPATH' on a Windows 7 machine (setup on a Windows XP computer should be very similar):
  1. Click 'Start' button
  2. Right click on 'Computer' then click on 'Properties'
  3. Click on 'Advanced system settings'
  4. Click on the 'Environment Variables...' button
  5. Under 'User variables for...' click the 'New...' button
  6. In the 'Variable name:' field enter 'PYTHONPATH'
  7. in the 'Variable value:' field enter 'C:\Python27;C:\imaging_scripts' or whatever name you used for your directory
  8. Click the 'OK' button then reboot your computer
  9. Test the path setup by loading the Python IDE. At the '>>>' prompt type 'import cMount_3'. If the IDE comes back with another '>>>' prompt without throwing an exception, the path is set up correctly.

A listing of the complete script that calls both the 'cMount' and 'cCamera' classes is given below:
import cMount_3
import cCamera_3

filterDictionary = {'R':0,'G':1,'B':2,'L':3,'HA':4}

# create an instance of the mount and camera class
testMount = cMount_3.cMount()
testCamera = cCamera_3.cCamera()

# get imaging location
print
print "The Sky6 location is %s" % testMount.getImagingLocation()


# prompt for CCD temperature (type 'skip' if no change to existing temp)
print
temp = raw_input("Enter CCD temperature (ex. '-20C','-5','skip'): ")
testCamera.setCCDTemp(temp)
testCamera.gotoCCDTemp()

# Test 1 - Slew and image with no autoguiding
# prompt for name of object to locate
print
obj = raw_input("Enter name of object to image: ")
if not testMount.findObject(obj):
    if testMount.checkObjectInWest():
        print "%s is west of the meridian" % obj
    else:
        print "%s is east of the meridian" % obj

    times = testMount.getTimes()
    print "System Date = %s" % times['Date Now']
    print "System Time = %s" % times['Time Now']
    if times.has_key('Rise time'):
        hms = testMount.getAngleToDMS(times['Rise time'])
        print "%s rise time    = %02.0f:%02.0f:%02.0f" % (obj,hms[0],hms[1],
                                                          hms[2])
    if times.has_key('Transit time'):
        hms = testMount.getAngleToDMS(times['Transit time'])
        print "%s transit time = %02.0f:%02.0f:%02.0f" % (obj,hms[0],hms[1],
                                                          hms[2])
    if times.has_key('Set time'):
        hms = testMount.getAngleToDMS(times['Set time'])
        print "%s set time     = %02.0f:%02.0f:%02.0f" % (obj,hms[0],hms[1],
                                                          hms[2])

# prompt for length of exposure
exp = raw_input("Enter length of exposure: ")
try:
    exp = float(exp)
except:
    print "ERROR: Invalid exposure length..."
    raise EnvironmentError, 'Halting program'

# prompt for binning of image
binning = raw_input("Enter binning for exposure (1/2/3): ")
if binning in ('1','2','3'):
    try:
        binmode = int(binning)
    except:
        print "ERROR: Invalid binning..."
        raise EnvironmentError, 'Halting program'

# prompt for number of exposures
noImages = raw_input("Enter the number of exposures: ")
try:
    noImages = int(noImages)
except:
    print "ERROR: Invalid number of images..."
    raise EnvironmentError, 'Halting program'

# prompt for filter
filterName = raw_input("Enter filter for exposures (L/R/G/B/Ha): ")
if not filterName.upper() in ('R','G','B','L','HA'):
    print "ERROR: Invalid filter designation..."
    raise EnvironmentError, 'Halting program'

# begin test
print
if not testMount.slewToObject(obj):
    testCamera.setBinning(binmode)
    testCamera.setFullFrame()
    fileName = "%s_%s_%sx%s" % (obj,filterName,binning,binning)
    for i in range(noImages):
        testCamera.exposeLight(exp,filterDictionary[filterName.upper()],
                               fileName)
else:
    print "ERROR: Object not found or error slewing to object"
    raise EnvironmentError, 'Halting program'


# Test 2 - Slew and image with autoguiding and auto guide star selection
# prompt for name of object to locate
print
obj = raw_input("Enter name of object to image: ")
if not testMount.findObject(obj):
    if testMount.checkObjectInWest():
        print "%s is west of the meridian" % obj
    else:
        print "%s is east of the meridian" % obj

    times = testMount.getTimes()
    print "System Date = %s" % times['Date Now']
    print "System Time = %s" % times['Time Now']
    if times.has_key('Rise time'):
        hms = testMount.getAngleToDMS(times['Rise time'])
        print "%s rise time    = %02.0f:%02.0f:%02.0f" % (obj,hms[0],hms[1],
                                                          hms[2])
    if times.has_key('Transit time'):
        hms = testMount.getAngleToDMS(times['Transit time'])
        print "%s transit time = %02.0f:%02.0f:%02.0f" % (obj,hms[0],hms[1],
                                                          hms[2])
    if times.has_key('Set time'):
        hms = testMount.getAngleToDMS(times['Set time'])
        print "%s set time     = %02.0f:%02.0f:%02.0f" % (obj,hms[0],hms[1],
                                                          hms[2])

# prompt for length of exposure
exp = raw_input("Enter length of exposure: ")
try:
    exp = float(exp)
except:
    print "ERROR: Invalid exposure length..."
    raise EnvironmentError, 'Halting program'

# prompt for binning of image
binning = raw_input("Enter binning for exposure (1/2/3): ")
if binning in ('1','2','3'):
    try:
        binmode = int(binning)
    except:
        print "ERROR: Invalid binning..."
        raise EnvironmentError, 'Halting program'

# prompt for number of exposures
noImages = raw_input("Enter the number of exposures: ")
try:
    noImages = int(noImages)
except:
    print "ERROR: Invalid number of images..."
    raise EnvironmentError, 'Halting program'

# prompt for filter
filterName = raw_input("Enter filter for exposures (L/R/G/B/Ha): ")
if not filterName.upper() in ('R','G','B','L','HA'):
    print "ERROR: Invalid filter designation..."
    raise EnvironmentError, 'Halting program'

#begin test
print
if not testMount.slewToObject(obj):
    # Start camera autoguiding with auto guide star select
    # Guide exposure = 2.0 second
    # Reset guide star positions for next test
    testCamera.resetGuideStar()
    if testCamera.autoGuide(True,2.0):
        testCamera.stopAutoGuide()
    # Make sure autoguider is running
    if testCamera.checkGuiderRunning():
        testCamera.setBinning(binmode)
        testCamera.setFullFrame()
        fileName = "%s_%s_%sx%s" % (obj,filterName,binning,binning)
        for i in range(noImages):
            testCamera.exposeLight(exp,filterDictionary[filterName.upper()],
                                   fileName)
        # Stop autoguider after all images complete
        testCamera.stopAutoGuide()    
    else:
        print "ERROR - Autoguider not running as expected"
else:
    print "ERROR: Object not found or error slewing to object"
    raise EnvironmentError, 'Halting program'

# Test 3 - Slew and image with autoguiding and manual guide star selection
# prompt for name of object to locate
print
obj = raw_input("Enter name of object to image: ")
if not testMount.findObject(obj):
    if testMount.checkObjectInWest():
        print "%s is west of the meridian" % obj
    else:
        print "%s is east of the meridian" % obj

    times = testMount.getTimes()
    print "System Date = %s" % times['Date Now']
    print "System Time = %s" % times['Time Now']
    if times.has_key('Rise time'):
        hms = testMount.getAngleToDMS(times['Rise time'])
        print "%s rise time    = %02.0f:%02.0f:%02.0f" % (obj,hms[0],hms[1],
                                                          hms[2])
    if times.has_key('Transit time'):
        hms = testMount.getAngleToDMS(times['Transit time'])
        print "%s transit time = %02.0f:%02.0f:%02.0f" % (obj,hms[0],hms[1],
                                                          hms[2])
    if times.has_key('Set time'):
        hms = testMount.getAngleToDMS(times['Set time'])
        print "%s set time     = %02.0f:%02.0f:%02.0f" % (obj,hms[0],hms[1],
                                                          hms[2])
        
# prompt for length of exposure
exp = raw_input("Enter length of exposure: ")
try:
    exp = float(exp)
except:
    print "ERROR: Invalid exposure length..."
    raise EnvironmentError, 'Halting program'

# prompt for binning of image
binning = raw_input("Enter binning for exposure (1/2/3): ")
if binning in ('1','2','3'):
    try:
        binmode = int(binning)
    except:
        print "ERROR: Invalid binning..."
        raise EnvironmentError, 'Halting program'

# prompt for number of exposures
noImages = raw_input("Enter the number of exposures: ")
try:
    noImages = int(noImages)
except:
    print "ERROR: Invalid number of images..."
    raise EnvironmentError, 'Halting program'

# prompt for filter
filterName = raw_input("Enter filter for exposures (L/R/G/B/Ha): ")
if not filterName.upper() in ('R','G','B','L','HA'):
    print "ERROR: Invalid filter designation..."
    raise EnvironmentError, 'Halting program'

# begin test
print
if not testMount.slewToObject(obj):
    # Start camera autoguiding with manual guide star select
    # Reset guide star positions for next test
    testCamera.resetGuideStar()
    if testCamera.autoGuide(False,0.0):
        testCamera.stopAutoGuide()
    # Make sure autoguider is running
    if testCamera.checkGuiderRunning():
        testCamera.setBinning(binmode)
        testCamera.setFullFrame()
        fileName = "%s_%s_%sx%s" % (obj,filterName,binning,binning)
        for i in range(noImages):
            testCamera.exposeLight(exp,filterDictionary[filterName.upper()],
                                   fileName)
        # Stop autoguider after all images complete
        testCamera.stopAutoGuide()    
    else:
        print "ERROR - Autoguider not running as expected"
else:
    print "ERROR: Object not found or error slewing to object"
    raise EnvironmentError, 'Halting program'

# warm the CCD to ambient
print
testCamera.warmCCD()

print
print "Script Complete!"
The first two lines of the script contain the import statements that reference the filenames (less the .py extension) containing the 'cMount' and 'cCamera' classes. At this point, instances of the two class are created and a dictionary is set up that indexes a letter or string representing the filter to the filter wheel slot number. Next, the current Sky6 location is printed then the user is prompted to enter a CCD temperature (enter 'skip' if no temperature change is desired). After setting the temperature and waiting for it to stabilize, the script begins the first of three tests.

The first test begins by prompting the user for the name of an object. If the object is located in the Sky6 database, the data related to the object (side of meridian, current date/time, rise/transit/set times) are printed to screen. The user is then prompted to enter the length of the exposure, the binning, the number of exposures, and the filter to be used. The testing then begins by commanding a slew to the object then the images are collected per user inputs with no autoguiding enabled. For the second test, the first test is repeated similarly but this time the images are collected with autoguiding and auto guide star selection (2.0 second guide exposures) enabled. The third test is identical to the first two tests except that autoguiding is enable with manual guide star selection.

RUNNING THE SCRIPT
Before executing this script, the Sky6 simulator must be manually connected to the telescope control system using the same procedure as described in the previous post. Also, the imaging and guide cameras in MaxIm DL should be first connected to the simulated cameras. The python script can then be executed from the IDE in the normal manner. The source listing for files used by this script can be downloaded from chapter_3.zip. Experiment with the script by running it multiple times and selecting different objects for The Sky6 to locate, slew to, and image.

WHAT'S NEXT?
One problem with this script is the repetitive and error-prone requirement to have the user manually enter object name, binning, exposure length, etc. for each test. In the next post, I will develop a new class that will allow this information to be read from an input text file. This will be a necessary feature to facilitate full automation of the imaging process.

CLICK HERE FOR NEXT POST IN THIS SERIES

No comments:

Post a Comment