A Script that calculates Sunrise and Sunset from your coordinates.

Hello.

Hello this script, originally written by Bruce R. Barkstrom, lets you get the times for Sunrise, and Sunset and hours of daylight, for a given date, or a range of dates in an unsaved TextEdit Document. It fetches the coordinates you have set in the Time Preferences pane, but lets you override those, should you wish to, by entering new.

Other than that, the script should be fairly robust, and input of dates are localized, so US citizens can enter their dates as mm-dd-yyyy (notice the 4 y’s for a year!).

Nigel has translated this script from CBasic to AppleScript, that once appeared in Byte Magazine, Yvan Koenig has tested, and read through the code I wrote for using it interactively. The fetching of coordinates from the Time Preferences wouldn’t have been possible, without the insights of Adam Bell.

Any bugs you might find, (you really shouldn’t), should be adressed to me, and please do report a bug if you find one. If you don’t want to post one in public, then that is fine with me, but please send me an e-mail about it.

The script is to be built upon with more astronomical data at a later stage, therefore it is important that this skeleton is as robust as possible.

Thanks!

Especially to Nigel Garvey and Yvan Koenig, for everything.

McUsr

-- Version 1.7
--                 SUNRISE - SUNSET ::: Requires Snow Leopard or Later.
-- This program is intended to compute the time of sunrise and sunset,
-- as well as the total solar energy incident on the top of the atmos-
-- phere for a given latitude and longitude at a given time of year.
-- Comments are welcome addressed to
--         Bruce R. Barkstrom
--         111 Pear Avenue
--         Newport News, VA  23607.
-- This program requires about 10k of text storage, and about 3.5k to run
-- when compiled by the CBASIC Version 2 compiler.

-- Original program published in Byte Magazine July 1981.
-- Transcribed to AppleScript by Nigel Garvey 4th/5th August 2013.
-- Requires the Satimage OSAX.
-- Further dabbling 6th-10th August 2013, including:
--   Improvements to FN.Print.Angle|() output formatting.
--   Change of time-zone input from zone number to ± hours from GMT.
--   Fix for rubbish times when time zones straddlle the Greenwich Meridian.

-- Above, and further testing by Nigel Garvey, other testing and code reading
-- of the user interface by Yvan Koenig with his keen eyes.
-- User interface, hopefully improved by McUsr
-- ****************************************
property parent : AppleScript
property |script name| : "SUNRISE - SUNSET"
property |version| : 2.3
property |First.Day.of.Month| : {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}
-- 0-based offsets.
property A : 1
property radians : pi / 180
property Debug : false
-- New properties for saving earlier obtained data
-- The convention is that we always ask to save
-- the last coordinates set Manually.
property savedPresets : false
property presetLongitude : missing value
property presetLatitude : missing value
property presetHourangle : missing value
property us_citizen : false
property decimalSep : missing value
property listSep : missing value
main()

(* Handlers for CBASIC functions which don't exist natively in AppleScript.
   Some use the Satimage OSAX. *)

on _INT(N)
	-- CBASIC's INT truncates rather than rounding down.
	-- It should also return a floating-point number, but we dont want that in the output!
	return N div 1
end _INT

on _SGN(N)
	if (N < 0) then return -1
	if (N = 0) then return 0
	return 1
end _SGN

on _ABS(N)
	-- CBASIC's ABS should return a floating-point number, but why change the original class?!
	if (N < 0) then return -N
	return N
end _ABS

on _SIN(R)
	return sin R -- Satimage OSAX.
end _SIN

on _COS(R)
	return cos R -- Satimage OSAX.
end _COS

on _TAN(R)
	return tan R -- Satimage OSAX.
end _TAN

on _ATN(R)
	return atan R -- Satimage OSAX
end _ATN

on _SQR(N)
	-- CBASIC's SQR() warns when the parameter's negative and makes it positive.
	if (N < 0) then
		say "Square route of negative attempted."
		set N to -N
	end if
	
	return N ^ 0.5
end _SQR

on _PRINT(txt)
	local A
	tell application "TextEdit"
		activate
		if ((count its documents) is 0) then make new document
		set A to (get path of its front document)
		try
			if A is "" then set A to missing value
		on error
			set A to missing value
		end try
		if A is not missing value then make new document
		make new paragraph at end of text of front document with data (txt & linefeed)
	end tell
	return true
end _PRINT

-- A couple of extra handlers to improve the AppleScript experience.

on pad(N)
	-- 22. September 2013, improved pad, for the cases where n < 0 and n > 100.
	-- Thanks to Yvan Koenig.
	-- Fixed a bug
	if N < -100 then
		return N as text
	else if N < 0 then
		return ("-" & text -2 thru -1 of ("0" & ((N as number) * -1) as text))
		--	error "The pad handler assumes positive values" number 5000
	else if N < 100 then
		return text 2 thru -1 of ((100 + N) as text)
	else
		return N as text
	end if
end pad

