equationoftime 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|