[quote=“ChrisBos, post:7, topic:6589”]
Carl’s code for Atan2 certainly works. However, if return of positive angles is required, I have found that the following, shorter code does the job.
Atan2 = ATAN(Y/X)
If X < 0 Then Atan2 += pi ElsIf Y < 0 Then Atan2 += 2*pi .
Chris I found your code matches in my tests below of a bunch of airports.
I tried to have a mix the possible Positive and Negatives. All are done twice flipping the Departure and Arrival location so that tests more. I also include odd ones like Null Island (0,0) and the Poles.
HeadingTests PROCEDURE()
CODE !Data from www.airnav.com or www.gcmap.com or Wikipedia
SYSTEM{PROP:MsgModeDefault}=MSGMODE:CANCOPY
!Mix +- +- Depart Lat Long Arrive Lat Long
HeadingTest2x(41.9769403,-87.9081497 , 49.009722, 2.547778 , 'Chicago -> Paris NE ?')
HeadingTest2x(41.9769403,-87.9081497 , -22.81 ,-43.250556 , 'Chicago -> Rio de Janeiro SE ?')
HeadingTest2x(41.9769403,-87.9081497 , -37.008056, 174.791667 , 'Chicago -> Auckland SW ?')
HeadingTest2x(41.9769403,-87.9081497 , 25.7953611,-80.2901158 ,'Chicago -> Miami SE ?')
HeadingTest2x(41.9769403,-87.9081497 , 39.8616667,-104.6731667 ,'Chicago -> Denver West?')
HeadingTest2x(-22.81 ,-43.250556 , -37.008056, 174.791667 , 'Rio de Janeiro -> Auckland West ?')
HeadingTest2x(-22.81 ,-43.250556 , 49.009722, 2.547778 , 'Rio de Janeiro -> Paris NE ?') !GIG-CDG - - + +
HeadingTest2x(41.275278 , 28.751944 , 49.009722, 2.547778 , 'Istanbul -> Paris NW ?') !IST-CDG + + + +
HeadingTest2x(41.9769403,-87.9081497 , 000 , 0000 , 'Chicago-Null Island Africa (0,0) E?')
HeadingTest2x(41.9769403,-87.9081497 , 90 , 0 , 'Chicago-North Pole N?')
HeadingTest2x(90 , 0 , -90 , 0 , 'North to South Pole S?')
RETURN
This calls, then again with flipped Arrive->Depart to test the REVERSE. Usually the Initial Heading is not the same as the reciprocal of the original Deoart → Arrive
HeadingTest2x PROCEDURE(REAL D_lat , REAL D_lon , REAL A_lat , REAL A_lon , STRING TripDesc)
CODE
HeadingTest1(D_lat ,D_lon, A_lat,A_lon ,TripDesc)
HeadingTest1(a_lat ,a_lon, d_lat,d_lon ,'REVERSE Trip --> '& CLIP(TripDesc) &' <<-- REVERSE')
RETURN
This is Bostjan’s code as unmodified as possible. I split out X and Y as variables. I changed the IF validation to INRANGE. I added Chris’ calc to compare and it always matched … to my eye … I should have had code to check.
HeadingTest1 PROCEDURE(REAL D_lat , REAL D_lon , REAL A_lat , REAL A_lon , STRING TripDesc)
Heading REAL
Y REAL
X REAL
Pi314 EQUATE(3.1415926535898)
Rad2Deg EQUATE(57.295779513082) !(180/3.1415926535898)
Deg2Rad EQUATE(0.0174532925199) !(3.1415926535898/180)
ChrisATan REAL
ChrisDeg REAL
CODE
IF INRANGE(D_lat, -90, 90) | !was IF D_lat And D_Lon but that failed for (0,0)
And INRANGE(D_lon, -180, 180) |
And INRANGE(A_lat, -90, 90) |
And INRANGE(A_lon, -180, 180) THEN !we got data
D_lat = d_lat * 3.1415926535898/180
D_lon = d_lon * 3.1415926535898/180
A_lat = a_lat * 3.1415926535898/180
A_lon = a_lon * 3.1415926535898/180
Y = SIN(A_lon - D_lon) * COS(A_lat)
X = COS(D_lat) * SIN(A_lat) - SIN(D_lat) * COS(A_lat) * COS(A_lon - D_lon)
Heading = ATAN2( Y , X ) * 180 / 3.1415926535898
Heading = round((Heading + 360) % 360,1) !Can return Negative Heading
ELSE
Heading = 999
END
ChrisATan = ATAN(Y/X) !Chris Bos posted this "if return of positive angles is required, I have found that the following, shorter code does the job"
If X < 0 THEN ChrisATan += PI314
ElsIf Y < 0 THEN ChrisATan += 2 * PI314
END
ChrisDeg = ROUND(ChrisATan * Rad2Deg,1)
CASE Message(TripDesc & |
'||Heading = '& Heading &' <9><<---> Reciprocal = '& (Heading+180)%360 & |
' Chris Head = '& ChrisDeg & |
'|={55}' & |
'|Depart <9> D_lat ='& D_lat & '<9> D_lon ='& D_lon & |
'|Arrive <9> A_lat ='& A_lat & '<9> A_lon ='& A_lon & |
'||Y = ' & Y &'|X = '& X & |
'|ATan2(Y,X) <9>= '& ATan2(Y,X) * Rad2Deg & |
'|ATan (Y/X) <9>= '& ATan(Y/X) * Rad2Deg & |
'||ChrisBos Degr = '& ChrisDeg & |
'|ChrisBos ATan = '& ChrisATan & |
' ','HeadingTest',ICON:NextPage,'Continue|Halt')
OF 2 ; HALT
END
RETURN