on new_query_continue()
	-- McUsr version 2.2 Coalesce the two dialogs into one.
	with timeout of (10 * minutes) seconds
		tell application (path to frontmost application as text)
			return (button returned of (display dialog "Do you wish to see Sunset/Sunrise for another date for this location
 or do you wish to get data for another Location?" buttons {"Quit", "Date", "Location"} cancel button 1 default button 2 with title |script name| with icon note) as text)
		end tell
	end timeout
end new_query_continue


(* The original program's subroutines. *)

-- Compute Julian Date.
on |FN.Julian.Date|(|Month|, |Day|, |Year|)
	set |Yrs.since.0| to |Year| + 4712
	set |No.of.lp.yrs| to _INT((|Yrs.since.0| - 1) / 4)
	set |Julian.Date| to 365 * |Yrs.since.0| + |No.of.lp.yrs|
	if (|Year| ≥ 1583) then
		set |Julian.Date| to |Julian.Date| - 10
		set |No.of.cent.yrs.snc.1583| to _INT((|Year| - 1501) / 100)
		set |No.of.cent.lp.yrs.snc.1583| to _INT((|Year| - 1201) / 400)
		set |Julian.Date| to |Julian.Date| - |No.of.cent.yrs.snc.1583| + |No.of.cent.lp.yrs.snc.1583|
	end if
	-- Deal with month and day.
	set |Julian.Date| to |Julian.Date| + (item |Month| of |First.Day.of.Month|) + |Day|
	if ((4 * (_INT(|Year| / 4)) = |Year|) and (|Month| ≥ 3)) then set |Julian.Date| to |Julian.Date| + 1
	if ((|Year| = 1582) and (((|Month| = 10) and (day ≥ 15)) or (month ≥ 11))) then set |Julian.Date| to |Julian.Date| - 10
	
	return |Julian.Date|
end |FN.Julian.Date|

-- Compute Mean Anomaly.
on |FN.M|(T, D)
	set M00 to -1.52417 + (1.5E-4 + 3.0E-6 * T) * T * T + 0.985600267 * D
	if (M00 > 360) then set M00 to M00 - 360 * (_INT(M00 / 360))
	
	return M00 * radians
end |FN.M|

-- Compute Obliquity of Ecliptic.
on |FN.epsilon|(T)
	return (23.452294 - (0.0130125 + (1.64E-6 - 5.03E-7 * T) * T) * T) * radians
end |FN.epsilon|

-- Compute Mean Longitude of Perigee.
on |FN.omega|(T, D)
	return (281.22083 + (4.53E-4 + 3.0E-6 * T) * T * T + 4.70684E-5 * D) * radians
end |FN.omega|

-- Compute Eccentricity.
on |FN.eccentricity|(T)
	return 0.01675104 - (4.08E-5 + 1.26E-7 * T) * T
end |FN.eccentricity|

-- Compute Longitude of Ascending Node of Lunar Oribt [sic!].
on |FN.Lunar.Long|(T, D)
	set |Lunar.Long| to 259.183275 + (0.002078 + 2.0E-6 * T) * T * T
	set |Lunar.Long| to |Lunar.Long| - 0.0529539222 * D
	
	return |Lunar.Long| * radians
end |FN.Lunar.Long|

-- Print time or angle in xx:xx:xx.xxx format.
-- The 'Angle' parameter is assumed to be in radians.
on |FN.Print.Angle|(|time.or.angle|, y, Angle)
	if (|time.or.angle| is "time") then
		set {factor, y0} to {12, " Hours"}
	else
		set {factor, y0} to {180, " Degrees"}
	end if
	set x1 to factor * Angle / pi
	if (x1 < 0) then
		set x1 to -x1
		set xSgn to "-"
	else
		set xSgn to ""
	end if
	set x2 to _INT(x1)
	set x3 to 60 * (x1 - x2)
	set x4 to _INT(x3)
	set x5 to 60 * (x3 - x4)
	set x6 to 1.0E-3 * (_INT(x5 * 1000))
	if (|time.or.angle| is "time") then set x2 to pad(x2)
	
	_PRINT(y & tab & tab & xSgn & x2 & ":" & pad(x4) & ":" & pad(x6) & y0)
end |FN.Print.Angle|

(* The original program's main body. *)

on main()
	-- Input Location Information.
	set Idne2 to true
	set Err to false
	set decimalSep to text 2 of ((1 / 2) as text)
	if decimalSep is "," then
		set listSep to ";"
	else
		set listSep to ","
	end if
	
	set inpMethod to "None So Far"
	repeat while (Idne2)
		
		repeat
			try
				if inpMethod is "None So Far" then
					if not savedPresets then
						-- implemented auto fetch of variables here
						set {_latQuartet, _longQuartet} to getLocaleDetails()
						set _stdTimeZone to ((time to GMT) / hours) as text
					else
						set _latQuartet to my presetLatitude
						set _longQuartet to my presetLongitude
						set {LatDir, LatD, LatM, LatS} to getValsFromQuartet(_latQuartet)
						set {LongDir, LonD, LonM, LonS} to getValsFromQuartet(_longQuartet)
						set _stdTimeZone to my presetHourangle
						set |Std.Time.Zone| to my presetHourangle
					end if
					set inpMethod to _QUERY_INPUT_METHOD(savedPresets)
				end if
				
				if inpMethod is false then
					error number -128
				else if inpMethod is "From Nearest City" then
					set {LatDir, LatD, LatM, LatS} to getValsFromQuartet(_latQuartet)
					set {LongDir, LonD, LonM, LonS} to getValsFromQuartet(_longQuartet)
					set |Std.Time.Zone| to _stdTimeZone as number
				else if inpMethod is "My Saved Properties" then
					set _latQuartet to my presetLatitude
					set _longQuartet to my presetLongitude
					set {LatDir, LatD, LatM, LatS} to getValsFromQuartet(_latQuartet)
					set {LongDir, LonD, LonM, LonS} to getValsFromQuartet(_longQuartet)
					set _stdTimeZone to my presetHourangle
					set |Std.Time.Zone| to my presetHourangle
				else
					-- we get latitude, normalize any coordinates, and performs the first checks.
					tell (_COORDINPUT("N or S latitude ( 0 - 90 Deg,Min,Sec)", (_latQuartet as text), {"N", "S"}, |script name|))
						set latTriplet to degreeTriplet of me from it
						if item 1 of it is "S" then
							if item 1 of latTriplet does not start with "-" then
								set item 1 of latTriplet to ("-" & (item 1 of latTriplet)) -- Thanks to Yvan Koenig.
							end if
						end if
					end tell
					
					if not Err then
						set normLatTriplet to hexagesNormalize for latTriplet
						set _latQuartet to aMapNotationQuartet from normLatTriplet by {"N", "S"}
						set {LatDir, LatD, LatM, LatS} to getValsFromQuartet(_latQuartet)
						
						tell (_COORDINPUT("E or W, Longitude (0 - 180 Deg,Min,Sec)", (_longQuartet as text), {"E", "W"}, |script name|))
							set longTriplet to degreeTriplet of me from it
							if item 1 of it is "W" then
								if item 1 of longTriplet does not start with "-" then
									set item 1 of longTriplet to ("-" & (item 1 of longTriplet)) -- Thanks to Yvan Koenig.
								end if
							end if
						end tell
						
						if not Err then
							set normLongTriplet to hexagesNormalize for longTriplet
							set _longQuartet to aMapNotationQuartet from normLongTriplet by {"E", "W"}
							-- A normalization, since we have kept the "-", to get the right hemisphere.
							set {LongDir, LonD, LonM, LonS} to getValsFromQuartet(_longQuartet)
							-- New: the user input for the time zone is now ± hours from GMT instead of a number from 1 to 24.
							set {|Std.Time.Zone|} to ¬
								_INPUT_HOUR_ANGLE("Your Standard or Summer Time Zone in hours from GMT (-12 to +14). Use " & decimalSep & "5 or " & ¬
									decimalSep & "75 where the offset includes an odd 30 or 45 minutes.", _stdTimeZone, |script name|)
							set _stdTimeZone to |Std.Time.Zone|
						end if
					end if
				end if
				
				if not Err then
					-- 7 sep. 2013 moved down, so the work with standard arguments
					set Latitude to LatD + (LatM + (LatS / 60)) / 60
					if LatDir is "S" then set Latitude to Latitude * -1
					if ((Latitude < -90) or (Latitude > 90)) then set Err to _PRINT("Latitude out of range (-90,90)")
					set Longitude to LonD + (LonM + (LonS / 60)) / 60
					if ((Longitude < 0.0) or (Longitude > 180)) then set Err to _PRINT("Longitude outside the range (0,180)")
					-- New: the allowed time-zone range is now -12 to +14 instead of 1 to 24.
					if ((|Std.Time.Zone| < -12) or (|Std.Time.Zone| > 14)) then set Err to ¬
						_PRINT("Std Time Zone outside the range (-12,14)")
					-- New: convert the ±-hours-from-GMT time-zone input to the original program's 1-to-24,
					--  west-from-Greenwich numbering system.
					
					set |Std.Time.Zone| to ((24 - |Std.Time.Zone|) mod 24 + 1)
					-- New: if the location's on the other side of the Greenwich Meridian from its nominal time zone,
					-- renumber the zone as a range extension on the location's side of the Meridian. 
					-- This corrects the sunrise and sunset time calculations.
					if (LongDir is "W") then -- Zone 24 becomes zone 0; zone 23 becomes zone -1.
						if (|Std.Time.Zone| > 22) then set |Std.Time.Zone| to |Std.Time.Zone| - 24
					else -- (LongDir is "E") -- Zone 1 becomes zone 25.
						if (|Std.Time.Zone| is 1) then set |Std.Time.Zone| to 25
					end if
				else
					error number -128
				end if
				
				if (not Err) then exit repeat
			on error number -128
				error number -128
			end try
		end repeat
		
		-- Revise longitude and latitude and standard time zone to be consistent.
		set Latitude to Latitude * radians
		if (Latitude < 0) then
			set x to "Antarctic"
		else
			set x to "Arctic"
		end if
		if (LongDir is "E") then set Longitude to 360 - Longitude
		set Longitude to Longitude * radians
		set |Time.Diff| to 12 * Longitude / pi
		set |Tot.time.diff| to |Time.Diff| - (|Std.Time.Zone| - 1)
		
		-- Input Date and Check for Correctness.
		set Idne to true
		if us_citizen then
			set dtSpecifyString to "(Month-Day-Year)"
		else
			set dtSpecifyString to "(Day-Month-Year)"
		end if
		set _dtNow to _NOW(us_citizen)
		repeat while (Idne)
			
			-- Adding localization of date formats here, NOT  regarding separator, but regarding the ordering
			-- US Citizens are accustomed to enter Month, Day, Year, and not Day Month Year as the rest of
			-- The Civil world
			if us_citizen then
				set {|Month|, |Day|, |Year|} to _INPUT_DATE("Date " & dtSpecifyString, _dtNow, |script name|, true, us_citizen, "PositiveDate")
			else
				set {|Day|, |Month|, |Year|} to _INPUT_DATE("Date " & dtSpecifyString, _dtNow, |script name|, true, us_citizen, "PositiveDate")
			end if
			
			set endRangeList to _INPUT_DATE("Enter end date " & "Date " & dtSpecifyString & " or \"Cancel\" if you just want data for one Date.", _dtNow, |script name|, false, us_citizen, "PositiveDate")
			
			set |AdjustedDay| to |Day| - 0.5 + |Time.Diff| / 24
			
			-- Compute current Julian Date, and time since 1900.
			set |J.D_Current| to |FN.Julian.Date|(|Month|, |AdjustedDay|, |Year|)
			
			
			if endRangeList is not false then
				if us_citizen then
					set {|EndMonth|, |EndDay|, |EndYear|} to endRangeList
				else
					set {|EndDay|, |EndMonth|, |EndYear|} to endRangeList
				end if
				set |AdjustedEndDay| to |EndDay| - 0.5 + |Time.Diff| / 24
				set |J.D_EndDay| to |FN.Julian.Date|(|EndMonth|, |AdjustedEndDay|, |EndYear|)
				if |J.D_EndDay| < |J.D_Current| then -- swap them
					set tmp to |J.D_Current|
					set |J.D_Current| to |J.D_EndDay|
					set |J.D_EndDay| to tmp
				end if
				set |J.D_EndDay| to |J.D_EndDay| + 1
			else
				set |J.D_EndDay| to |J.D_Current| + 1
			end if
			
			-- NEW: print the coordinates and date at the top of each entry.
			
			_PRINT("LATITUDE: " & (LatDir & " " & pad(_ABS(LatD)) & ":" & pad(_ABS(LatM)) & ":" & pad(_ABS(LatS)) & tab) ¬
				& "LONGITUDE: " & (LongDir & " " & pad(_ABS(LonD)) & ":" & pad(_ABS(LonM)) & ":" & pad(_ABS(LonS)) & linefeed))
			
			repeat while ((|J.D_EndDay| - |J.D_Current|) > 0)
				-- START OF REPORT 
				if us_citizen then
					_PRINT("DATE: " & (pad(|Month|) & "/" & pad(|Day|) & "/" & |Year|) & linefeed)
				else
					_PRINT("DATE: " & (pad(|Day|) & "/" & pad(|Month|) & "/" & |Year|) & linefeed)
				end if
				set D to |J.D_Current| - |FN.Julian.Date|(1, 1, 1900)
				set T to D / 36525
				
				-- Compute solar orbit.
				set ecc to |FN.eccentricity|(T)
				set M0 to |FN.M|(T, D) -- Radians
				set E to M0
				repeat 3 times
					set E to E + (M0 - (E - ecc * (_SIN(E)))) / (1 - ecc * (_COS(E)))
				end repeat
				set V to 2 * (_ATN(_SQR((1 + ecc) / (1 - ecc)) * (_TAN(0.5 * E))))
				if (V < 0) then set V to V + 2 * pi
				set R to A * (1 - ecc * (_COS(E)))
				set eps to |FN.epsilon|(T) -- Radians
				set omeg to |FN.omega|(T, D) -- Radians
				-- Nutation Terms are computed here.
				set L1 to |FN.Lunar.Long|(T, D) -- Radians
				set |Nutation.of.Obliquity| to (0.0025583333 + 2.5E-7 * T) * (_COS(L1)) * radians
				set eps to eps + |Nutation.of.Obliquity|
				set |Nutation.of.Longitude| to -(0.0047872222 + 4.72222222E-6 * T) * (_SIN(L1)) * radians
				-- Compute solar declination.
				set |sine.del| to _SIN(eps) * (_SIN(V + omeg))
				set |cosine.del| to _SQR(1 - |sine.del| * |sine.del|)
				set del to _ATN(|sine.del| / |cosine.del|)
				-- Compute Equation of Time.
				set |mean.long| to omeg + M0
				if (|mean.long| < 0) then set |mean.long| to |mean.long| + 2 * pi
				if (|mean.long| > 2 * pi) then set |mean.long| to |mean.long| - 2 * pi * (_INT(|mean.long| / (2 * pi)))
				set y to _TAN(0.5 * eps)
				set y to y * y
				set y to (1 - y) / (1 + y)
				set alpha0 to omeg + V + |Nutation.of.Longitude|
				if (alpha0 < 0) then set alpha0 to alpha0 + 2 * pi
				if (alpha0 > 2 * pi) then set alpha0 to alpha0 - 2 * pi * (_INT(alpha0 / (2 * pi)))
				set alpha to _ATN(y * (_TAN(alpha0)))
				set |Eqn.of.time| to alpha - |mean.long|
				set |Eqn.of.time| to |Eqn.of.time| - pi * (_INT(|Eqn.of.time| / pi))
				if (_ABS(|Eqn.of.time|) > 0.9 * pi) then set |Eqn.of.time| to |Eqn.of.time| - _SGN(|Eqn.of.time|) * pi
				set a0 to |Eqn.of.time| + |mean.long|
				if (a0 > 2 * pi) then set a0 to a0 - 2 * pi * (_INT(a0 / (2 * pi)))
				-- Print various orbital related quantities if desired.
				if endRangeList is false then
					|FN.Print.Angle|("angle", "mean anomaly ", M0)
					_PRINT(linefeed)
					|FN.Print.Angle|("angle", "eccentric anom", E)
					|FN.Print.Angle|("angle", "true anomaly  ", V)
					|FN.Print.Angle|("angle", "obliquity     ", eps)
					|FN.Print.Angle|("angle", "nutation of ob", |Nutation.of.Obliquity|)
					|FN.Print.Angle|("angle", "longitude     ", alpha0)
					|FN.Print.Angle|("angle", "nutation of ln", |Nutation.of.Longitude|)
					|FN.Print.Angle|("angle", "solar declin  ", del)
					|FN.Print.Angle|("time", "solar R.A.    ", a0)
					|FN.Print.Angle|("time", "equation of tm", |Eqn.of.time|)
					_PRINT("eccentricity                  " & ecc)
					_PRINT("r                                    " & R)
					_PRINT(linefeed)
				end if
				-- Length of Day.
				set mum to _COS(Latitude - del)
				set mun to -(_COS(Latitude + del))
				set mua to 0
				-- Refraction Effect computed here.
				if (Debug and endRangeList is false) then
					|FN.Print.Angle|("time", "Total time difference", |Tot.time.diff| * pi / 12)
				end if
				if (-mum * mun > 0) then
					set |Refrac.corr| to 0.0555555556 / (_SQR(-mum * mun))
				else
					set |Refrac.corr| to 0
					_PRINT("The sun's upper limb does not cross the horizon.")
				end if
				if (Debug and endRangeList is false) then |FN.Print.Angle|("time", "Refraction corr is  ", |Refrac.corr| * pi / 12)
				if (mun > mua) then set mua to mun
				if (mum > mua) then
					set x to _SQR((mua - mun) / (mum - mua))
					set |frac.of.day.sun.up| to 1 - (2 / pi) * (_ATN(x))
					set |basic.sunset| to 12.0 * |frac.of.day.sun.up|
					set |basic.sunrise| to |basic.sunset|
					set |basic.sunset| to |basic.sunset| + |Refrac.corr| + |Eqn.of.time| * 12 / pi
					set |basic.sunrise| to |basic.sunrise| + |Refrac.corr| - |Eqn.of.time| * 12 / pi
					set |time.basic.sunset| to 12 + |basic.sunset|
					set |time.basic.sunrise| to 12 - |basic.sunrise|
					set |time.sunrise| to |time.basic.sunrise| + |Tot.time.diff|
					set |time.sunset| to |time.basic.sunset| + |Tot.time.diff|
					set |fraction.avail.sun| to 0.5 * ((mum + mun) * |frac.of.day.sun.up| + (mum - mun) * (_SIN(pi * |frac.of.day.sun.up|)) / pi)
					-- PRINT:PRINT:\
					
					|FN.Print.Angle|("time", "Sunrise occurs at  ", |time.sunrise| * pi / 12)
					|FN.Print.Angle|("time", "Sunset occurs at  ", |time.sunset| * pi / 12)
					|FN.Print.Angle|("time", "Hours of daylight ", (|time.sunset| - |time.sunrise|) * pi / 12)
				else
					_PRINT("You are in the " & x & " winter - the sun doesn't rise.")
					set |fraction.avail.sun| to 0
				end if
				if (Debug) then _PRINT("Hour angle = " & |Std.Time.Zone|)
				
				if (endRangeList is false) then _PRINT(linefeed & "Sunlight available at the top of the atmosphere is " & 1.188864E+8 * |fraction.avail.sun| & " Joules per square metre.")
				
				_PRINT(linefeed)
				set |J.D_Current| to |J.D_Current| + 1
				set {|Year|, |Month|, |Day|} to gregorianCalendarTriplet from |J.D_Current|
			end repeat
			-- END OF REPORT 
			
			if inpMethod is "Manually" then
				-- Talkative approach, but it is really best to ask if wants to save properties
				-- after a result has been displayed.
				
				set btnReply to PROMPT_FOR_SAVING_PROPERTIES()
				if btnReply is "Save" then
					set my presetLatitude to _latQuartet
					set my presetLongitude to _longQuartet
					set my presetHourangle to ((24 - (|Std.Time.Zone| - 1)) mod 24)
					set my savedPresets to true
				else if btnReply is "Quit" then
					error number -128
				end if
			end if
			
			set btnReply to new_query_continue()
			if btnReply is "Quit" then
				set {Idne, Idne2} to {false, false}
			else if btnReply is "Location" then
				set inpMethod to "Manually"
				set {Idne, Idne2} to {false, true}
			else
				set inpMethod to "Previous Vals"
				-- but we are to enter new dates, so we'll start with the last one entered.
				if us_citizen then
					set _dtNow to "" & |Month| & "-" & |Day| & "-" & |Year|
				else
					set _dtNow to "" & |Day| & "-" & |Month| & "-" & |Year|
				end if
				set {Idne, Idne2} to {true, false}
			end if
		end repeat
	end repeat
end main

-- New handlers, introduced by McUsr in order to "tidy" up the UI.

on _QUERY_INPUT_METHOD(fromSavedProperties)
	-- for some reason this won't work from within Script Debugger
	with timeout of (10 * minutes) seconds
		tell application (path to frontmost application as text)
			try
				if not fromSavedProperties then
					set btnMethod to button returned of (display dialog "Do you want to enter coordinates, or use the ones set for nearest City by your Date and Time Preferences?" with title |script name| buttons {"Cancel", "From Nearest City", "Manually"} cancel button 1 default button 2 with icon note)
				else
					set btnMethod to button returned of (display dialog "Do you want to enter coordinates, or use the ones which you have previously saved?" with title |script name| buttons {"Cancel", "My Saved Properties", "Manually"} cancel button 1 default button 2 with icon note)
				end if
			on error
				set btnMethod to "Cancel"
			end try
		end tell
	end timeout
	if btnMethod = "Cancel" then
		return false
	else
		return btnMethod
	end if
end _QUERY_INPUT_METHOD

-- 15th September 2013
-- Now serves as an input handler for both date and hour-angle.
-- Changed a wee-bit, to return an empty string if there wasn't anything to return.
-- 22th September, adding way to return when cancelling input isn't fatal
-- 28th September, added localization of decimal and list separator, and 
-- added a parameter for telling about input of degrees, in which all terms
-- should be negative, and not only the first.
-- 28th September, changed one more time, to handle dateinput, where "-" is an 
-- eminent separator. the added listType can be one of three:
-- Regular : this means that the number list is treated as a raw list of numbers
-- both negative and positive numbers are valid.
-- PositiveDate : this means that the three numbers can be separated with "-"
-- EveryDate    : also negative dates, this means that we can't use the "-" as 
-- a separator but have to resort to ";" "," or "/".
-- Triplet    : If only one of the numbers is prepended by a "-", then all three numbers
-- are made to be negative quantities. Good for hour min, sec, and deg min sec
-- Changed name of the handler to ListInput

on _LIST_INPUT(txt, defaAns, isWarning, scriptName, stayAlive, listType)
	-- The core of the handler originally written by Nigel Garvey.
	-- Parameters:
	--			txt:			Text:		The message that is shown in the input dialog.
	--			defaAns:		Text: 	The default value that is show in the input field.
	--			isWarning:	Boolean: If set to true, then a caution icon is shown, not 
	--										a note icon which is default.
	--			ScriptName:	Text:		Dialog box title, not necessarily a script name.
	--			stayAlive:	Boolean: If true the script returns missing value, otherwise
	--										dies.
	--			listType:	Word:		Word can be: "Regular", "PositiveDate", "EveryDate"
	--										or "Triplet","Hours".		
	-- Returns: 
	--			A list with values on success.
	--			Missing value if the user canceled and stayAlive is true.
	--			{""} if the input was bad somehow.
	--			Dies directly if the user cancelled and the stayalive was false.
	--
	--	If you have this handler in a tell block, then you want to wrap
	--	That tell block into a try block to "smooth over" the error number -128 call.
	local inpt, btnList, isNegative, astid, outpt, digits, param, listSep
	-- Thanks to Yvan Koenig for tips on increasing robustness!
	if listType is not in {"Regular", "PositiveDate", "EveryDate", "Triplet", "Hours"} then ¬
		error "Bad value for listType" number 4015
	set inpt to missing value
	# Configures list separator based on decimal separator: ";" <-- "," and "," <-- "."
	# A list separator always works, as do spaces between elements.
	# A "-" works for dates , when dates are restricted to positive dates.
	# A "." works for separatring elements of dates and times too.
	# A "/" works for separating dates.
	# A ":" works for separating hours.
	# A  list of regular numbers can only be separated with space and the designated list separator.
	# Counting of the elements happens in the caller of this handler.
	
	if text 2 of ((1 / 2) as text) is "," then
		set listSep to ";"
	else
		set listSep to ","
	end if
	# Appropriate buttons for whether we shall stay alive or not.
	if stayAlive then
		set btnList to {"Cancel", "Ok"}
	else
		set btnList to {"Quit", "Ok"}
	end if
	
	with timeout of (10 * minutes) seconds
		tell application (path to frontmost application as text)
			if isWarning then
				try
					set inpt to text returned of (display dialog txt default answer defaAns with title scriptName buttons btnList with icon caution cancel button 1 default button 2)
				end try
			else
				try
					set inpt to text returned of (display dialog txt default answer defaAns with title scriptName buttons btnList with icon note cancel button 1 default button 2)
				end try
			end if
		end tell
	end timeout
	if inpt is missing value then
		if not Debug and not stayAlive then -- Need to stay alive here, if we are entering the second date.
			error number -128 -- User hit cancel.
		else
			return missing value -- Thanks to Yvan Koenig.
		end if
	end if
	if inpt is "" then return {}
	
	if listType is "Triplet" then
		ignoring white space
			set isNegative to (inpt begins with "-")
		end ignoring
	else if listType is "EveryDate" then
		set isNegative to (inpt contains "-")
	else
		set isNegative to false
	end if
	log "" & inpt
	set astid to AppleScript's text item delimiters
	set titems to {"-", listSep, space}
	if listType is not in {"PositiveDate", "Triplet"} then set titems to rest of titems
	if listType is in {"PositiveDate", "EveryDate"} then set titems to titems & {".", "/"}
	if listType is in {"Triplet", "Hours"} then set end of titems to ":"
	
	set AppleScript's text item delimiters to titems
	if listType is in {"Triplet", "EveryDate"} then
		set outpt to inpt's words
	else
		set outpt to inpt's text items
	end if
	#	set AppleScript's text item delimiters to " "
	#	set inpt to inpt as text
	set AppleScript's text item delimiters to astid
	
	set reslist to {}
	#	set outpt to inpt's words
	set digits to "1234567890"
	repeat with i from 1 to (count outpt)
		set param to item i of outpt
		if param is not "" and (character 1 of param is in digits or character 1 of param is "-") then
			try -- Thanks to Yvan Koenig.
				set param to param as number
			on error
				if listType is not in {"PositiveDate", "EveryDate"} then
					# Can't have decimal fraction anyway
					try
						set AppleScript's text item delimiters to "."
						set param to text items of param
						set AppleScript's text item delimiters to ","
						set param to param as text
						set AppleScript's text item delimiters to astid
						set param to param as number
					on error
						return {""} -- this must be resolved by caller
					end try
				else
					return {""}
				end if
			end try
			if (isNegative) then # This works only for triplets
				if listType is in {"EveryDate", "Triplet"} then set param to -param
			end if
			set end of reslist to param
		end if
	end repeat
	
	return reslist
end _LIST_INPUT

to PROMPT_FOR_SAVING_PROPERTIES()
	-- Asks for saving Properties when they have  been input manually.
	with timeout of (10 * minutes) seconds
		tell application (path to frontmost application as text)
			try
				return (button returned of (display dialog "Do you wish to save the coordinates you have entered as standard coordinates?" buttons {"Quit", "Never Mind", "Save"} cancel button 1 default button 2 with title |script name| with icon note))
			on error
				return ("Quit")
			end try
		end tell
	end timeout
end PROMPT_FOR_SAVING_PROPERTIES

-- handler that returns a list of latidude and longitude coordinates,
-- from a quartet: there should really be 4 elements in it.
to getValsFromQuartet(strQuartet)
	local _tids, mlist
	tell (a reference to AppleScript's text item delimiters)
		set _tids to contents of it
		set contents of it to space
		tell (strQuartet)
			if (count text items of it) < 4 then
				set AppleScript's text item delimiters to _tids
				error "Error: There are too few elements in the Quartet:" number 6010
				-- Thanks to Yvan Koenig for spelling corrections! :)
			else
				set mlist to text items of it
			end if
		end tell
		set contents of it to _tids
	end tell
	return mlist
end getValsFromQuartet

-- returns true if the day appears within a month
-- http://www.celestrak.com/columns/v02n02/
to validDate(dy, mo, YR)
	set daynumbers to {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
	if mo < 1 or mo > 12 then
		return false
	else if mo = 2 then
		if ((YR mod 4) = 0) and (((YR mod 100) ≠ 0) or ((YR mod 400) = 0)) and (mo > 2) then
			set item 2 of daynumbers to 29
		end if
	end if
	if dy < 1 or dy > item mo of daynumbers then
		return false
	else
		return true
	end if
end validDate

-- 22th September:
-- changing the handler to divert between entry of a mandatory date, and one that isn't
-- so we have an easy way, to enter a range of dates, if the user enters a second date,
-- but can resort to the first, if the user defaulted the second dialog.

on _INPUT_DATE(txt, defaAns, scriptName, mandatory, us_origin, dateRange)
	# http://wiki.collectiveaccess.org/wiki/Date_and_Time_Formats
	local defaulted, missing_args, areNumbers, aValidDate, fullySpecified, positiveNumbers, theDateL
	set theDateL to _LIST_INPUT(txt, defaAns, false, scriptName, (not mandatory), dateRange)
	-- if the date is a mandatory one, then stayalive is false when calling _INPUT
	if theDateL is missing value then
		if mandatory then
			-- then something is done if Debug is true
			if (Debug) then _PRINT("User cancelled during input of a date.")
			error number -128 -- User hit cancel.
		else
			return false
		end if
	end if
	set {areNumbers, aValidDate, fullySpecified, positiveNumbers} to {true, true, true, true}
	if theDateL = {} then
		set {defaulted, missing_args} to {true, true}
	else
		set {defaulted, missing_args} to {false, false}
	end if
	
	repeat while true
		if defaulted then
			if missing_args then
				set errStr to "A value is mandatory."
				set defaultValues to defaAns
			else if not areNumbers then
				set errStr to "A Date is in numbers only."
				set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "-"}
				set defaultValues to theDateL as text
				set AppleScript's text item delimiters to tids
			else if not fullySpecified then
				if us_origin then
					set errStr to "We need Day,Month,Year."
				else
					set errStr to "We need Month,Day,Year."
				end if
				set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "-"}
				set defaultValues to theDateL as text
				set AppleScript's text item delimiters to tids
			else if not positiveNumbers then
				set errStr to "A Date hasn't negative numbers."
				set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "-"}
				set defaultValues to theDateL as text
				set AppleScript's text item delimiters to tids
			else if not aValidDate then
				set errStr to "That date doesn't exist."
				set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "-"}
				set defaultValues to theDateL as text
				set AppleScript's text item delimiters to tids
			end if
			set theDateL to _LIST_INPUT(errStr & return & return & txt, defaultValues, true, scriptName, (not mandatory), dateRange)
			-- if the date is a mandatory one, then stayalive is false when calling _INPUT
			if theDateL is missing value then
				if mandatory then
					-- then something is done if Debug is true
					if (Debug) then _PRINT("User cancelled during input of a date.")
					error number -128 -- User hit cancel.
				else
					return false
				end if
			end if
			set {defaulted, missing_args, areNumbers, aValidDate, fullySpecified, positiveNumbers} to {false, false, true, true, true, true}
		end if
		local probe
		if theDateL ≠ {} then
			if length of theDateL < 3 then set {defaulted, fullySpecified} to {true, false}
			if fullySpecified then
				repeat with i from 1 to 3
					set probe to item i of theDateL as number
					try
						if dateRange is "PositiveDate" and probe < 0 then
							set {defaulted, positiveNumbers} to {true, false}
							exit repeat
						end if
					on error
						set {defaulted, areNumbers} to {true, false}
						exit repeat
					end try
				end repeat
				if not defaulted then
					if not us_origin then
						if not validDate(item 1 of theDateL, item 2 of theDateL, item 3 of theDateL) then
							set {defaulted, aValidDate} to {true, false}
						end if
					else
						if not validDate(item 2 of theDateL, item 1 of theDateL, item 3 of theDateL) then
							set {defaulted, aValidDate} to {true, false}
						end if
					end if
				end if
			end if
		else
			set {defaulted, missing_args} to {true, true}
		end if
		if not defaulted then exit repeat
	end repeat
	return theDateL
	
end _INPUT_DATE


on _INPUT_HOUR_ANGLE(txt, defaAns, scriptName)
	local tids, defaulted, missing_args, isNumber, withinRange, theTz, elmList
	set theTz to missing value
	try
		set theTz to _LIST_INPUT(txt, defaAns, false, scriptName, false, "Regular")
	end try
	
	if theTz is missing value then
		if Debug then _PRINT("User cancelled during input of hour angle.")
		error number -128 -- User hit cancel.
	else
		set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ","}
		set elmList to text items of theTz
		set theTz to theTz as text
		set AppleScript's text item delimiters to tids
	end if
	if (count elmList) = 1 or text 1 of (item 1 of elmList as text) is not "-" then
		set theTz to theTz as number
	else
		set theTz to ("" & item 1 of elmList & "," & text 2 thru -1 of (item 2 of elmList as text)) as number
	end if
	
	set {isNumber, withinRange} to {true, true}
	if theTz = "" then
		set {defaulted, missing_args} to {true, true}
	else
		set {defaulted, missing_args} to {false, false}
	end if
	
	repeat while true
		if defaulted then
			if missing_args then
				set errStr to "A value is mandatory."
				set defaultValues to defaAns
			else if not isNumber then
				set errStr to "The Time Zone must be a number."
				set defaultValues to theTz
			else if not withinRange then
				set errStr to "Time Zone beyond the valid range."
				set defaultValues to theTz
			end if
			set {theTz} to _LIST_INPUT(errStr & return & return & txt, defaAns, true, scriptName, false, "Regular")
			if theTz is missing value then
				if Debug then _PRINT("User cancelled during editing of hour angle.")
				error number -128 -- User hit cancel.
			end if
			
			set {defaulted, missing_args, isNumber, withinRange} to {false, false, true, true}
		end if
		
		if theTz ≠ "" then
			try
				set param to theTz as number
			on error
				set {defaulted, isNumber} to {true, false}
				error "couldn't make a number of it" number 4000
			end try
			if isNumber then
				if param < -12 or param > 14 then
					set {defaulted, withinRange} to {true, false}
				end if
			end if
		else
			set {defaulted, missing_args} to {true, true}
		end if
		if not defaulted then exit repeat
	end repeat
	return {theTz}
