Sunday, July 10, 2011

Automated Astrophotography with Python - Part 7

FOCUSING WITH FOCUSMAX
Achieving great focus is undoubtedly one of the most critical operations for any astronomical imaging application. Using the free application, FocusMax, this step can be reduced to a simple, repeatable, and highly effective operation. Fortunately, FocusMax provides all of the necessary interfaces to quickly automate this important step. The following listing shows a new class named 'cFocuser'. This class is responsible for controlling all aspects of the focus operation. The listing for the 'cFocuser' class is shown below:
import time
import pythoncom
import win32com.client

FOCUSMAXTIME = 150
LASTFOCHFD = -1

ERROR   = True
NOERROR = False

##------------------------------------------------------------------------------
## Class: cFocuser
##------------------------------------------------------------------------------
class cFocuser:
    def __init__(self,printlog):
        printlog.log(0,"Connecting to FocusMax...")
        self.__FM = win32com.client.Dispatch("FocusMax.FocusControl")

    def checkFocusStar(self,printlog):
        printlog.log(0,"Checking focus by measuring HFD...")
        HFD = []
        count = 0
        # take 7 HFD measurements, sort the list, report the median value
        while count < 7:
            busy = self.__FM.SingleExposeAsyncStatus        
            self.__FM.SingleExposeAsync()
            # wait for measurement to end
            startTime = time.time()
            busy = -1
            while busy == -1 and (time.time() - startTime) < FOCUSMAXTIME:
                time.sleep(1)
                busy = self.__FM.SingleExposeAsyncStatus
            if self.__FM.SingleExposeAsyncStatus != 1:
                printlog.log(0,"HFD Measurement failed or timed out")
                return -1        
            result = self.__FM.HalfFluxDiameter
            flux = self.__FM.TotalFlux
            time.sleep(0.5)
            printlog.log(1,"HFD Measurement: %0.2f  Total Flux: %d" %
                         (result,flux))
            if flux > 20000:
                HFD.append(result)
                count = count + 1
        HFD.sort()
        j = len(HFD)
        if not j%2:
            median = (HFD[(j/2)-1]+HFD[j/2])/2.0
        else:
            median = HFD[j/2]
        focuserPos = self.__FM.Position
        printlog.log(0,"Median HFD = %0.2f at Pos = %d" % (median,focuserPos))
        return median

    def focusStar(self,printlog):
        global LASTFOCHFD
        printlog.log(0,"Starting FocusMax Focus method...")
        try:
            busy = self.__FM.FocusAsyncStatus        
            start = self.__FM.FocusAsync()
        except pythoncom.com_error, (hr, msg, exc, arg):
            printlog.log(0,"ERROR: %s" % exc[2])
            return ERROR
        else:
            if start:
                # wait for focus method to end
                startTime = time.time()
                busy = -1
                while busy == -1 and (time.time() - startTime) < FOCUSMAXTIME:
                    time.sleep(1)
                    busy = self.__FM.FocusAsyncStatus
                if self.__FM.FocusAsyncStatus != 1:
                    printlog.log(0,"Focus method failed or timed out.")
                    return ERROR        
                printlog.log(0,"FocusMax Focus method complete...")
                tgtHFD = self.__FM.HalfFluxDiameter
                if tgtHFD > 0.5:
                    median = self.checkFocusStar(printlog)
                    if median == -1:
                        return ERROR
                    elif median < 0.5 or median > 4.0:
                        printlog.log(0,"ERROR: Focus results out of range.")
                        return ERROR
                    else:
                        LASTFOCHFD = median
                        return NOERROR
                else:
                    printlog.log(0,"ERROR: Focus method failed.")
                    return ERROR
            else:
                printlog.log(0,"ERROR: FocusMax Focus method failed to start.")
                return ERROR

##
## END OF 'cFocuser' Class
##
The constructor for 'cFocuser' is called whenever a new object of the class is instantiated and is used to create a new object (__FM) that is bound to the FocusMax object (FocusControl) required by this class. Following the constructor, the 'checkFocusStar()' method performs a check of focus quality by invoking the 'SingleExposeAsync()' method. This method is called multiple times to take a series of measurement of a suitable focus star that has previously been centered in the field of view. After waiting for the exposure to complete, the method checks the properties that holds the measurement's HFD and total flux (brightness). The measurements are written to the screen and to the log file and the HFD value is appended to a list data structure. After seven measurements are collected, the HFD list is sorted and the median value is extracted and returned to the method's calling routine.

The other method in this class is named 'focusStar()' and performs the actual focus operation (nearly equivalent to clicking on the 'Focus' button on FocusMax's front panel). This method invokes the FocusMax 'FocusAsync()' method to kick off the focus operation. The method then polls the 'FocusAsyncStatus' property to determine when the focus operation is complete. If the focus star's HFD is a reasonable value, the 'checkFocusStar()' method is called to determine the median of seven consecutive HFD measurements. The median value is then assigned to a global variable, 'LASTFOCHFD'.

UNIT TESTING
Unlike unit tests for modules presented up to this point, the unit test for this class can not be simulated. Therefore, actual stars must be available to adequately verify the operation of this module. Due to poor weather and equipment failures I have been unable to generate and verify a unit test for the 'cFocuser' class. As soon as I am able, I will update this post with a unit test.

WHAT'S NEXT?
In the next post, I will demonstrate the method that I use to automatically select focus stars. This method will work with previous modules and result in a means to select a suitable focus star near the object to image, slew to the star, focus and determine median HFD, then slew to the main object to begin imaging. This procedure, along with periodic slews back to the focus star to check and ,if necessary, re-focus, is the main sequence of events that I use for my astronomical imaging script.

2 comments:

  1. Hi Jon,
    many thanks for this series of great tutorial articles. When do you plan to publish the next post demonstrating automated focusing ?
    --Sam

    ReplyDelete
  2. Sam,

    Sorry about not keeping up with these posts. I'll try to put together the next post in this series in the next week or so. Thanks for your interest in this material. If there's anything specific you would like to see in a post, let me know.

    -- Jon

    ReplyDelete