equationoftime 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.md +22 -0
- data/LICENSE.txt +22 -0
- data/README.md +80 -0
- data/README2.txt +70 -0
- data/Rakefile +80 -0
- data/doc/GeoLatLng.html +770 -0
- data/doc/_index.html +123 -0
- data/doc/class_list.html +54 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +339 -0
- data/doc/file.README.html +179 -0
- data/doc/file_list.html +56 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +179 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +178 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +611 -0
- data/doc/top-level-namespace.html +112 -0
- data/equationoftime.gemspec +35 -0
- data/examples/Equation_of_Time.jpg +0 -0
- data/examples/analemma_data_generator.rb +53 -0
- data/examples/check_date_type.rb +57 -0
- data/examples/compare_geoc_long_ra.rb +31 -0
- data/examples/data_table.rb +26 -0
- data/examples/earth_rotation.rb +13 -0
- data/examples/eot_methods_list.rb +16 -0
- data/examples/eot_plot.r +57 -0
- data/examples/eot_suntimes.rb +140 -0
- data/examples/equation_of_time.py +186 -0
- data/examples/figure_1.jpg +0 -0
- data/examples/file_converter.rb +31 -0
- data/examples/from_readme.rb +10 -0
- data/examples/geo_locator.rb +12 -0
- data/examples/getjd.rb +45 -0
- data/examples/input_suntimes.rb +21 -0
- data/examples/julian_day_formula.rb +29 -0
- data/examples/julian_day_formula.txt +12 -0
- data/examples/my_time_conversion.rb +21 -0
- data/examples/nutation_series.txt +678 -0
- data/examples/nutation_series.yaml +14239 -0
- data/examples/nutation_table5_3a.txt +682 -0
- data/examples/nutation_table5_3a.yaml +9532 -0
- data/examples/ptime.rb +162 -0
- data/examples/read_nutation_data.rb +399 -0
- data/examples/suntimes.rb +28 -0
- data/examples/suntimes_test.rb +47 -0
- data/examples/test_poly_eval.rb +38 -0
- data/examples/time_scales.rb +29 -0
- data/examples/usage_example.rb +13 -0
- data/examples/use_angles.rb +155 -0
- data/lib/eot/angles.rb +337 -0
- data/lib/eot/constants.rb +168 -0
- data/lib/eot/displays.rb +213 -0
- data/lib/eot/geo_lat_lng_smt.rb +80 -0
- data/lib/eot/init.rb +93 -0
- data/lib/eot/nutation.rb +70 -0
- data/lib/eot/nutation_table5_3a.yaml +9532 -0
- data/lib/eot/times.rb +130 -0
- data/lib/eot/utilities.rb +129 -0
- data/lib/eot/version.rb +6 -0
- data/lib/eot.rb +11 -0
- data/tests/minitest/aliased_angles_spec.rb +287 -0
- data/tests/minitest/aliased_displays_spec.rb +106 -0
- data/tests/minitest/aliased_times_spec.rb +36 -0
- data/tests/minitest/aliased_utilities_spec.rb +49 -0
- data/tests/minitest/angles_spec.rb +313 -0
- data/tests/minitest/constants_spec.rb +27 -0
- data/tests/minitest/delta_epsilon_spec.rb +35 -0
- data/tests/minitest/displays_spec.rb +111 -0
- data/tests/minitest/geo_spec.rb +36 -0
- data/tests/minitest/init_spec.rb +32 -0
- data/tests/minitest/nutation_spec.rb +33 -0
- data/tests/minitest/times_spec.rb +137 -0
- data/tests/minitest/utilities_spec.rb +121 -0
- data/tests/spec_config.rb +3 -0
- data/wiki.md +46 -0
- data/wiki2.md +4 -0
- metadata +240 -0
@@ -0,0 +1,186 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
"""
|
3
|
+
https://bitbucket.org/cmcqueen1975/sundials/src/26a0f54a7c18?at=default
|
4
|
+
Calculation of "equation of time".
|
5
|
+
|
6
|
+
References:
|
7
|
+
http://en.wikipedia.org/wiki/Equation_of_time
|
8
|
+
http://www.sundials.co.uk/equation.htm
|
9
|
+
|
10
|
+
Calculations have been done according to the Wikipedia reference.
|
11
|
+
|
12
|
+
Dependencies:
|
13
|
+
- Python 2.x
|
14
|
+
- NumPy
|
15
|
+
- SciPy (only strictly needed for the more accurate calculation)
|
16
|
+
- matplotlib to plot the graph
|
17
|
+
"""
|
18
|
+
|
19
|
+
import datetime
|
20
|
+
from collections import namedtuple
|
21
|
+
|
22
|
+
import numpy as np
|
23
|
+
import scipy # only strictly needed for the more accurate calculation in equation_of_time_accurate()
|
24
|
+
import scipy.optimize
|
25
|
+
|
26
|
+
# Named tuple to hold geographic location
|
27
|
+
Location = namedtuple('Location', 'latitude, longitude')
|
28
|
+
|
29
|
+
|
30
|
+
# If a location is given, a longitude correction is calculated and included in the graph.
|
31
|
+
# If the sundial itself includes the longitude correction, just use the 0 value here.
|
32
|
+
LOCATION = Location(0, 0)
|
33
|
+
#LOCATION = Location(51.3809, -2.3603) # Bath, England
|
34
|
+
#LOCATION = Location(35.10, 138.86) # Numazu, Japan
|
35
|
+
#LOCATION = Location(-37.81, 144.96) # Melbourne, Victoria, Australia
|
36
|
+
|
37
|
+
|
38
|
+
DAYS_PER_TROPICAL_YEAR = 365.242
|
39
|
+
SUN_ECCENTRICITY = 0.01671
|
40
|
+
|
41
|
+
# The angle from the vernal equinox to the periapsis in the plane of the ecliptic.
|
42
|
+
SUN_ANGLE_OFFSET = 4.9358
|
43
|
+
|
44
|
+
# Angle of tilt of earth's axis--about 23.44 degrees
|
45
|
+
SUN_OBLIQUITY = 0.40910
|
46
|
+
|
47
|
+
|
48
|
+
# Date range for drawing a graph.
|
49
|
+
DATE_START = datetime.date(2009, 1, 1)
|
50
|
+
DATE_END = datetime.date(2010, 1, 1)
|
51
|
+
# Periapsis occurs on a slightly different date each year--varying by a couple
|
52
|
+
# of days. 4th of January is about the average.
|
53
|
+
DATE_PERIAPSIS = datetime.date(2009, 1, 4)
|
54
|
+
|
55
|
+
|
56
|
+
def longitude_offset(location):
|
57
|
+
"""Given a location, return the offset due to longitude, in degrees
|
58
|
+
Location's longitude is used. Latitude isn't needed.
|
59
|
+
"""
|
60
|
+
longitude = location.longitude
|
61
|
+
longitude_per_hour = (360. / 24)
|
62
|
+
longitude_offset = longitude % longitude_per_hour
|
63
|
+
if longitude_offset > longitude_per_hour / 2:
|
64
|
+
longitude_offset -= longitude_per_hour
|
65
|
+
return longitude_offset
|
66
|
+
|
67
|
+
|
68
|
+
def longitude_offset_min(location):
|
69
|
+
minute_per_longitude = 24 * 60 / 360.
|
70
|
+
return longitude_offset(location) * minute_per_longitude
|
71
|
+
|
72
|
+
|
73
|
+
def mean_anomaly(day_number_n):
|
74
|
+
"""day_number_n is the number of days from periapsis."""
|
75
|
+
return day_number_n * (2 * np.pi / DAYS_PER_TROPICAL_YEAR)
|
76
|
+
|
77
|
+
|
78
|
+
@np.vectorize
|
79
|
+
def eccentric_anomaly(mean_anomaly_value):
|
80
|
+
local_sun_eccentricity = SUN_ECCENTRICITY
|
81
|
+
|
82
|
+
def eccentric_anomaly_function(eccentric_anomaly_value):
|
83
|
+
return eccentric_anomaly_value - local_sun_eccentricity * np.sin(eccentric_anomaly_value) - mean_anomaly_value
|
84
|
+
|
85
|
+
# eccentric_anomaly_value = scipy.optimize.brentq(eccentric_anomaly_function, 0 - 0.0001, 2 * np.pi + 0.0001)
|
86
|
+
eccentric_anomaly_value = scipy.optimize.fsolve(eccentric_anomaly_function, mean_anomaly_value)
|
87
|
+
return eccentric_anomaly_value
|
88
|
+
|
89
|
+
|
90
|
+
def true_anomaly(eccentric_anomaly_value):
|
91
|
+
local_sun_eccentricity = SUN_ECCENTRICITY
|
92
|
+
|
93
|
+
half_eccentric_anomaly = eccentric_anomaly_value / 2
|
94
|
+
a_x = np.cos(half_eccentric_anomaly)
|
95
|
+
a_y = np.sin(half_eccentric_anomaly)
|
96
|
+
a_y *= np.sqrt((1 + local_sun_eccentricity) / (1 - local_sun_eccentricity))
|
97
|
+
return 2 * np.arctan2(a_y, a_x)
|
98
|
+
|
99
|
+
|
100
|
+
def right_ascension(sun_angle):
|
101
|
+
"""sun_angle is the angle from the vernal equinox to the Sun in the plane of the ecliptic.
|
102
|
+
It is the true_anomaly value plus the SUN_ANGLE_OFFSET."""
|
103
|
+
a_x = np.cos(sun_angle)
|
104
|
+
a_y = np.sin(sun_angle)
|
105
|
+
return np.arctan2(a_y * np.cos(SUN_OBLIQUITY), a_x)
|
106
|
+
|
107
|
+
|
108
|
+
def equation_of_time_accurate(day_number_n):
|
109
|
+
"""Calculate the equation of time (in min), given a day number.
|
110
|
+
|
111
|
+
day_number_n is the number of days from periapsis.
|
112
|
+
Returns the difference between solar time and clock time, in minutes.
|
113
|
+
This uses a more accurate calculation.
|
114
|
+
"""
|
115
|
+
mean_anomaly_value = mean_anomaly(day_number_n)
|
116
|
+
eccentric_anomaly_value = eccentric_anomaly(mean_anomaly_value)
|
117
|
+
true_anomaly_value = true_anomaly(eccentric_anomaly_value)
|
118
|
+
right_ascension_value = right_ascension(true_anomaly_value + SUN_ANGLE_OFFSET)
|
119
|
+
eot = mean_anomaly_value + SUN_ANGLE_OFFSET - right_ascension_value
|
120
|
+
# Get the angles into the range we want--that is, -pi to +pi
|
121
|
+
eot = (eot + np.pi) % (2 * np.pi) - np.pi
|
122
|
+
return eot * (24 * 60 / 2 / np.pi)
|
123
|
+
|
124
|
+
|
125
|
+
def equation_of_time_simple(day_number_n):
|
126
|
+
"""Calculate the equation of time (in min), given a day number.
|
127
|
+
|
128
|
+
day_number_n is the number of days from periapsis.
|
129
|
+
Returns the difference between solar time and clock time, in minutes.
|
130
|
+
This uses a simple, approximate calculation.
|
131
|
+
"""
|
132
|
+
mean_anomaly_value = mean_anomaly(day_number_n)
|
133
|
+
return -7.655 * np.sin(mean_anomaly_value) + 9.873 * np.sin(2 * mean_anomaly_value + 3.588)
|
134
|
+
|
135
|
+
|
136
|
+
#equation_of_time = equation_of_time_simple
|
137
|
+
equation_of_time = equation_of_time_accurate
|
138
|
+
|
139
|
+
|
140
|
+
def main():
|
141
|
+
import matplotlib
|
142
|
+
#matplotlib.use('pdf')
|
143
|
+
#matplotlib.use('svg')
|
144
|
+
from matplotlib import pyplot as plt
|
145
|
+
|
146
|
+
|
147
|
+
date_range = np.arange(matplotlib.dates.date2num(DATE_START), matplotlib.dates.date2num(DATE_END), 0.1)
|
148
|
+
day_numbers = date_range - matplotlib.dates.date2num(DATE_PERIAPSIS)
|
149
|
+
|
150
|
+
# Calculate the accurate and simple calculations of equation of time.
|
151
|
+
solar_offset_accurate_min = equation_of_time_accurate(day_numbers) + longitude_offset_min(LOCATION)
|
152
|
+
solar_offset_simple_min = equation_of_time_simple(day_numbers) + longitude_offset_min(LOCATION)
|
153
|
+
|
154
|
+
# Plot the graph, either solar vs clock, or vice-versa.
|
155
|
+
if 1:
|
156
|
+
# Solar time vs clock time
|
157
|
+
plt.plot_date(date_range, solar_offset_accurate_min, '-')
|
158
|
+
# plt.plot_date(date_range, solar_offset_simple_min, '--')
|
159
|
+
plt.ylabel('solar time - clock time (min)')
|
160
|
+
else:
|
161
|
+
# Clock time vs solar time
|
162
|
+
plt.plot_date(date_range, -solar_offset_accurate_min, '-')
|
163
|
+
# plt.plot_date(date_range, -solar_offset_simple_min, '--')
|
164
|
+
plt.ylabel('clock time - solar time (min)')
|
165
|
+
|
166
|
+
# Set month lines
|
167
|
+
ax = plt.subplot(111)
|
168
|
+
ax.xaxis.set_major_locator(matplotlib.dates.MonthLocator())
|
169
|
+
ax.xaxis.set_major_formatter(matplotlib.ticker.NullFormatter())
|
170
|
+
# Set month labels centred in the middle (actually on day 15) of each month.
|
171
|
+
ax.xaxis.set_minor_locator(matplotlib.dates.MonthLocator(bymonthday=15))
|
172
|
+
ax.xaxis.set_minor_formatter(matplotlib.dates.DateFormatter('%b'))
|
173
|
+
for tick in ax.xaxis.get_minor_ticks():
|
174
|
+
tick.tick1line.set_markersize(0)
|
175
|
+
tick.tick2line.set_markersize(0)
|
176
|
+
|
177
|
+
plt.grid(True)
|
178
|
+
|
179
|
+
plt.show()
|
180
|
+
# plt.savefig('equation_of_time.pdf')
|
181
|
+
# plt.savefig('equation_of_time.svg')
|
182
|
+
# plt.savefig('equation_of_time.png')
|
183
|
+
|
184
|
+
|
185
|
+
if __name__ == '__main__':
|
186
|
+
main()
|
Binary file
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# the file this data comes from 'Circular_179.pdf' IAU 2000A Nutation Series
|
2
|
+
# the first 678 lines are for lunisolar data
|
3
|
+
|
4
|
+
require 'safe_yaml'
|
5
|
+
|
6
|
+
# make array elements from each line of the file
|
7
|
+
filename = "nutation_table5_3a.txt"
|
8
|
+
temp_array = []
|
9
|
+
data = File.readlines(filename)
|
10
|
+
|
11
|
+
# clean out whitespace and new line breaks
|
12
|
+
data.each {|i| temp_array << i.strip}
|
13
|
+
|
14
|
+
# make new muti-dimensional data array
|
15
|
+
data = []
|
16
|
+
temp_array.each {|i| data << i.split}
|
17
|
+
|
18
|
+
# save the array in a yaml file
|
19
|
+
File::open( "nutation_table5_3a.yaml", "w" ) do |f|
|
20
|
+
YAML.dump( data, f )
|
21
|
+
end
|
22
|
+
|
23
|
+
# show the new data file contents as an array.
|
24
|
+
data = []
|
25
|
+
File.open( "nutation_table5_3a.yaml" ) do |f|
|
26
|
+
YAML.load_documents( f ) do |doc|
|
27
|
+
data = doc
|
28
|
+
p data
|
29
|
+
end
|
30
|
+
p f
|
31
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# geo_locator.rb
|
2
|
+
|
3
|
+
lib = File.expand_path('../../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'eot'
|
7
|
+
|
8
|
+
geo = GeoLatLng.new
|
9
|
+
geo.addr = "8000 South Michigan Ave., Chicago, IL"
|
10
|
+
geo.get_coordinates_from_address
|
11
|
+
p geo.lat
|
12
|
+
p geo.lng
|
data/examples/getjd.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
include Math
|
5
|
+
|
6
|
+
def calc_time_julian_centurey(t)
|
7
|
+
# Julian Day Number j(2000) subtracted
|
8
|
+
(t - 2451545.0) / 36525.0
|
9
|
+
# Time in fractional centurey
|
10
|
+
end
|
11
|
+
|
12
|
+
# Truncate large angles
|
13
|
+
def mod_360(x)
|
14
|
+
360.0 * ( x / 360.0 - Integer( x / 360.0))
|
15
|
+
end
|
16
|
+
|
17
|
+
def calc_mean_long_aries(t)
|
18
|
+
|
19
|
+
mod_360(280.46061666 + t * 36525.0 * 360.98564736629 + t * (t * 0.000387933 - t * (t / 38710000.0)))
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
puts "outputs data every 5 seconds"
|
24
|
+
loop do
|
25
|
+
time = Time.now.utc
|
26
|
+
theDate = Date.new(time.year, time.month, time.day)
|
27
|
+
# puts time
|
28
|
+
# puts theDate
|
29
|
+
# puts "#{theDate.ajd} = Astronomical Julian Day"
|
30
|
+
# puts "#{theDate.jd - 0.5} = Astronomical Julian Day"
|
31
|
+
# puts "#{theDate.jd} = Julian Day"
|
32
|
+
t = time
|
33
|
+
theDayFraction = (t.usec / (1000000.0 * 3600.0) + t.min / 60.0 + t.hour + t.sec / 3600.0) / 24.0
|
34
|
+
# puts "#{theDayFraction} = Day Fraction time now"
|
35
|
+
|
36
|
+
theTotal = theDate.ajd + theDayFraction
|
37
|
+
# puts "#{theTotal} = Astronomical Julian Day + Day Fraction time now"
|
38
|
+
|
39
|
+
tjc = calc_time_julian_centurey(theTotal)
|
40
|
+
gmst = calc_mean_long_aries(tjc)
|
41
|
+
|
42
|
+
puts "#{gmst.round 3} = (GHA) Mean Hour Angle First Point of Aries (Vernal Equinox)"
|
43
|
+
puts "#{(gmst/15.0).round 3} = Mean Greenwich Siderial Time (GMST)"
|
44
|
+
sleep 5
|
45
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# input_suntimes.rb
|
2
|
+
|
3
|
+
lib = File.expand_path('../../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'eot'
|
7
|
+
|
8
|
+
eot = Eot.new
|
9
|
+
|
10
|
+
@date = Date.today.to_s
|
11
|
+
@zone = -5
|
12
|
+
puts @date
|
13
|
+
eot.date = @date
|
14
|
+
geo = GeoLatLng.new
|
15
|
+
# note: you will need internet access to get the coordinates next
|
16
|
+
geo.addr = "8000 South Michigan Ave., Chicago, IL"
|
17
|
+
geo.get_coordinates_from_address
|
18
|
+
eot.longitude = geo.lng.to_f
|
19
|
+
eot.latitude = geo.lat.to_f
|
20
|
+
puts "Sunrise #{eot.sunrise_dt().to_time}"
|
21
|
+
puts "Sunset #{eot.sunset_dt().to_time}"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# julian_day_formula.rb
|
2
|
+
|
3
|
+
# 3) Dropping the fractional part of all results of all multiplications and divisions, let
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
def date_to_ajd date
|
7
|
+
year = date.year
|
8
|
+
month = date.month
|
9
|
+
|
10
|
+
if month <= 2
|
11
|
+
year = year -1
|
12
|
+
month = month +12
|
13
|
+
end
|
14
|
+
|
15
|
+
day = date.day
|
16
|
+
|
17
|
+
a = (year / 100).floor
|
18
|
+
b = (a / 4).floor
|
19
|
+
c = 2 - a + b
|
20
|
+
e = (365.25 * (year + 4716)).floor
|
21
|
+
f = (30.6001 * (month +1)).floor
|
22
|
+
c + day + e + f - 1524.5
|
23
|
+
end
|
24
|
+
|
25
|
+
t = Time.now.utc
|
26
|
+
date_string = "#{t.year}-#{t.month}-#{t.day}"
|
27
|
+
date = Date.parse(date_string)
|
28
|
+
puts t
|
29
|
+
puts "Todays date is #{date} and the AJD is #{date_to_ajd date}"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# my_time_conversion.rb
|
2
|
+
|
3
|
+
# This method will convert hours decimal into a Time object and let you see H:M:S format
|
4
|
+
def decimal_to_strf(time_in)
|
5
|
+
t = Time.now
|
6
|
+
year = t.year
|
7
|
+
month = t.month
|
8
|
+
day = t.day
|
9
|
+
hours = Integer(time_in)
|
10
|
+
decimal_minutes = (time_in - hours) * 60.0
|
11
|
+
minutes = Integer(decimal_minutes)
|
12
|
+
decimal_seconds = (decimal_minutes - minutes) * 60.0
|
13
|
+
seconds = Integer(decimal_seconds)
|
14
|
+
my_time = Time.new(year, month, day, hours, minutes, seconds, "-05:00")
|
15
|
+
my_time.strftime("%H:%M:%S")
|
16
|
+
end
|
17
|
+
|
18
|
+
decimal_hours = 4.64
|
19
|
+
puts
|
20
|
+
puts "#{decimal_hours} = #{decimal_to_strf(decimal_hours)}"
|
21
|
+
puts
|