end _INPUT_HOUR_ANGLE

-- 10 September 2013 : Reworked version of _INPUT above, specialized for handling coordinates.
-- 15 Sepetember 2013 : Enforced robustness.

-- You'll have to get the directions right, and in the right positions,
-- the rest of the arguments must be numbers valid numbers
-- there must be at least one number, ( a degree )
-- the number can't be less than -91 nor greater than 90 if latitude.
-- not be less than -181 nor greater than 180 if longitude.
-- any missing zeroes will be filled in.

on _COORDINPUT(txt, defaAns, directions, scriptName)
	local defaulted, missing_args, areNumbers, withinRange, correctDirection, inpt, tids
	-- Thanks to Yvan Koenig for tips for increasing the robustness of the dialog.
	set inpt to missing value
	with timeout of (10 * minutes) seconds
		tell application (path to frontmost application as text)
			try
				set inpt to text returned of (display dialog txt default answer defaAns with title scriptName buttons {"Quit", "OK"} with icon note cancel button 1 default button 2)
			end try
		end tell
	end timeout
	if inpt is missing value then
		if Debug then _PRINT("User cancelled during first entry of coordinates.")
		error number -128 -- User hit cancel.
	end if
	
	set {areNumbers, withinRange, correctDirection} to {true, true, true}
	if inpt = "" then
		set {defaulted, missing_args} to {true, true}
	else
		set {defaulted, missing_args} to {false, false}
	end if
	repeat while true
		
		if defaulted then
			if missing_args then
				set errStr to "Input is Mandatory."
				set defaultValues to defaAns
			else if not areNumbers then
				set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, " "}
				set defaultValues to inpt as text
				set AppleScript's text item delimiters to tids
				set errStr to "Deg,Min,Sec are numbers."
			else if not withinRange then
				set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, " "}
				set defaultValues to inpt as text
				set AppleScript's text item delimiters to tids
				set errStr to "The degrees are not within range:"
				if item 1 of directions is "N" then
					set errStr to errStr & space & "-91.0 < Deg < 90."
				else
					set errStr to errStr & space & "-181.0 < Deg < 180."
				end if
			else if not correctDirection then
				set errStr to "The direction can only be: " & item 1 of directions & "/" & item 2 of directions & "."
				set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, " "}
				set defaultValues to inpt as text
				set AppleScript's text item delimiters to tids
			end if
			with timeout of (10 * minutes) seconds
				tell application (path to frontmost application as text)
					set inpt to text returned of (display dialog (errStr & return & return & txt) default answer defaultValues with title scriptName buttons {"Quit", "OK"} with icon caution cancel button 1 default button 2)
				end tell
			end timeout
			if inpt is missing value then
				if Debug then _PRINT("User cancelled during editing of coordinates.")
				error number -128 -- User hit cancel.
			end if
			
			set {defaulted, missing_args, areNumbers, withinRange, correctDirection} to {false, false, true, true, true}
			
		end if
		
		if inpt ≠ "" then
			set tids to AppleScript's text item delimiters
			set AppleScript's text item delimiters to {",", ":", "/", ".", space, tab, "|"}
			set inpt to inpt's text items
			set AppleScript's text item delimiters to tids
			set digits to "1234567890"
			set outp to {}
			set num_inptArgs to (count inpt)
			if item num_inptArgs of inpt = "" then set num_inptArgs to num_inptArgs - 1
			if num_inptArgs < 2 then
				set defaulted to true
				set missing_args to true
			else
				set areNumbers to true
				repeat with i from 1 to num_inptArgs
					if i = 1 then
						if item 1 of inpt is in directions then
							set end of outp to item 1 of inpt
						else
							set correctDirection to false
							exit repeat
						end if
					else if item i of inpt = "" then -- thanks to Yvan Koenig.
					else
						ignoring white space
							set isNegative to (item i of inpt begins with "-")
						end ignoring
						
						set param to word 1 of item i of inpt
						if (character 1 of param is in digits) then
							set param to param as number
							-- checks to see if the parameter is within the range
							if (isNegative) then
								set param to -param
								if i = 2 and item 1 of directions = "N" then
									if param ≤ -91.0 then
										set withinRange to false
										exit repeat
									end if
								else if i = 2 and item 1 of directions = "E" then
									if param ≤ -181.0 then
										set withinRange to false
										exit repeat
									end if
								end if
							else if i = 2 and item 1 of directions = "N" then
								if param > 90.0 then
									set withinRange to false
									exit repeat
								end if
							else if i = 2 and item 1 of directions = "E" then
								if param > 180.0 then
									set withinRange to false
									exit repeat
								end if
							end if
							if i > 2 and i < 4 then -- We "format" the two intermediary terms of the quartet.
								if isNegative then
									set end of outp to "-" & my pad(-param) -- thanks to Yvank Koenig.
								else if i > 2 then
									set end of outp to my pad(param) -- thanks to Yvank Koenig.
									-- It may part of a negative triplet.
								end if
							else
								set end of outp to (param as text)
							end if
						else
							set areNumbers to false
						end if
					end if
				end repeat
				if areNumbers and withinRange and correctDirection then
					repeat with i from (count outp) to 3
						set end of outp to "00"
					end repeat
					exit repeat
				else
					set defaulted to true
				end if
			end if
		else
			set {defaulted, missing_args} to {true, true}
		end if
	end repeat
	return outp
