1+ #!/usr/bin/env python3
2+ import math
3+ from astropy .coordinates import SkyCoord , EarthLocation , AltAz , Angle
4+ from astropy import units as u
5+ from astropy .time import Time
6+ from astropy .utils import iers
7+ import sys , subprocess
8+
9+ def polarcalc (mylat , mylong , myelev , observing_time , p1RA , p1DEC , p2RA , p2DEC , p3RA , p3DEC ):
10+ #iers.conf.auto_download = False
11+ #iers.conf.auto_max_age = None
12+
13+ #Create time object based on given time
14+ observing_time = Time (observing_time )
15+
16+ #Create location object based on lat/long/elev
17+ observing_location = EarthLocation (lat = mylat * u .deg , lon = mylong * u .deg , height = myelev * u .m )
18+
19+ #Create coordinate objects for each point
20+ p1 = SkyCoord (p1RA , p1DEC , unit = 'deg' )
21+ p2 = SkyCoord (p2RA , p2DEC , unit = 'deg' )
22+ p3 = SkyCoord (p3RA , p3DEC , unit = 'deg' )
23+ p1X = (90 - p1 .dec .degree ) * math .cos (p1 .ra .radian )
24+ p1Y = (90 - p1 .dec .degree ) * math .sin (p1 .ra .radian )
25+ p2X = (90 - p2 .dec .degree ) * math .cos (p2 .ra .radian )
26+ p2Y = (90 - p2 .dec .degree ) * math .sin (p2 .ra .radian )
27+ p3X = (90 - p3 .dec .degree ) * math .cos (p3 .ra .radian )
28+ p3Y = (90 - p3 .dec .degree ) * math .sin (p3 .ra .radian )
29+
30+ #Calculate center of circle using three points in the complex plane. DEC is treated as unitless for the purposes of the calculation.
31+ x , y , z = complex (p1X ,p1Y ), complex (p2X ,p2Y ), complex (p3X ,p3Y )
32+ w = z - x
33+ w /= y - x
34+ c = (x - y )* (w - abs (w )** 2 )/ 2j / w .imag - x
35+ resultX = - c .real
36+ resultY = c .imag
37+
38+ #Convert X/Y values of circle into RA/DEC
39+ resultDEC = (90 - math .sqrt (resultX ** 2 + resultY ** 2 ))
40+ resultRA = math .atan2 (resultY , resultX )* 360 / (2 * math .pi )
41+ if resultRA < 0 :
42+ resultRA = (180 - abs (resultRA ))+ 180
43+
44+ #Create coordinate object for current alignment offset
45+ offset = SkyCoord (resultRA , resultDEC , frame = 'itrs' , unit = 'deg' , representation_type = 'spherical' , obstime = Time (observing_time ))
46+ print (f"Current alignment in RA/DEC: { Angle (resultRA * u .deg ).to_string (u .hour , precision = 2 )} /{ Angle (resultDEC * u .deg ).to_string (u .degree , precision = 2 )} ." )
47+
48+ #Create coordinate object for pole
49+ pole = SkyCoord (0 , 90 , frame = 'itrs' , unit = 'deg' , representation_type = 'spherical' , obstime = Time (observing_time ))
50+
51+ #Create coordinate object for pole
52+ poleAzAlt = pole .transform_to (AltAz (obstime = Time (observing_time ),location = observing_location ))
53+ print (f"True polar alignment in Az./Alt.: 0h00m00s/{ poleAzAlt .alt .to_string (u .degree , precision = 2 )} ." )
54+
55+ #Transform current alignment to Alt/Az coordinate system
56+ offsetAzAlt = offset .transform_to (AltAz (obstime = Time (observing_time ),location = observing_location ))
57+ print (f"Current alignment in Az./Alt.: { offsetAzAlt .az .to_string (u .hour , precision = 2 )} /{ offsetAzAlt .alt .to_string (u .degree , precision = 2 )} ." )
58+
59+ #Calculate offset deltas from pole
60+ #Normalize the azimuth values to between -180 and 180 degrees prior to determining offset.
61+ errorAz = (((poleAzAlt .az .deg + 180 ) % 360 - 180 )- ((offsetAzAlt .az .deg + 180 ) % 360 - 180 ))* 60
62+ print (f"Azimuth error correction is: { errorAz :.4f} arcminutes." )
63+ errorAlt = (poleAzAlt .alt .deg - offsetAzAlt .alt .deg )* 60
64+ print (f"Altitude error correction is: { errorAlt :.4f} arcminutes." )
65+
66+ return errorAz , errorAlt
67+
68+ #Latitude in degrees
69+ mylat = float (sys .argv [1 ])
70+
71+ #Longitude in degrees
72+ mylong = float (sys .argv [2 ])
73+
74+ #Elevation in meters
75+ myelev = float (sys .argv [3 ])
76+
77+ #YYYY-MM-DD HH:MM:SS format
78+ time = sys .argv [4 ]
79+
80+ #All RA/DEC values must be in compatible format to Astropy.coordinates library.
81+ #Preferrably degrees, but 00h00m00.0s and 00d00m00.0s should also work
82+ p1RA = float (sys .argv [5 ])
83+ p1DEC = float (sys .argv [6 ])
84+ p2RA = float (sys .argv [7 ])
85+ p2DEC = float (sys .argv [8 ])
86+ p3RA = float (sys .argv [9 ])
87+ p3DEC = float (sys .argv [10 ])
88+
89+ #Serial port address for Arduino, typically /dev/ttyACM0 in Astroberry, possibly /dev/ttyACM1
90+ if len (sys .argv ) <= 11 :
91+ serialport = "/dev/ttyACM0"
92+ else :
93+ serialport = sys .argv [11 ]
94+
95+ result = polarcalc (mylat , mylong , myelev , time , p1RA , p1DEC , p2RA , p2DEC , p3RA , p3DEC )
96+
97+ #Verify error correction can be handled by AutoPA hardware (assuming it is in home/centered position)
98+ moveAz = "N"
99+ if abs (result [0 ]) > 120 :
100+ moveAz = input ("Azimuth error may be out of bounds of hardware capabilities if not in home position. Continue? (Y/N): " )
101+ else :
102+ moveAz = "Y"
103+ if moveAz .upper () == "Y" :
104+ #Call process to move azimuth using elevated privileges to override any existing serial connection
105+ subprocess .call (['sudo' , './altaz.py' , "az" , str (result [0 ]), serialport ])
106+
107+ moveAlt = "N"
108+ if result [1 ] > 168 :
109+ moveAz = input ("Altitude error may be out of bounds of hardware capabilities if not in home position. Continue? (Y/N): " )
110+ elif result [1 ] > 432 :
111+ moveAz = input ("Altitude error may be out of bounds of hardware capabilities if not in home position. Continue? (Y/N): " )
112+ else :
113+ moveAlt = "Y"
114+ if moveAlt .upper () == "Y" :
115+ #Call process to move altitude using elevated privileges to override any existing serial connection
116+ subprocess .call (['sudo' , './altaz.py' , "alt" , str (result [1 ]), serialport ])
0 commit comments