Friday, May 30, 2014

Star Autoguide with RPi - (6) Operation Procedure

Star Autoguide with RPi - Operation Procedure

  • Note:
    • I do not know how to setup 'autostart' to switch display to LCD display, login into startX, and execute my python program automatically on RPi.  With my wireless keyboard and without external monitor, after turn on RPi, wait until RPi's LED stop blanking (it means it is ready to login), and then login blindly until LCD display has been turn on again.  If you know how to setup auto start on RPi.  It will save you a lot of trouble.
    • I develop all my program on PC and then transfer to RPi.  It save me a lot of programming time.  It is much easier working or troubleshoot on PC.
  • Prepare Raspberry Pi: Connect camera, key board, power, and so on
  • Login as pi
  • Enter password raspberry
  • Login as root for control GPIO pins, sudo su
  • I have created a short cut in name 'exe' by nano, just like BAT file in PC.  In my case, it will activate 2.8"LCD touch screen, and execute startX program) exe     
  • in StatX, double click the Python program, say 'st7.py' (you can use any filename)) 

    • I: mean current maximum intensity pixels
    • Max I: The maximum of previous I
    • Integ: It is the number of image integration.  Default is 5 now

  • Press up/down key to increase/decrease the image intensity.  After press up arrowhead for three time.  'Integ' has been increased from 5 to 8 now, and the image is much brighter compare to previous picture. 
  • Press space switch to next window for star selection, and press any key for the next star


    • Press 'p' twice  to select a star

    • Auto guiding now.  Only show 16x zoom in ROI image area.  Integ value will be adjusted by the software automatically.  Larger number means darker star, and the star guiding response will be a bit slower.

    • Press 'r' for re-select another star or press 'Esc' to quit this program
       
    • Below video conclude this project.  

    Enjoy...


    4/1/2016 Update: You may find another camera option from Amazon.com. http://www.amazon.com/XCSOURCE-Telescope-Electronic-Eyepiece-DC625/dp/B00ZUF90J2?ie=UTF8&psc=1&redirect=true&ref_=ox_sc_act_title_3&smid=A38N3TNH1N2LMX



    Star Autoguide with RPi - (5) Python code

    #################################################################################
    #
    #  Auto Guide with RPi
    #  by Samson Yang 5/30/2014
    #  Hardware requirement:
    #        Raspberry Pi Model B
    #        USB camera
    #        Wireless keyboard (optional)
    #        Adafruit PiTFT Mini Kit - 320x240 2.8" TFT (optional)
    #        Custom PCB
    #
    #  Software:
    #        python 2.7, OpenCV V2.4.6 and numpy.
    #
    #  Blogger: https://www.blogger.com/blogger.g?blogID=634479844432177891#allposts
    #  Youtube: https://www.youtube.com/watch?v=1k9vwmUJgbM
    #
    #  Note:
    #    Author is just a beginner to Python.  Please feel free to modify this program for your own project.
    #    Due to time constrain and limited programming skill, there will be no technical support for these code.
    #    Have fun and good luck.  :)
    #
    #################################################################################

    GP=1 #see below note
    #GP=0 For PC only, GPIO will not be activated
    #GP=1 For RPi only, GPIO will control telescope mount.  EQG has been tested.
    import cv2
    import cv2.cv as cv
    import numpy as np
    from random import randrange

    # For GPIO Control
    # Dec-, green, GPIO#22
    # Dec+, yellow, GPIO#4
    # Ra-, blue, GPIO#17
    # Ra+, red, GPIO#18
    if GP==1:
        from time import sleep
        import RPi.GPIO as GPIO
        GPIO.setmode(GPIO.BCM)

        #Assign GPIO number
        RP=18  #R+
        RN=17  #R-
        DP=4   #D+
        DN=22  #D-
        #Setip up for GPIO Pin
        GPIO.setup(RP, GPIO.OUT)
        GPIO.setup(RN, GPIO.OUT)
        GPIO.setup(DP, GPIO.OUT)
        GPIO.setup(DN, GPIO.OUT)
        GPIO.output(RP, False)
        GPIO.output(RN, False)
        GPIO.output(DP, False)
        GPIO.output(DN, False)

    #setup camera
    print "Initializing camera......."
    np.set_printoptions(threshold='nan')

    if GP==1: #work on RPi
        cap = cv2.VideoCapture(0) #for camera 0 on RPi
    if GP==0: #work on PC
        cap = cv2.VideoCapture(1) #for camera 0 on RPi

    ret, frame = cap.read()
    print "Camera is initialized."

    qq=1

    while qq==1:
        #while loop until find a good star
        X1=0
        NoIntegration=5
        while X1==0:
            maxStartBrightness=0
            while True:
             
                # for image integration
                temptrackStar=0
                countloop=0
                for countloop in range (0,NoIntegration):              
                    ret, frame = cap.read()
                    imggray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)              
                    temptrackStar=temptrackStar+imggray
                imggray=temptrackStar
             
                maxBright = imggray.max()
                ratio=255/maxBright #normalization ratio
                imggray = imggray*ratio #normalized image
                if maxStartBrightness<maxBright:
                    maxStartBrightness=maxBright

                imggray_diaply=imggray.copy()
                displaytext = "I: "+str(maxBright)+". Max I: "+str(maxStartBrightness)+" Integ: "+str(NoIntegration)
                cv2.putText(imggray_diaply,displaytext, (5,25), cv2.FONT_HERSHEY_SIMPLEX, 1, 255,1)
                cv2.putText(imggray_diaply,"Focus, then press space bar!", (5,60), cv2.FONT_HERSHEY_SIMPLEX, 1, 255,1)

                #Reduce screen size
                newx,newy = imggray_diaply.shape[1]/2,imggray_diaply.shape[0]/2
                img3 = cv2.resize(imggray_diaply,(newx,newy))
                cv2.imshow('Check image',img3)
             
                key = cv.WaitKey(20)
                if key == 32:  #press space to move to next step
                    break
             
                if key == 2490368: #press up for increase image brightness
                    NoIntegration=NoIntegration+1

                if key == 2621440: #press down for decrease image brightness
                    NoIntegration=NoIntegration-1
                    if NoIntegration<=1:
                        NoIntegration=1
                     
            cv.DestroyAllWindows()
            img=imggray.copy()

            #Star location
            trackX=np.dtype('float16')
            trackY=np.dtype('float16')
            trackX=0.
            trackY=0.
            img2=img.copy() #For display only
            cv.WaitKey(20)
            maxpix = img
            bY, bX  = img.shape

            #Finding all star
            uX = 1.0*(img.max(axis=0)) #sum o column
            uY = 1.0*(img.max(axis=1)) #sum of row
            nX = 0
            nY = 0
            X = np.zeros(10000)
            Y = np.zeros(10000)

            #Set star intensity threshold (self setting)
            # criteria is >50% of max intensity
            Threshold = uX.max()*0.5
            Sub_Search_range=15

            for c in range(0,bX-1):
                if (uX[c] >=Threshold)*(uX[c+1]<=Threshold):
                    X[nX]=c
                    nX=nX+1
                else:
                    uX[c]=0

            for r in range(0,bY-1):
                if (uY[r] >=Threshold)*(uY[r+1]<=Threshold):
                    Y[nY]=r
                    nY=nY+1
                else:
                    uY[r]=0

            uY[len(uY)-1]=0
            uX[len(uX)-1]=0

            # Search for star
            starCount=0
            X1=0
            Y1=0
            for c in range (0,nX):
                for r in range(0,nY):
                    img2=img.copy()
                    ulX=X[c]-Sub_Search_range
                    ulY=Y[r]-Sub_Search_range
                    lrX=X[c]+Sub_Search_range
                    lrY=Y[r]+Sub_Search_range
                    #print ulX,lrX,ulY,lrY
                    if ulX<=0:
                        ulX=0
                    if ulY<=0:
                        ulY=0
                    if lrX>=bX-1:
                        lrX=bX-1
                    if lrY>=bY-1:
                        lrY=bY-1

                    temp = img[ulY:lrY,ulX:lrX].max()
                    if temp>=Threshold:
                        trackY = Y[r]
                        trackX = X[c]
                        starCount=starCount+1
                        cv2.rectangle(img2, (int(ulX),int(ulY)),(int(lrX),int(lrY)), 255, 1)
                        cv2.putText(img2,"Press any key for search next star.", (5,25), cv2.FONT_HERSHEY_SIMPLEX, 1, 255,1)
                        cv2.putText(img2,"Press 'p' to pick your star for guiding.", (5,60), cv2.FONT_HERSHEY_SIMPLEX, 1, 255,1)

                        #Reduce screen size
                        newx,newy = img2.shape[1]/2,img2.shape[0]/2
                        img3 = cv2.resize(img2,(newx,newy))                      
                        cv2.imshow("stars",img3)

                        #print "Press 'p' to choose your star:"
                        if X1==0:
                            print cv.WaitKey()
                            if cv.WaitKey()==112:
                                Y1=Y[r]
                                X1=X[c]                          

        trackY=Y1
        trackX=X1
        cv2.destroyAllWindows()
        #end of while loop
     
        ################################
        cv2.destroyAllWindows()
        trackBoxC=100 #tracking box size
        trackBoxR=160 #tracking box size
        refX=0.
        refY=0.
        averageX=0
        averageY=0
        averageCount=0
        temptrackStar=0
        checkS=0
        blureFactor=2 # The most stable factor is 2
        while True:
            #Star image integration (make it brighter)
            starInteg=0
            temptrackStar=0
            checkS=0
            while checkS<127:
                ret, frame = cap.read()
                img2 = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                trackStar = img2[
                    trackY-int(trackBoxC/2):trackY+int(trackBoxC/2),
                    trackX-int(trackBoxR/2):trackX+int(trackBoxR/2)]
                temptrackStar=temptrackStar+trackStar
                starInteg=starInteg+1
                checkS=temptrackStar.max()
            trackStar=temptrackStar#/starInteg

            #Blure and normalize star image
            trackStar=cv2.GaussianBlur(trackStar,(0,0),blureFactor)
            maxBright=trackStar.max()
            ratio=255/maxBright #normalization ratio
            trackStar = trackStar*ratio #normalized image

            #Find X and Y star profile
            c=trackStar.max(axis=0)
            r=trackStar.max(axis=1)
            peakP=c.max()*0.8 #caculate the 80% or higher average
            count=0
            peakY=0
            for cc in range (0,len(c)):
                if c[cc] > peakP:
                    peakY=peakY+cc
                    count = count+1
            if count<>0:
                peakY=1.0*peakY/count
            else:
                print "Error count = 0, #111"

            count=0
            peakX=0
            for rr in range (0,len(r)):
                if r[rr] > peakP:
                    peakX=peakX+rr
                    count = count+1
            if count>0:
                peakX=1.0*peakX/count
            else:
                print"Cannot find star!"

            if refX==0:
                refX=peakX
                refY=peakY
             
            #Finding average star position
            averageX = averageX+peakX*1.00-refX
            averageY = averageY+peakY*1.00-refY
            averageCount=averageCount+1
            if averageCount==5:
                averageCount=0

            if peakY>=trackBoxR:
                peakY=trackBoxR
            if peakX>=trackBoxC:
                peakX=trackBoxC
            if peakY<0:
                peakY=0
            if peakX<0:
                peakX=0

            offsetX = -1*(peakY*1.00-refY)
            offsetY = -1*(peakX*1.00-refX)
            if GP==1:
                #Star Guiding
                if offsetY>1: #R+
                    GPIO.output(RP, True)
                else:
                    GPIO.output(RP, False)

                if offsetY<-1: #R-
                    GPIO.output(RN, True)
                else:
                    GPIO.output(RN, False)

                if offsetX>1: #D+
                    GPIO.output(DP, True)
                else:
                    GPIO.output(DP, False)

                if offsetX<-1: #D-
                    GPIO.output(DN, True)
                else:
                    GPIO.output(DN, False)

            #Display cross, box, and single star image
            iBox=10 #inner box size
            cv2.line(trackStar,(0,trackBoxC/2) , (trackBoxR/2-iBox,trackBoxC/2), 80,1)
            cv2.line(trackStar,(trackBoxR/2+iBox,trackBoxC/2) , (trackBoxR,trackBoxC/2), 80,1)
            cv2.line(trackStar,(trackBoxR/2,0) , (trackBoxR/2,trackBoxC/2-iBox), 80,1)
            cv2.line(trackStar,(trackBoxR/2,trackBoxC/2+iBox) , (trackBoxR/2,trackBoxC), 80,1)
            cv2.rectangle(trackStar, (trackBoxR/2-iBox,trackBoxC/2-iBox),(trackBoxR/2+iBox,trackBoxC/2+iBox), 125, 1)
            displaytext = "Offset:("+str(offsetX)+","+str(offsetY)+")"
            cv2.putText(trackStar,displaytext, (2,10), cv2.FONT_HERSHEY_SIMPLEX, 0.3, 125,1)
            displaytext = "Integ: "+str(starInteg)+"."
            cv2.putText(trackStar,displaytext, (2,90), cv2.FONT_HERSHEY_SIMPLEX, 0.3, 125,1)

            #display . on screen randemly, so we know it is alive
            if randrange(10)>5:
                cv2.putText(trackStar,".", (125,10), cv2.FONT_HERSHEY_SIMPLEX, 0.3, 60,1)

            #Reduce screen size
            img3 = cv2.resize(trackStar,(trackBoxR*2,trackBoxC*2))

            cv2.imshow("Lock on star",img3)
            key=cv.WaitKey(20)

            if key==114: #press r to redo image
                cv2.destroyAllWindows()
                break

            if key==27: #pressw q to quit
                qq=0
                cv2.destroyAllWindows()
                break

    if GP==1:
        #Turn off all GPIO ports
        GPIO.output(4, False)
        GPIO.output(17, False)
        GPIO.output(18, False)
        GPIO.output(22, False)
     
    cv2.destroyAllWindows()

    Star autoguide with RPi - (4) Electronic circuit

    The Orion Atlas EQ-G autoguide port is shown in below picture.

    A simple circuit has been design for making the connection between RPi and EQ-G autoguide port.
    The color of LED and the GPIO pin# from RPi are following:
    Yellow LED, Dec +, GPIO #4
    Green LED, Dec -, GPIO #22
    Blue LED, RA-, GPIO #17
    Red LED, RA+, GPIO #18
    Below is the circuit board based on above configuration.
    Next step: Connect RPi auto guide cable to EQ-G.

    Setup for the real test now
    The webcam has been installed to 400mm guide scope.  Now, start the star auto guiding test.  A bright star has been chosen for the test.  You may skip the lengthy boring start auto guiding process.  It just for a record.
    Test with 2 pixels tolerance and x0.5 autoguiding speed.  Again you may skip the lengthy boring star auto guiding process too.
    Test with 1 pixels tolerance and x0.125 auto guiding speed.
    <Add video here>

    What if I discount and re-connect the guiding cable from EQ-G?