end _COORDINPUT


to getLocaleDetails()
	try
		set localeDetails to text 1 thru -2 of (do shell script "export pwhere=$(readlink /etc/localtime |sed -n 's_/[^/]*/[^/]*/[^/]*/\\([^/]*\\)/\\([^/]*\\).*_\\1.\\2_p');(sed -ne '/'\"$pwhere\"'/ s/^[^-+]*\\([-+][[:digit:]]*\\)[^+-]*\\([-+][[:digit:]]*\\).*/\\1 \\2/p' </usr/share/zoneinfo/zone.tab ;defaults read .GlobalPreferences AppleLocale 2>/dev/null || echo en_US) |tr '
' ' '")
		-- latitude space longitude space locale space
	on error
		error "Error: Something went uttlerly wrong while retrieving localeDetails:" number 5999
	end try
	
	tell (a reference to AppleScript's text item delimiters)
		set _tids to contents of it
		set contents of it to space
		tell (localeDetails)
			if (count text items of it) < 3 then
				set AppleScript's text item delimiters to _tids
				error "Error: There are too few main elements in the localeDetails:" number 6010
				-- Thanks to Yvan Koenig for spelling corrections! :)
			else
				set {LatString, LongString, localeString} to text items of it
			end if
		end tell
		set contents of it to _tids
	end tell
	if localeString is "en_US" then set my us_citizen to true
	return ({(transformCoordString for LatString by 5), (transformCoordString for LongString by 6)})
end getLocaleDetails


to transformCoordString for aRawString by minimumLength
	local TheDegree, theMinutes, theSeconds, Direction, degreeString
	tell (aRawString)
		try
			if length of it is minimumLength and minimumLength is 5 then
				set {TheDegree, theMinutes} to {(text 1 thru 3 of it), (text 4 thru -1 of it)}
				if TheDegree begins with "-" then
					set Direction to "S"
				else
					set Direction to "N"
				end if
				set degreeString to (Direction & space & text 2 thru -1 of TheDegree & space & theMinutes & space & "00")
			else if length of it is minimumLength and minimumLength is 6 then
				set {TheDegree, theMinutes} to {(text 1 thru 4 of it), (text 5 thru -1 of it)}
				if TheDegree begins with "-" then
					set Direction to "W"
				else
					set Direction to "E"
				end if
				set degreeString to (Direction & space & text 2 thru -1 of TheDegree & space & theMinutes & space & "00")
				
				
			else if minimumLength is 5 then -- length of it is 8 Thanks to kel1 fo showing me. 
				set {TheDegree, theMinutes, theSeconds} to {(text 1 thru 3 of it), (text 4 thru 5 of it), (text 6 thru -1 of it)}
				if TheDegree begins with "-" then
					set Direction to "S"
				else
					set Direction to "N"
				end if
				-- Thanks to Nigel Garvey for pointing out the bug here.
				set degreeString to (Direction & space & text 2 thru -1 of TheDegree & space & theMinutes & space & theSeconds)
			else
				set {TheDegree, theMinutes, theSeconds} to {(text 1 thru 4 of it), (text 5 thru 6 of it), (text 7 thru -1 of it)}
				if TheDegree begins with "-" then
					set Direction to "W"
				else
					set Direction to "E"
				end if
				set degreeString to (Direction & space & text 2 thru -1 of TheDegree & space & theMinutes & space & theSeconds)
			end if
		on error
			error "transformCoordString: something is very wrong, do you have a some customized coord-setting?" number 5000
		end try
	end tell
	return degreeString
end transformCoordString

-- 
on _NOW(us_origin)
	-- Thanks to Yvan Koenig for optimzation.
	tell (current date)
		if us_origin then
			return ((("" & (its month as integer) as text) & "-" & its day as text) & "-" & its year)
		else
			return ((("" & its day as text) & "-" & (its month as integer) as text) & "-" & its year)
		end if
	end tell
end _NOW


to degreeTriplet from aMapNotationQuartet
	-- length considerations
	if (length of aMapNotationQuartet < 4) then
		error "degreeTriplet: incomplete quartet, Lacks one or more of: dir, deg ,min, sec (4 terms)." number 4000
	end if
	tell (aMapNotationQuartet)
		set dmsTriplet to rest of it
		if item 1 of it is "S" or item 1 of it is "W" then
			set item 1 of dmsTriplet to -(item 1 of dmsTriplet) as text -- Thanks to Yvan Koenig.
		end if
		return dmsTriplet
	end tell
end degreeTriplet

-- The idea is simple, the first character in the tuple
-- is for the positive direction, and the other for the
-- negative, it is supposed to work with a normalized
--  Angle, (only first term to right negative, and 
-- {0≤d<360, 0≤m<60, 0≤s<60 }

-- If we get a negative coordinate, then we choose the 
-- second directon, we remove the negative degrees when
-- we have set the direction.

to aMapNotationQuartet from aDegreeTriplet by aDirectionTuple
	if (length of aDegreeTriplet < 3) then ¬
		error "aMapNotationQuartet: incomplete triplet, Lacks one or more of: deg ,min, sec (3 terms)." number 4000
	
	if (length of aDirectionTuple < 2) then ¬
		error "aMapNotationQuartet: incomplete tuplet, Lacks one or more of: {\"N\",\"S\"} or {\"E\",\"W\"}(2 terms)." number 4000
	
	local probe, newQuartet, mark
	set newQuartet to {"", "", "", ""}
	tell aDegreeTriplet
		set probe to item 1 of it
		if probe begins with "-" then
			set item 1 of newQuartet to item 2 of aDirectionTuple
			set item 2 of newQuartet to text -2 thru -1 of ("0" & (((probe as number) * -1) as text))
			set mark to 2
		else if probe as number > 0 then
			set item 1 of newQuartet to item 1 of aDirectionTuple -- N/E if righthanded sys.
			-- coercions may be simplified?
			set item 2 of newQuartet to text -2 thru -1 of ("0" & probe)
			set mark to 2
		else
			set probe to item 2 of it
			if probe begins with "-" then
				set item 1 of newQuartet to item 2 of aDirectionTuple
				set item 3 of newQuartet to text -2 thru -1 of ("0" & (((probe as number) * -1) as text))
				set mark to 3
			else if probe as number > 0 then
				set item 1 of newQuartet to item 1 of aDirectionTuple
				set item 3 of newQuartet to text -2 thru -1 of ("0" & (probe as text))
				set mark to 3
			else
				set probe to item 3 of it
				if probe begins with "-" then
					set item 1 of newQuartet to item 2 of aDirectionTuple
					set item 4 of newQuartet to text -2 thru -1 of ("0" & (((probe as number) * -1) as text))
					set mark to 4
				else if probe as number ≥ 0 then
					set item 1 of newQuartet to item 1 of aDirectionTuple
					set item 4 of newQuartet to text -2 thru -1 of ("0" & (probe as text))
					set mark to 4
				end if
			end if
		end if
		-- we transmit the rest of the arguments here.
		repeat with i from mark to 3
			set item (i + 1) of newQuartet to text -2 thru -1 of ("0" & (item i of it as number))
		end repeat
	end tell
	return newQuartet
end aMapNotationQuartet


to hexagesNormalize for degreeTriplet
	if (length of degreeTriplet < 3) then ¬
		error "coordDegLib's hexagesNormalize: incomplete triplet, Lacks one or more of: nr ,min, sec (3 terms)." number 4000
	local normalizedResult, hasNeg
	set {hasNeg, normalizedResult} to {false, {0, 0, 0}}
	
	tell normalizedResult
		set {item 1 of it, item 2 of it, item 3 of it} ¬
			to ¬
			{item 1 of degreeTriplet as number, ¬
				item 2 of degreeTriplet as number, ¬
				item 3 of degreeTriplet as number}
		-- -0 = 0 , no way to detect "-0", so we take precautions.
		set b to item 1 of degreeTriplet as number
		if (item 1 of degreeTriplet as number) = 0 and item 1 of degreeTriplet begins with "-0" then
			set hasNeg to true
		else if item 1 of degreeTriplet as number = 0 and item 2 of degreeTriplet = 0 and item 2 of degreeTriplet begins with "-0" then
			set hasNeg to true
		end if
		-- Removes any negative seconds
		if item 3 of it < 0 then
			if item 2 of it ≠ 0 then
				set item 3 of it to (item 3 of it) + 60
				set item 2 of it to (item 2 of it) - 1
			else if item 1 of it ≠ 0 then
				set item 1 of it to (item 1 of it) - 1
				set item 2 of it to 59
				set item 3 of it to (item 3 of it) + 60
			end if
		else if item 3 of it > 60 then
			-- if the term is greater than 60, then we update the minutes
			-- and subtract the minutes we transferred from the seconds.
			
			set item 2 of it to (item 2 of it) + ((item 3 of it) div 60)
			set item 3 to (item 3 of it) mod 60
		end if
		
		-- removes any negative minutes if the term isn't the first
		if item 2 of it < 0 and item 1 of it ≠ 0 then
			set item 2 of it to (item 2 of it) + 60
			set item 1 of it to (item 1 of it) - 1
		else if item 2 of it > 60 then
			-- if the term is greater than 60, then we update the degrees
			-- and subtract the degrees we transferred from the minutes
			set item 1 of it to (item 1 of it) + ((item 2 of it) div 60)
			set item 2 of it to (item 2 of it) mod 60
		end if
		
		repeat with i from 1 to 3
			if item i of it < 0 then
				if i = 1 and length of (item 1 of it as text) < 3 then
					set item i of it to "-" & (text -2 thru -1 of ("0" & ((item i of it) * -1)))
				else if i = 1 then
					-- We don't know the number of digits
					set item i of it to item i of it as text
				else
					set item i of it to "-" & (text -2 thru -1 of ("0" & ((item i of it) * -1)))
				end if
			else
				set item i of it to text -2 thru -1 of ("0" & (item i of it))
			end if
		end repeat
		-- we must restore any negative signs, that has got lost in the process!
		if hasNeg and item 1 as number ≥ 0 and item 2 as number ≥ 0 and item 3 as number ≥ 0 then set item 1 to "-00"
	end tell
	return normalizedResult
end hexagesNormalize

to gregorianCalendarTriplet from JDN
	-- From wikipedia Julian Dates
	-- This is an algorithm by Richards to convert a Julian Day Number, J, to a date in the Gregorian calendar
	-- (proleptic, when applicable). Richards does not state which dates the algorithm is valid for.
	-- it seems to work for dates after, BUT NOT INCLUDING 15th of oct 1582.
	-- I have extended it to return a hour, min, sec triplet for the fractional part of the JDN  else where.
	local y, m, N, R, p, V, s, w, b, c, f, g, h, D, MT, YR, JD
	set y to 4716
	set j to 1401
	set m to 2
	set N to 12
	set R to 4
	set p to 1461
	set V to 3
	set u to 5
	set s to 153
	set w to 2
	set b to 274277
	set c to -38
	
	set JD to JDN as integer
	set f to JD + j + (((4 * JD + b) div 146097) * 3) div 4 + c
	set E to R * f + V
	set g to (E mod p) div R
	set h to u * g + w
	set D to (h mod s) div u + 1
	set MT to (h div s + m) mod N + 1
	set YR to E div p - y + (N + m - MT) div N
	return {YR, MT, D}
end gregorianCalendarTriplet



Hello.

I have used the handlers for input in another script, and there I found some room for improvement.

I have localized a decimal separator, it should be “;” if “,” is the decimal separator, and “,” if the decimal separator is “.”.

There are also other rules, according to formatting of dates that is also considered: a “.” is a valid separator between the items of a data, as is “/”. In addition, I have stil allowed “-” as a separator between items of a data, and a space, works as a list delimiter everywhere.

Hello.

I have removed a bug, that turned minutes into negative minutes, when it shouldn’t, and thereby lowering latitude or longitude with a degree.

Hello.

This is another version, which I will add more planets to than just the moon, that is added at the moment.
There is one bug here, which occurs for the moon at 14.11.2013, for some reason, I haven’t been able to reveal yet.
The computations for the moon where supposed to be fast, rather than totally precise. They are far to inaccurate in januray when the sun is closest to the earth, there are discrepancies up to 1.5 hours. I’ll guess I’ll trade in the moon script for something else, if I can’t make it to work better as it is. -I have really tried.

Well, and your coordinates, must be entered manually. You’ll want those to be as close to you as possible, closer than the nearest city anyway. East longitude, and North latitude are positive, West and South negative.

You’ll need Satimage.Osax in order to run this, at least at the moment. :slight_smile:

Enjoy.


# Factored ui's formatClockTime into something more maintainable.

# Setting some stuff straight, and hopefully provides a faster, yet more 
# accurate program.
# using another formulae for interpolation,
property latitude : 58 + (1 / 60 * (28 + (1 / 60 * 56)))

property longitude : 8 + (1 / 60 * (46 + (1 / 60 * 57)))
property degrad : pi / 180
property arcticCircle : 66.5
property antarcticCircle : -arcticCircle
property eventNames : {"Rise:", "Transit:", "Set:"}

on run
	global am_pm_clock
	-- resets whatever values, so that any script copying will work with the receivers locale.
	set am_pm_clock to util's undef()
	
	set ScriptTitle to "Planet Times"
	
	
	set {dateRangeStart, dateRangeEnd} to ui's getDateRange((current date), "Week", ScriptTitle)
	
	if ((dateRangeEnd - dateRangeStart) / days) < 1 then
		set singular to true
	else
		set singular to false
		set oldMonth to dateRangeStart's month
		ui's _MAKEDOC("The Planet's rise, transit and set from " & dateRangeStart's short date string & " to " & dateRangeEnd's short date string & return, 550, 800)
		ui's _PRINT(linefeed)
		ui's _PRINT((dateRangeStart's month as string) & linefeed)
	end if
	
	# We use another kind of julian date here, a better kind, that starts the day at 0.h
	
	set JDT_Start to util's asdToJd(dateRangeStart)
	set JDT_cur to JDT_Start
	set JDT_end to util's asdToJd(dateRangeEnd)
	
	repeat while JDT_cur ≤ JDT_end
		local dec_rise, dec_trans, dec_set
		local probe
		set probe to (util's civilTimeToGmtFromLocalTZInSeconds(dateRangeStart)) / hours
		set solarEvents to {}
		tell (Sun's riseTransitSet(JDT_cur, longitude, latitude, probe))
			local h, m, s, at_time
			repeat with i from 1 to (count it)
				if item i of it is false then
					set end of solarEvents to false
				else
					set {h, m, s} to my ui's decDegToHexagesDeg(item i of it)
					set end of solarEvents to my ui's formatClockTime(h, m, s)
				end if
			end repeat
		end tell
		tell dateRangeStart
			set datum to it's short date string
			if day of it is 1 and JDT_cur ≠ JDT_Start then
				my ui's _PRINT(linefeed & (its month as string) & linefeed)
			end if
		end tell
		
		printPlanetLine(datum, solarEvents, "Sun  ")
		
		set lunarEvents to {}
		tell (Moon's riseTransitSet(JDT_cur, longitude, latitude, probe, my Sun))
			local h, m, s, at_time
			repeat with i from 1 to (count it)
				if item i of it is false then
					set end of lunarEvents to false
				else
					set {h, m, s} to my ui's decDegToHexagesDeg(item i of it)
					set end of lunarEvents to my ui's formatClockTime(h, m, s)
				end if
			end repeat
		end tell
		printPlanetLine(datum, lunarEvents, "Moon ")
		set dateRangeStart to dateRangeStart + 1 * days
		set JDT_cur to JDT_cur + 1
	end repeat
	
end run


on printPlanetLine(datum, planetEvents, planet)
	local i, mstr
	set mstr to datum & ": " & planet & space
	repeat with i from 1 to 3
		if item i of planetEvents = false then
			if i = 1 then
				set mstr to mstr & "No " & item i of eventNames
			else
				set mstr to mstr & ", No " & item i of eventNames
			end if
		else if item i of planetEvents = true then
			set mstr to mstr & "Up all day."
			exit repeat
		else if i = 1 then
			set mstr to mstr & item i of eventNames & " " & item i of planetEvents
		else
			set mstr to mstr & ", " & item i of eventNames & " " & item i of planetEvents
		end if
	end repeat
	my ui's _PRINT(mstr)
end printPlanetLine

script ui
	property Debug : false
	
	on pad(N)
		-- 22. September 2013, improved pad, for the cases where n < 0 and n > 100.
		-- Thanks to Yvan Koenig.
		-- Fixed a bug
		if N < -100 then
			return N as text
		else if N < 0 then
			return ("-" & text -2 thru -1 of ("0" & ((N as number) * -1) as text))
			--	error "The pad handler assumes positive values" number 5000
		else if N < 100 then
			return text 2 thru -1 of ((100 + N) as text)
		else
			return N as text
		end if
	end pad
	
	on _PRINT(txt)
		# Nigel Garvey made the original version.
		local a
		tell application "TextEdit"
			if not my Debug then
				activate
			else
				do shell script "open -b \"com.apple.textedit\""
			end if
			if ((count its documents) is 0) then make new document
			set a to (get path of its front document)
			try
				if a is "" then set a to missing value
			on error
				set a to missing value
			end try
			if a is not missing value then make new document
			make new paragraph at end of text of front document with data (txt & linefeed) with properties {font:"Andale Mono", size:12}
		end tell
		return true
	end _PRINT
	
	on _MAKEDOC(heading, _width, _height)
		tell application "TextEdit"
			tell (make new document at front)
				set text of it to heading
				set font of every character to "Andale Mono"
				set size of every character to 13
			end tell
			tell its front window
				#	set {item 3 of its bounds, item 4 of its bounds} to {800, 300}
				set _bounds to bounds
				set item 3 of _bounds to (item 1 of _bounds) + _width
				set item 4 of _bounds to (item 2 of _bounds) + _height
				set bounds to _bounds
				
			end tell
		end tell
	end _MAKEDOC
	
	# converts a decimal degree number, that is a degree, with
	# minutes and seconds as a decimal fraction into a real
	# hexagesimal number with separate terms for degrees,
	# minutes and seconds
	-- Converts from the decimalDegree coordinat system (58.4603)
	-- to DMS ( 58 27 37 )
	on decDegToHexagesDeg(decimalDegrees)
		# new version, faster with increased precision
		local tempvar, hexagesDegs, hexagesMins, hexagesSecs
		set hexagesDegs to decimalDegrees div 1
		set tempvar to (decimalDegrees mod 1) * 3600
		# have to check for sign, and negate if have degrees,
		# and degrees are negative.
		if tempvar < 0 then
			if hexagesDegs ≠ 0 then
				set tempvar to tempvar * -1
			end if
		end if
		set hexagesMins to tempvar div 60
		# this sign always correct
		set hexagesSecs to tempvar mod 60
		# have to adjus if neg, and has degrees
		if hexagesSecs < 0 then
			if hexagesMins ≠ 0 then
				set hexagesSecs to hexagesSecs * -1
			end if
		end if
		return {hexagesDegs, hexagesMins, hexagesSecs}
	end decDegToHexagesDeg
	
	on setClockFormat(m)
		local clock_format
		considering case
			tell (current date)'s time string
				if it contains "AM" or it contains "PM" then
					set clock_format to m's big_AMPM
				else if it contains "Am" or it contains "Pm" then
					set clock_format to m's ususal_AmPm
				else if it contains "am" or it contains "pm" then
					set clock_format to m's small_ampm
				else
					set clock_format to m's hour24
				end if
			end tell
		end considering
		return clock_format
	end setClockFormat
	
	on padHourWithBlank(N)
		-- "Special Handler" for padding the 12 hour clock with blanks.
		return (text -2 thru -1 of (" " & N as text))
	end padHourWithBlank
	
	on formatAmPmClock(_hr, _min, _sec, amstr, pmstr)
		if (_hr div 12) > 0 then
			return (" " & padHourWithBlank((_hr div 12)) & ":" & pad(_min) & ":" & pad((_sec div 1)) & pmstr)
		else
			if _hr < 0 or _min < 0 then
				if _hr < -12 then
					return ("-" & padHourWithBlank((-_hr div 1)) & ":" & pad(_min) & ":" & pad((_sec div 1)) & amstr)
				else if _hr < 0 then
					return ("-" & padHourWithBlank((-_hr div 1)) & ":" & pad(_min) & ":" & pad((_sec div 1)) & pmstr)
				else
					return ("-" & padHourWithBlank(_hr div 1) & ":" & pad((-_min)) & ":" & pad((_sec div 1)) & pmstr)
				end if
			else
				return (" " & padHourWithBlank((_hr div 1)) & ":" & pad(_min) & ":" & pad((_sec div 1)) & amstr)
			end if
		end if
	end formatAmPmClock
	
	on formatClockTime(_hr, _min, _sec)
		-- New! Deals with negative hours.
		script o
			property big_AMPM : 3
			property ususal_AmPm : 2
			property small_ampm : 1
			property hour24 : 0
		end script
		global am_pm_clock
		local a
		try
			set a to am_pm_clock
		on error
			set am_pm_clock to setClockFormat(o)
		end try
		
		if am_pm_clock = o's hour24 then
			if _hr < 0 or _min < 0 then
				if _hr < 0 then
					return ("-" & pad(((-_hr) div 1)) & ":" & pad(_min) & ":" & pad((_sec div 1)))
				else
					return ("-" & pad(((_hr) div 1)) & ":" & pad(-_min) & ":" & pad((_sec div 1)))
				end if
			else
				return (" " & pad((_hr div 1)) & ":" & pad(_min) & ":" & pad((_sec div 1)))
			end if
		else if am_pm_clock = o's big_AMPM then
			return (formatAmPmClock(_hr, _min, _sec, " AM", " PM"))
		else if am_pm_clock = o's ususal_AmPm then
			return (formatAmPmClock(_hr, _min, _sec, " Am", " Pm"))
		else # am_pm_clock = small_ampm 
			return (formatAmPmClock(_hr, _min, _sec, " am", " pm"))
		end if
	end formatClockTime
	
	-- Ask the user for the range of dates to be covered.
	-- from a start date, up to, and including enddate.
	on getDateRange(initialDate, initialRange, ScriptTitle)
		-- Nigel Garvey http://macscripter.net/viewtopic.php?id=41273
		-- customized a little by McUsr (All faults are mine!), 
		-- so it is able to deliver just one date denoting a range of 1 day.
		-- There are preset ranges of Month, Week, and Year, based on the initalDate.
		set anIcon to note
		set theMessage to "Enter the required date range:"
		set today to initialDate
		repeat
			if initialRange = "Month" then
				set enddate to today + 30 * days
				set day of enddate to day of today
				set i to 0
				repeat while month of enddate > ((month of today) + 1)
					set month of enddate to ((month of enddate) - 1)
					set day of enddate to ((day of enddate) - 1)
					set i to i + 1
				end repeat
				if i > 0 then
					set enddate to today + (30 - i) * days
				end if
			else if initialRange = "Week" then
				set enddate to today + 6 * days
			else if initialRange = "Year" then
				set enddate to today + 364 * days
				# We'll add one day for a leap year
				local yr
				
				if today's month ≤ 3 then
					set yr to today's year
					if ((yr mod 4) = 0) and (((yr mod 100) ≠ 0) or ((yr mod 400) = 0)) then set enddate to endate + 1 * days
				else
					set yr to enddate's year
					if ((yr mod 4) = 0) and (((yr mod 100) ≠ 0) or ((yr mod 400) = 0)) then set enddate to endate + 1 * days
				end if
			end if
			
			set d1 to today's short date string
			set d2 to enddate's short date string
			
			tell application (path to frontmost application as text)
				set dateRange to text returned of (display dialog theMessage default answer d1 & " - " & d2 with title ScriptTitle buttons {"Quit", "Ok"} cancel button 1 default button 2 with icon anIcon)
				
			end tell
			try
				set {tids, AppleScript's text item delimiters, gotOne} to {AppleScript's text item delimiters, "-", false}
				set {dateRangeStart, gotOne} to {date (text item 1 of dateRange), true}
				
				set dateRangeEnd to date (text item 2 of dateRange)
				set AppleScript's text item delimiters to tids
				exit repeat
			on error
				if gotOne and length of text items of dateRange = 1 then
					set dateRangeEnd to date (text item 1 of dateRange)
					set AppleScript's text item delimiters to tids
					exit repeat
				else if gotOne then
					set today to dateRangeStart
				else
					set today to initialDate
				end if
				set AppleScript's text item delimiters to tids
				if theMessage does not contain "wasn't" then
					set theMessage to "The input  wasn't  valid!" & return & return & theMessage
					set anIcon to caution
				end if
			end try
		end repeat
		set dateRangeEnd's time to days - 1 -- Sets the last date's time to 23:59:59, the last second of the range.
		return {dateRangeStart, dateRangeEnd}
	end getDateRange
end script

script util
	property degrad : pi / 180
	
	on civilTimeToGmtFromLocalTZInSeconds(anASDate)
		return (anASDate - (TZtoTZ(anASDate, (do shell script "readlink /etc/localtime |sed -n 's_/[^/]*/[^/]*/[^/]*/\\([^/]*\\)/\\([^/]*\\).*_\\1/\\2_p'"), "GMT")))
	end civilTimeToGmtFromLocalTZInSeconds
	
	on TZtoTZ(TZ1date, TZ1, TZ2)
		return (do shell script ("eraTime=$(TZ=" & TZ1 & " date -jf '%FT%T' '" & (TZ1date as «class isot» as string) & "' '+%s') ; TZ=" & TZ2 & " date -r \"$eraTime\" '+%FT%T'") as «class isot») as date
	end TZtoTZ
	
	on rev(x)
		tell (x)
			return it - ((it div 360.0) * 360.0) + ((it < 0) as integer) * 360
		end tell
	end rev
	
	on cosd(x)
		return cos (x * degrad)
	end cosd
	
	on sind(x)
		return (sin (x * degrad))
	end sind
	
	on asind(x)
		return ((asin (x)) / degrad)
	end asind
	
	on sphericalToRectangular(Ra, decl, r)
		-- http://www.stjarnhimlen.se/comp/tutorial.html
		local x, y, z
		set x to r * (cosd(Ra)) * (cosd(decl))
		set y to r * (sind(Ra)) * (cosd(decl))
		set z to r * (sind(decl))
		return {x, y, z}
	end sphericalToRectangular
	
	on rectangularToSpherical(x, y, z)
		-- http://www.stjarnhimlen.se/comp/tutorial.html
		local Ra, decl, r
		set r to (x * x + y * y + z * z) ^ 0.5
		if x = 0 and y = 0 then
			set Ra to 0
		else
			set Ra to atan2 {y, x}
		end if
		if (y < 0.1 and y > -0.1) then
			set decl to atan2 {z, (x * x + y * y) ^ 0.5}
		else
			set decl to asin (z / r)
		end if
		return {Ra * 180 / pi, decl * 180 / pi, r}
	end rectangularToSpherical
	
	on rectEclipTorectEquat(xeclip, yeclip, zeclip, oblecl)
		-- http://www.stjarnhimlen.se/comp/tutorial.html	
		-- same formulae for rotating from equat to eclip
		-- but now oblecl must be negative!
		local xequat, yequat, zequat
		set xequat to xeclip
		set yequat to yeclip * (cosd(oblecl)) - zeclip * (sind(oblecl))
		set zequat to yeclip * (sind(oblecl)) + zeclip * (cosd(oblecl))
		return {xequat, yequat, zequat}
	end rectEclipTorectEquat
	
	on rotateAlongY(x, y, z, obsLat)
		# Rotates Rectangualr geometric coordinates into
		# topocentric.
		local xhor, yhor, zhor
		set xhor to x * (my Sun's util's sind(obsLat)) - z * (my Sun's util's cosd(obsLat))
		set yhor to y
		set zhor to x * (my Sun's util's cosd(obsLat)) + z * (my Sun's util's sind(obsLat))
		local az, alt
		set az to (atan2 {yhor, xhor}) / degrad + 180
		set alt to (asin zhor) / degrad
		#	set alt to (atan2 {zhor, ((xhor * xhor + yhor * yhor) ^ 0.5)}) / degrad
		return {az, alt}
	end rotateAlongY
	
	on horizHd(latDeg, declDeg, lhaDeg)
		# returns the height over or under the current
		# horizon in degrees. same as topocentric altitude
		return (asin ((sin (latDeg * degrad)) * (sin (declDeg * degrad)) + ((cos (latDeg * degrad)) * (cos (declDeg * degrad)) * (cos (lhaDeg * degrad))))) / degrad
	end horizHd
	
	on topoCentric(ha, decl, r, lat)
		# returns topocentric (local coordinates)
		# in the form azimuth, altitude.
		local ex, ey, ez
		set {ex, ey, ez} to Sun's util's sphericalToRectangular(lha, decl, r)
		return rotateAlongY(ex, ey, ez, lat)
	end topoCentric
	
	on asdToJd(asDate)
		-- one for converting into Schlyters version of Julian Day numbers.
		tell (asDate)
			return util's JD(its year, its month as integer, its day)
		end tell
	end asdToJd
	
	on JD(yr, mt, dy)
		# Julian day number, + 12 hours, so you won't have to adjust for the 
		# surplus of 12 hours, this julian day starts at ut0h, not ut-12h.
		return (367.0 * yr - (7.0 * (yr + ((mt + 9.0) / 12.0))) / 4.0 + (275.0 * mt) / 9.0 + dy - 7.3053E+5) div 1
	end JD
	
	on rised(riseDeg, latDeg, declDeg, dontIterate)
		# Computes the local hour angle, or returns
		# true or false if the planet is over
		# respective under the horizon at all times.
		local cosLHA
		set cosLHA to ((sin (degrad * riseDeg)) - ((sin (latDeg * degrad)) * (sin (declDeg * degrad)))) / ((cos (latDeg * degrad)) * (cos (declDeg * degrad)))
		if cosLHA < -1 then
			return true # above the horizon at all times
		else if cosLHA > 1 then
			return false # below horizion at all times
		else
			if dontIterate then
				return (acos cosLHA) / degrad / 15
			else
				return (acos cosLHA) / degrad / 15.04107
			end if
		end if
	end rised
	
	on horizontalFromEquatorial(lha, decl, latitude)
		# Latest version!
		# The LHA/Decl system. Both arguments in radians! Local Hour Angle, Declination
		# (as opposed to Right Ascension (RA)).
		# latitude, also in radians is the observers latitude.
		# Returns Azimuth, and elevation.
		# Azimuth is the longtitude, usually and here measured from North.
		# h_ is the elevation, or altitude, and is the angle between an object and the observers horizon.
		local Azimuth, Azimuth2, h_
		set Azimuth to atan2 {(sin lha), (((cos lha) * (sin latitude)) - ((tan decl) * (cos latitude)))}
		# Meeus (12.5) (both above)
		set h_ to asin (((sin latitude) * (sin decl)) + ((cos latitude) * (cos decl) * (cos lha)))
		# Meeus ( 12.6)
		return {Azimuth, h_}
	end horizontalFromEquatorial
	
	to offsetOfLocalhourAngle(decLongitude)
		# Ripped from coord deg lib:
		# eastern longitude is positive. westwards negative.
		
		# see: http://macscripter.net/viewtopic.php?pid=166581#p166581
		# post 43
		# we remove any superfluos degrees first.
		set decLongitude to decLongitude mod 360
		
		# we make angles ≥ 180 into negatives
		if decLongitude ≥ 180 then
			set decLongitude to decLongitude - 360
		end if
		local hourAngle
		if decLongitude < 0 then
			set decLongitude to (decLongitude * -1) - 0.1
			# we have to skew the numbers by -0.1 to keep
			# boundary conditions
			set hourAngle to (24 - ((decLongitude + 7.5) div 15)) mod 24
		else
			set hourAngle to (decLongitude + 7.5) div 15
		end if
		# 2013.10.24: fixed bug, when adding 7.5 to say 178 degrees.
		if hourAngle ≥ 12 then # we'll turn it into its negative counter part.
			set hourAngle to -(12 + (12 - hourAngle))
		end if
		return hourAngle
	end offsetOfLocalhourAngle
	
	on undef()
		return
	end undef
	
	on findZeroPoint5PointsCurve(tbl, cp)
		# Meeus (3.11) (cp = centerpoint, is what is named y3 in Meeus)
		local d1, d2, d3
		set {d1, d2, d3} to {{}, {}, {}}
		repeat with i from (cp - 2) to (cp + 1)
			set end of d1 to (item (i + 1) of tbl) - (item i of tbl)
			
		end repeat
		repeat with i from 1 to 3
			set end of d2 to (item (i + 1) of d1) - (item i of d1)
		end repeat
		repeat with i from 1 to 2
			set end of d3 to (item (i + 1) of d2) - (item i of d2)
		end repeat
		set B_ to item 2 of d1
		set C_ to item 3 of d1
		set F_ to item 2 of d2
		set h_ to item 1 of d3
		set J_ to item 2 of d3
		set K_ to J_ - h_
		set M_ to (K_ / 24)
		set N_ to ((h_ + J_) / 12)
		set P_ to ((F_ / 2) - M_)
		set Q_ to (((B_ + C_) / 2) - N_)
		
		local n0, n1
		set n0 to (item cp of tbl)
		repeat while true
			set n1 to n0
			set p1 to -1 * ((M_ * (n0 ^ 4)) + (N_ * (n0 ^ 3)) + (P_ * (n0 ^ 2)) + (Q_ * n0) + (item cp of tbl))
			set c1 to ((4 * M_ * (n0 ^ 3)) + (3 * N_ * (n0 ^ 2)) + (2 * P_ * n0) + Q_)
			# Needs a lot of curve to use this one.
			set n0 to n0 + (p1 / c1)
			if (abs (n1 - n0)) < 1.0E-14 then exit repeat
		end repeat
		return n0
	end findZeroPoint5PointsCurve
	
	on hasZeroPoint(theTbl)
		# The handler returns whether we are passing
		# a zero point or not, an even better version
		# for when the number of items aren't set, is
		# one that returns the zeropoint value, or the
		# middle of them if several.
		# It is to be used for classifying our table 
		# for an interpolation of the zero point.
		script o
			property l : theTbl
		end script
		local flipped, direction
		set {flipped, direction} to {false, 0}
		# if all values are zero, then we aren't flipped.
		if item 1 of o's l ≥ -1.0E-4 and item 1 of o's l < 0 then
			set direction to -0.5
		else if item 1 of o's l < 1.0E-4 and item 1 of o's l > 0 then
			set direction to 0.5
		else if item 1 of o's l < -1.0E-4 then
			set direction to -1
		else if item 1 of o's l > 1.0E-4 then
			set direction to 1
		end if
		local central
		repeat with i from 2 to (length of o's l)
			if item i of o's l ≥ -1.0E-4 and item i of o's l < 0 then
				if direction > 0 and not flipped then
					set flipped to true
					if (abs (item (i - 1) of o's l)) < (abs (item i of o's l)) then
						set central to i - 1
					else
						set central to i
					end if
				else if direction > 0 then
					return true
				end if
				set direction to -0.5
			else if item i of o's l < 1.0E-4 and item i of o's l > 0 then
				if direction < 0 and not flipped then
					set flipped to true
					if (abs (item (i - 1) of o's l)) < (abs (item i of o's l)) then
						set central to i - 1
					else
						set central to i
					end if
				else if direction < 0 then
					return true
				end if
				set direction to 0.5
			else if item i of o's l < -1.0E-4 then
				if direction > 0 and not flipped then
					set flipped to true
					if (abs (item (i - 1) of o's l)) < (abs (item i of o's l)) then
						set central to i - 1
					else
						set central to i
					end if
				else if direction > 0 then
					return true
				end if
				set direction to -1
			else if item i of o's l > 1.0E-4 and not flipped then
				if direction < 0 and not flipped then
					set flipped to true
					if (abs (item (i - 1) of o's l)) < (abs (item i of o's l)) then
						set central to i - 1
					else
						set central to i
					end if
					set direction to 1
				else if direction < 0 then
					return true
				end if
			end if
		end repeat
		if not flipped then
			return false
		else
			return central
		end if
	end hasZeroPoint
	
end script

script Sun
	
	property arcticCircle : 66.5
	property antarcticCircle : -arcticCircle
	property w : 0 --       Sun's longitude of Perihelion.
	property ecc_sun : 0 -- Sun's Eccentricity.
	property ms : 0 --      Sun's MeanAnomaly.
	property ls : 0 --      Sun's  mean longitude.
	property obecl : 0 --   Obliquity of the Ecliptic.
	property E_ : 0 --      Eccentric anamoly of the Sun.
	property v : 0 --       Sun's true anamoly.
	property rs : 0 --      Distance to sun in AU.
	property tls : 0 --     Sun's true longitude.
	property gmst0 : 0 --   Greenwhich mean sidereal time at ut 0h -in hours!
	
	property Ra : 0 --      Stored internally so we can
	property decl : 0 --    save some computations.
	
	
	on riseTransitSet(theJd, obsLong, obsLat, civOffset)
		local dontIterate
		local physOffset
		set offsetDiff to civOffset - (util's offsetOfLocalhourAngle(obsLong))
		if obsLat > antarcticCircle and obsLat < arcticCircle then
			set dontIterate to false
			local hr_transit
			set hr_transit to UT_LocalNoon(obsLong, theJd, dontIterate)
			
			set hr_transit to (hr_transit + civOffset + 24) mod 24
			local Ra, decl
			set {Ra, decl} to my Sun's fetchRaDecl()
			local rlha, hr_rise, hr_set
			set rlha to my util's rised(-0.583, obsLat, decl, dontIterate)
			
			set hr_rise to hr_transit - rlha
			set hr_set to hr_transit + rlha
			
		else
			set dontIterate to true
			
			local hr_transit
			set hr_transit to (my Sun's UT_LocalNoon(obsLong, theJd, dontIterate))
			local Ra, decl
			set {Ra, decl} to my Sun's fetchRaDecl()
			local rlha, hr_rise, hr_set
			set rlha to my util's rised(-0.583, obsLat, decl, dontIterate)
			
			
			if rlha is not true and rlha is not false then
				set dontIterate to false
				
				repeat 4 times
					local old_hr_transit
					set old_hr_transit to hr_transit
					set hr_transit to (my Sun's UT_LocalNoon(obsLong, theJd, dontIterate))
					#				set hr_transit to (hr_transit + civOffset + 24) mod 24
					if (abs old_hr_transit - hr_transit) < 0.016 then exit repeat
					set theJd to theJd div 1 + hr_transit / 24
				end repeat
				
				set hr_rise to hr_transit - rlha
				
				local temp_hr_transit, tempJD
				set {temp_hr_transit, tempJD} to {hr_transit, theJd}
				repeat 4 times
					local old_hr_rise
					set old_hr_rise to hr_rise
					set tempJD to tempJD div 1 + hr_rise / 24
					set temp_hr_transit to (my Sun's UT_LocalNoon(obsLong, tempJD, dontIterate))
					#	set temp_hr_transit to (temp_hr_transit + civOffset + 24) mod 24
					
					set rlha to my util's rised(-0.583, obsLat, decl, dontIterate)
					set hr_rise to temp_hr_transit - rlha
					if (abs old_hr_rise - hr_rise) < 0.016 then exit repeat
				end repeat
				
				if (tempJD div 1) < (theJd div 1) then set hr_rise to hr_rise - 24
				
				-- If the end result is < 0 then it is for the previous day
				-- which we'll return ( jd-1)
				
				set hr_set to hr_transit + rlha
				set {temp_hr_transit, tempJD} to {hr_transit, theJd}
				repeat 4 times
					local old_hr_set
					set old_hr_set to hr_set
					set tempJD to tempJD div 1 + hr_set / 24
					set temp_hr_transit to (my Sun's UT_LocalNoon(obsLong, tempJD, dontIterate))
					#	set temp_hr_transit to (temp_hr_transit + civOffset + 24) mod 24
					
					set rlha to my util's rised(-0.583, obsLat, decl, dontIterate)
					set hr_set to temp_hr_transit + rlha
					if (abs old_hr_set - hr_set) < 0.016 then exit repeat
				end repeat
				
				if (tempJD div 1) > (theJd div 1) then set hr_set to hr_set + 24
				
				-- If the end result is > 24 then it is for the next day
				-- which we'll return (jd+1).
			else if rlha then
				-- Sun above horizon whole day
				return true
			else if not rlha then
				-- Sun below horizon whole day
				return false
			end if
		end if
		return {hr_rise + offsetDiff, hr_transit + offsetDiff, hr_set + offsetDiff}
	end riseTransitSet
	
	on UT_LocalNoon(declong, D, dontIterate)
		
		# As an added bonus, we get the equation of time baked into the
		# result, since we get the apparent right ascension of the sun.
		
		local physOfs
		set physOfs to util's offsetOfLocalhourAngle(declong)
		if dontIterate then
			set D to D div 1 + 0.5 - physOfs / 24
		else
			# set D to D div 1 - physOfs / 24
			set D to D - physOfs / 24
		end if
		ComputeOrbitalElements(D)
		local tst
		set tst to my Sun's gmst0
		
		local Ra, decl, ut
		set {Ra, decl} to my Sun's raDecl()
		if dontIterate then
			set ut to (Ra - gmst0 - declong / 15)
		else
			set ut to (Ra - gmst0 - declong / 15.04107)
		end if
		return (ut + 24) mod 24 # Clock arithmetic.
	end UT_LocalNoon
	
	on fetchRaDecl()
		return {Ra, decl}
	end fetchRaDecl
	
	on last_gmst0()
		# returns the gmst0 from last computation of orbital elements.
		return gmst0
	end last_gmst0
	
	on lmst(ut, obsLong)
		# .6 in http://www.stjarnhimlen.se/comp/tutorial.html#2
		local lst
		set lst to (gmst0 + ut + obsLong / 15) mod 24
		if lst < 0 then set lst to lst + 24
		return lst
	end lmst
	
	# Sun's orbital elements, the cornerstone of it all.
	# -in our part of the Galaxy anyway.
	# Computation of orbital elements for getting a sane
	# gmst0, so that we can compute sidereal time should be
	# done at 12 ut! ((Jd div 1) + 0.5)
	on ComputeOrbitalElements(D)
		set w to SunsLongOfPerihelion(D)
		set ecc_sun to SunsEccentricity(D)
		set ms to util's rev(SunsMeanAnomaly(D))
		set ls to util's rev(w + ms)
		set gmst0 to (util's (rev((my ls) + 180))) / 15
		set obecl to obleclOfEcliptic(D)
		set E_ to SunsEccAnamoly(D, ecc_sun, ms)
	end ComputeOrbitalElements
	
	
	on raDecl()
		# compute suns distance r (to earth in AU), and true anamoly v..
		local xv, yv
		set xv to (util's cosd(E_)) - ecc_sun
		set yv to ((1 - ecc_sun * ecc_sun) ^ 0.5) * (util's sind(E_))
		set v to (atan2 {yv, xv}) / (util's degrad)
		set rs to ((xv * xv) + (yv * yv)) ^ 0.5
		# compute Sun's true longitude:
		set tls to util's rev(w + v)
		
		# first compute ecliptic rectangular coordinates
		local xs, ys, zs
		set zs to 0
		set xs to rs * (util's cosd(tls))
		set ys to rs * (util's sind(tls))
		# convert to equatorial rect coordinates:
		local xe, ye, ze
		set xe to xs
		set ye to ys * (util's cosd(obecl))
		set ze to ys * (util's sind(obecl))
		# convert to right ascension and declination
		# Ra, decl are properties that are set.
		if ye = 0 and xe = 0 then
			set Ra to 0
		else
			set Ra to (atan2 {ye, xe}) / (util's degrad) / 15
		end if
		set decl to (atan2 {ze, (xe * xe + ye * ye) ^ 0.5}) / (util's degrad)
		return {Ra, decl}
	end raDecl
	
	on SunsLongOfPerihelion(D)
		return (282.9404 + 4.70935E-5 * D)
	end SunsLongOfPerihelion
	
	on SunsEccentricity(D)
		return (0.016709 - 1.151E-9 * D)
	end SunsEccentricity
	
	on SunsMeanAnomaly(D)
		return (356.047 + 0.9856002585 * D)
	end SunsMeanAnomaly
	
	on SunsEccAnamoly(D, e, m)
		return (m + (180 / pi) * e * (util's sind(m)) * (1 + e * (util's cosd(m))))
	end SunsEccAnamoly
	
	on obleclOfEcliptic(D)
		return (23.4393 - 3.563E-7 * D)
	end obleclOfEcliptic
	
end script


script Moon
	property lunarArcticCircle : 61.5
	property lunarAntarcticCircle : -lunarArcticCircle
	
	property i : 5.1454 #  Inclination
	property a : 60.2666 # Earth radii 
	property e : 0.0549 #  Eccentricity
	property N : 0 #       Longitude of ascending node.
	property w : 0 #       Argument of Perigee
	property m : 0 #       Mean anamoly.
	property lm : 0 #      MoonsMeanLongitude
	property F_ : 0 #      MoonsArgumentOfLatitude
	property E_ : 0 #      Eccentric Anomaly - Keplers equation.
	property r : 0 #       Radix in earthradii
	property v : 0 #       True anamoly 
	property D_ : 0 #      MoonsMeanElongation
	
	on riseTransitSet(theJd, obsLong, obsLat, civOffset, theSun)
		-- Computes the rise, transit and set and delivers in a triple.
		-- theJD: we don't consider a time fraction.
		script o
			property lst : missing value # Local sidereal time
			property ha : missing value #  Hour angle
		end script
		
		local physOffset
		set physOffset to util's offsetOfLocalhourAngle(obsLong)
		
		-- This is a feeble attempt to get as close as possible to the real
		-- time, which will fail as long as the algorithms aren't exact enough.
		-- I don't see how any kind of interpolation can improve this result
		if obsLat > lunarAntarcticCircle and obsLat < lunarArcticCircle then
			-- Whence Moon's transit through our local meredian?
			local hr_transit
			set theJd to theJd div 1 + 0.5 + physOffset / 24 -- our UT vantage point.
			repeat 4 times
				set hr_transit to (my hourAngleOfTransit((theJd), obsLong, obsLat, ¬
					theSun, civOffset, ¬
					a reference to o's lst, ¬
					a reference to o's ha) ¬
					) # - offsetDiff
				
				set theJd to theJd - hr_transit / 24
				# Starts off at noon, when positive, it happened some hours ago.
				# we do subtract that time. We effectively add, if it hasn't happned yet.
			end repeat
			
			local localtime
			set localtime to (theJd mod 1) * 24 + civOffset
			
			#	set theJd to theJd + offsetDiff / 24
			local transitJD
			set transitJD to theJd
			set hr_transit to theJd mod 1 * 24
			
			--	 Preparations for finding rise and set.
			-- First we'll recompute the correct orbital elements for the moon.
			
			
			-- Compute the Right Ascencion and declination, with adjustement
			-- for parallax; pertubations are already accounted for.
			
			
			-- Calculates the distance in time from rise/set to transit.
			local rlha, dontIterate
			set dontIterate to true
			set rlha to (my util's rised(-0.583 - (my mpar), obsLat, (my topDecl), dontIterate))
			-- h = -0.583 degrees - m_par: 
			-- Center of Moon's disk touches the horizon; atmospheric refraction considered.
			
			-- Calculates the vantage points in time for rise/set that we'll center about
			-- when creating the interpolation tables.
			
			local hr_rise, hr_set, sgn
			set hr_set to hr_transit + rlha
			
			set hr_rise to hr_transit - rlha
			set theJd to theJd div 1 + hr_rise / 24
			if hr_rise < 0 then
				set sgn to -1
			else
				set sgn to 1 -- So we can tell difference between today and yesterday.
			end if
			set hr_transit to hr_transit + civOffset
			--
			-- Prepares data for the interpolation table.
			set hr_rise to my narrowedTime(sgn, theJd, civOffset, obsLong, obsLat, theSun)
			
			# And we should of course compute the set time as well!
			set theJd to theJd div 1 + hr_set / 24 # we start off where we left..
			if hr_set > 24 then
				set sgn to -1
			else
				set sgn to 1
			end if
			
			set hr_set to my narrowedTime(sgn, theJd, civOffset, obsLong, obsLat, theSun)
			if class of hr_set is integer and hr_set < 0 then set hr_set to hr_set + 48 -- (sgn)
			
			# some post processing since it isn't obvious that we have 
			# all values. We also do correct for any differences between
			# physical timezone and civil time here.
			#	set discrepancyToGMT
			
			if class of hr_rise is boolean and class of hr_set is boolean then
				return {false, hr_transit + civOffset, false}
			else if class of hr_rise is boolean then
				return {false, hr_transit + civOffset, hr_set}
			else if class of hr_set is boolean then
				return {hr_rise + 1, hr_transit + civOffset, false}
			else
				return {hr_rise + 1, hr_transit + civOffset, hr_set}
			end if
		else
			return false
		end if
	end riseTransitSet
	
	-- New version, that tries to be more correct.
	on narrowedTime(sgn, theJd, civOffset, obsLong, obsLat, theSun)
		script o
			property ha : 0
			property lst : 0
		end script
		local eventJDtmp, ipTbl
		set ipTbl to {}
		#		repeat with i from -8 to 8 by 1
		repeat with i from -6 to 6 by 1
			set eventJDtmp to theJd + i * 0.25 / 24
			(my hourAngleOfTransit(eventJDtmp, obsLong, obsLat ¬
				, theSun, civOffset, ¬
				a reference to o's lst, ¬
				a reference to o's ha) ¬
				)
			
			set end of ipTbl to (my util's horizHd(obsLat, my topDecl, (o's ha) * 15)) ¬
				+ (-1 * (-0.583 - (my mpar)))
		end repeat
		
		-- Determines existance of zero value in the interval.
		local flipped
		set flipped to my util's hasZeroPoint(ipTbl)
		#		log "jd = " & theJd & "flipped = " & flipped
		if class of flipped is integer then
			
			# We'll use the interpolation table to find the zero-value.
			#			set eventJDtmp to eventJDtmp - (8 + (8 - flipped)) * 0.25 / 24
			set eventJDtmp to eventJDtmp - (6 + (6 - flipped)) * 0.25 / 24
			# rolled back: the central xvalue we'll use
			
			set hr_event to eventJDtmp mod 1 * 24
			
			set N to my util's findZeroPoint5PointsCurve(ipTbl, flipped)
			set hr_event to hr_event + N * 0.25 + civOffset
			
			if sgn < 0 then
				set hr_event to hr_event - 24 # so we remember it is from the day before!
			end if
		else
			set hr_event to false
		end if
		return hr_event
	end narrowedTime
	
	
	on hourAngleOfTransit(D, obsLong, obsLat, theSun, civOffset, lst, ha)
		local Ra, decl, physOfs, ut_h
		set physOfs to util's offsetOfLocalhourAngle(obsLong)
		
		
		theSun's ComputeOrbitalElements(D)
		
		set ut_h to (D mod 1) * 24
		set {Ra, decl} to raDecl(D, theSun)
		# set {Ra, decl} to raDecl(D, theSun)
		set contents of lst to theSun's lmst(ut_h, obsLong)
		#	set contents of ha to ((contents of lst) * 15 - Ra) / 15 - physOfs
		set contents of ha to ((contents of lst) * 15 - Ra) / 15
		local topRa, topDecl
		set {topRa, topDecl} to my Moon's ¬
			raDeclWithParallax(my Moon's pertRA, my Moon's pertDecl, ¬
				(contents of ha) * 15, ¬
				obsLat)
		set contents of ha to ((contents of lst) * 15 - topRa) / 15
		return contents of ha
	end hourAngleOfTransit
	
	property topRa : 0
	property topDecl : 0
	
	on raDeclWithParallax(Ra, decl, ha, obsGeodetLat)
		# Deals with Parallax
		local topoAlt
		set topoAlt to decl - (my mpar) * (my util's cosd(decl))
		local obsGeocentLat
		set obsGeocentLat to obsGeodetLat - 0.1924 * (my util's sind(2 * obsGeodetLat))
		local rho
		set rho to 0.99833 + 0.00167 * (my util's cosd(2 * obsGeodetLat))
		local g # Auxillary angle
		set g to (atan2 {(tan ¬
			(obsGeocentLat * (my util's degrad))), ¬
			(cos (ha * (my util's degrad)))} ¬
			) ¬
			/ (my util's degrad)
		-- Now we convert the geocentric Right Ascension and Declination
		
		set topRa to Ra - (my mpar) * rho ¬
			* (cos (obsGeocentLat * (my util's degrad))) ¬
			* (sin (ha * (my util's degrad))) ¬
			/ (my util's cosd(decl))
		
		set topDecl to decl - (my mpar) * rho ¬
			* (sin (obsGeocentLat * (my util's degrad))) ¬
			* (sin ((g - decl) * (my util's degrad))) ¬
			/ (sin (g * (my util's degrad)))
		return {topRa, topDecl}
	end raDeclWithParallax
	
	on raDecl(D, theSun)
		
		ComputeOrbitalElements(D, theSun's ls)
		ComputeEclipticRectCoords()
		ComputeEclipticSphericalCoords()
		ComputePerturbations(theSun's ms)
		return EquaPertCoords(theSun's obecl)
	end raDecl
	
	on ComputeOrbitalElements(D, sun_ls)
		set N to longOfMoonsAscNode(D)
		set w to MoonsArgOfPerigee(D)
		set m to MoonsMeanAnamoly(D)
		set lm to util's rev(N + w + m)
		set F_ to util's rev(w + m)
		set E_ to MoonsEccAnamoly(m, e)
		local x_lunOrb, y_lunOrb
		# rectangular coordinates in the plane of the lunar orbit
		set x_lunOrb to a * ((cos E_ * (util's degrad)) - e)
		set y_lunOrb to a * ((1 - e * e) ^ 0.5) * (sin E_ * (util's degrad))
		set r to (x_lunOrb * x_lunOrb + y_lunOrb * y_lunOrb) ^ 0.5
		set v to util's rev(180 / pi * (atan2 {y_lunOrb, x_lunOrb}))
		set D_ to lm - sun_ls
	end ComputeOrbitalElements
	
	property xeclip : 0
	property yeclip : 0
	property zeclip : 0
	
	on ComputeEclipticRectCoords()
		
		set xeclip to r * ((util's cosd(N)) * (util's cosd(v + w)) ¬
			- (util's sind(N)) * (util's sind(v + w)) * (util's cosd(i)))
		
		set yeclip to r * (((util's sind(N)) * (util's cosd(v + w)) ¬
			+ (util's cosd(N)) * (util's sind(v + w)) * (util's cosd(i))))
		
		set zeclip to r * ((util's sind(v + w)) * (util's sind(i)))
		
	end ComputeEclipticRectCoords
	
	property eclLong : 0
	property eclDecl : 0
	
	on ComputeEclipticSphericalCoords()
		
		set eclLong to util's rev(180 / pi * (atan2 {yeclip, xeclip}))
		
		set eclDecl to 180 / pi * (atan2 {zeclip, (xeclip * xeclip + yeclip * yeclip) ^ 0.5})
		
	end ComputeEclipticSphericalCoords
	
	property eclPertLong : 0
	property eclPertDecl : 0
	property rpert : 0
	property mpar : 0 # Moon's parallax
	
	on ComputePerturbations(sun_ms)
		set eclPertLong to eclLong + pert_long(m, D_, sun_ms, F_)
		set eclPertDecl to eclDecl + pert_lat(F_, D_, m)
		set rpert to r + pert_lunarDist(m, D_)
		set mpar to util's asind(1 / rpert)
	end ComputePerturbations
	
	property pertRA : 0
	property pertDecl : 0
	# We just use these to save computations! when we have already iterated
	# the values as a side effect of finding the moon's hour angle.
	
	on EquaPertCoords(sun_obecl)
		local xPertEclip, yPertEclip, zPertEclip
		set {xPertEclip, yPertEclip, zPertEclip} to ¬
			util's sphericalToRectangular(eclPertLong, eclPertDecl, 1)
		local xEqua, yEqua, zEqua
		set {xEqua, yEqua, zEqua} to ¬
			util's rectEclipTorectEquat(xPertEclip, yPertEclip, zPertEclip, sun_obecl)
		local Ra
		set {Ra, pertDecl} to util's rectangularToSpherical(xEqua, yEqua, zEqua)
		set pertRA to util's rev(Ra)
		return {pertRA, pertDecl}
	end EquaPertCoords
	
	on longOfMoonsAscNode(D)
		return (util's rev(125.1228 - 0.0529538083 * D))
	end longOfMoonsAscNode
	
	on MoonsArgOfPerigee(D)
		return (util's rev(318.0634 + 0.1643573223 * D))
	end MoonsArgOfPerigee
	
	on MoonsMeanAnamoly(D)
		return (util's rev(115.3654 + 13.0649929509 * D))
	end MoonsMeanAnamoly
	
	on MoonsEccAnamoly(m, ecc)
		local e0, e1, diff
		-- first approximation
		set e0 to m + (180 / pi) * ecc * (sin (m * (util's degrad)) * (1 + ecc * (cos (m * (util's degrad)))))
		set {e1, diff} to {1, e0}
		repeat while (abs (diff)) > 5.0E-4
			set e1 to e0 - (e0 - (180 / pi) * ecc * (sin (e0 * (util's degrad))) - m) / (1 - ecc * (cos (e0 * (util's degrad))))
			set {diff, e0} to {e1 - e0, e1}
		end repeat
		return e1
	end MoonsEccAnamoly
	
	on pert_long(mm, D, ms, f)
		return (-1.274 * (util's sind(mm - 2 * D)) ¬
			+ 0.658 * (util's sind(2 * D)) ¬
			- 0.186 * (util's sind(ms)) ¬
			- 0.059 * (util's sind(2 * mm - 2 * D)) ¬
			- 0.057 * (util's sind(mm - 2 * D + ms)) ¬
			+ 0.053 * (util's sind(mm + 2 * D)) ¬
			+ 0.046 * (util's sind(2 * D - ms)) ¬
			+ 0.041 * (util's sind(mm - ms)) ¬
			- 0.035 * (util's sind(D)) ¬
			- 0.031 * (util's sind(mm + ms)) ¬
			- 0.015 * (util's sind(2 * f - 2 * D)) ¬
			+ 0.011 * (util's sind(mm - 4 * D)))
	end pert_long
	
	on pert_lat(f, D, mm)
		return (-0.173 * (util's sind(f - 2 * D)) ¬
			- 0.055 * (util's sind(mm - f - 2 * D)) ¬
			- 0.046 * (util's sind(mm + f - 2 * D)) ¬
			+ 0.033 * (util's sind(f + 2 * D)) ¬
			+ 0.017 * (util's sind(2 * mm + f)))
	end pert_lat
	
	on pert_lunarDist(mm, D)
		return (-0.58 * (util's cosd(mm - 2 * D)) ¬
			- 0.46 * (util's cosd(2 * D)))
	end pert_lunarDist
	
end script



Hello.

I have cleaned up the code above some, (formatClockTime()). I have also used a different interpolation scheme, that didn’t seem to create greater accuracy, but at least the calculation times for the moon are going twice as fast.

The bug that I can’t explain still pops up.

I am actually coming back with a script for the moon that should be accurate, because this nags me, first I’ll try to add terms to the moon in this script, as I have found the “raw-material”, and if that doesn’t add up, I’ll be back with something accurate and much slower. :slight_smile:

Hello.

I actually found a bug, which in this case is quite rare, since I have worked so much with this.

Well. Now it is 1hour 8 minutes out of sync with the Moon, at the worst day of the year, which is the perihelion, when the Earth and the Sun is closest, and thereby excerts the most gravitational forces on the Moon.

It is not something to base tidal water charts on, but I can live with that, -for now, and maybe readress the issue when I have done the planets. :slight_smile: