astro-algo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,23 @@
1
+ = Astronomical Algorithms
2
+
3
+ This library implements algorithms from Jean Meeus, <i>Astronomical Algorithms</i>,
4
+ 2nd English Edition, Willmann-Bell, Inc., Richmond, Virginia, 1999, with corrections
5
+ as of June 15, 2005.
6
+
7
+ Gem astro-algo provides two modules:
8
+
9
+ * Astro which implements algorithms from the book. (require 'astro-algo')
10
+ * LunarYear which implements helper routines for calculating lunar years. (require 'lunaryear')
11
+
12
+ This gem also comes with several command line scripts.
13
+ equinox:: prints the date and time of the vernal equinox for the given year.
14
+ lunarcalendar:: prints a conversion chart between the customary Gregarian
15
+ calendar and a lunar year beginning with the full moon on or before
16
+ the vernal equinox.
17
+ moon_clock.rb:: a Tk GUI application which displays information about the current
18
+ lunation.
19
+ moons:: prints the phases of the moon for the given year.
20
+
21
+ This is a work in progress. Only the algorithms from <i>Astronomical Algorithms</i>
22
+ needed to support my hobby in the LunarYear have been implemented. I will add more
23
+ algorithms from the book in time.
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # equinox.rb
3
+
4
+ # Compute the date of the vernal equinox for a given year (default current year).
5
+ #
6
+ # Example: compute the date and time of the vernal equinox for 2008.
7
+ # $ equinox 2008 => Thu Mar 20 05:48:18 2008
8
+
9
+ require File.join(File.dirname(__FILE__), '../lib/astro-algo')
10
+
11
+
12
+ if ARGV.length == 0
13
+ year = Time.now.year
14
+ else
15
+ year = ARGV[0].to_i
16
+ end
17
+
18
+ puts Astro.date_of_vernal_equinox(year).to_utc.asctime
19
+
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # lunarcalendar.rb
3
+
4
+ # Print a lunar calendar for the given year (default current year).
5
+
6
+ require File.join(File.dirname(__FILE__), '../lib/lunaryear')
7
+
8
+
9
+
10
+ if ARGV.length == 0
11
+ year = Time.now.year
12
+ else
13
+ year = ARGV[0].to_i
14
+ end
15
+
16
+
17
+
18
+ lunation = LunarYear.new_moon_before_vernal_equinox(year)
19
+ next_years_lunation = LunarYear.new_moon_before_vernal_equinox(year + 1)
20
+ vernal_equinox = Astro.date_of_vernal_equinox(year).to_utc
21
+ vemonth = vernal_equinox.month
22
+ veday = vernal_equinox.day
23
+
24
+ moonth = 0
25
+ while lunation < next_years_lunation
26
+ moon_date = Astro.date_of_moon(lunation, Astro::PhaseNew).to_utc
27
+ next_moon = Astro.date_of_moon(lunation + 1, Astro::PhaseNew).to_utc
28
+ moon_day = 0
29
+ until moon_date.month == next_moon.month && moon_date.day == next_moon.day
30
+ ve = vemonth == moon_date.month && veday == moon_date.day ? '*' : ' '
31
+ puts '%2d-%02d %s%s' % [moonth, moon_day, moon_date.strftime('%b %d'), ve]
32
+ moon_day += 1
33
+ moon_date += 1
34
+ end
35
+ puts '--------------'
36
+ lunation += 1
37
+ moonth += 1
38
+ end
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env ruby
2
+ # moon_clock.rb
3
+ #
4
+ # A clock application which displays the current local time,
5
+ # Universal time, time since the previous new and full moons,
6
+ # and time until the next new and full moons.
7
+ #
8
+ # Copyright (c) 2007 John Powers
9
+
10
+ require 'tk'
11
+ require File.join(File.dirname(__FILE__), '../lib/lunaryear')
12
+
13
+
14
+ DateTimeFormat = '%Y-%m-%d %H:%M:%S'
15
+
16
+
17
+ # Format number of days.
18
+ # +days+ is a rational number of days.
19
+ # Return days formatted like 28d 14:59:11
20
+ def format_days(days)
21
+ d, r = days.divmod(1)
22
+ h, r = (24*r).divmod(1)
23
+ m, r = (60*r).divmod(1)
24
+ s = (60*r).floor
25
+ '%dd %d:%02d:%02d' % [d, h, m, s] # 28d 14:59:11
26
+ end
27
+
28
+
29
+ # Set local and universal clocks to current time.
30
+ def set_clocks
31
+ now = DateTime.now
32
+ $current_time.value = now.strftime(DateTimeFormat)
33
+ $universal_time.value = now.new_offset.strftime(DateTimeFormat)
34
+ $lunar_date.value = '%4d\'%02d\'%02d' % LunarYear.lunar_date(now.to_date)
35
+ now
36
+ end
37
+
38
+
39
+ # Update moon times
40
+ $tnew0 = DateTime.jd(1)
41
+ $tnew1 = DateTime.jd(1)
42
+ $tfull0 = DateTime.jd(1)
43
+ $tfull1 = DateTime.jd(1)
44
+
45
+
46
+ # Set all moon clocks relative to the current time -- the
47
+ # number of days since the last moon or until the next moon.
48
+ def set_moon_clocks
49
+ now = set_clocks
50
+
51
+ # Has new or full moon expired?
52
+ if now > $tnew1 || now > $tfull1
53
+ $tnew0, $tnew1, $tfull0, $tfull1 = LunarYear.date_of_moons(now)
54
+ end
55
+
56
+ $last_new_moon.value = format_days(now - $tnew0)
57
+ $next_new_moon.value = format_days($tnew1 - now)
58
+ $last_full_moon.value = format_days(now - $tfull0)
59
+ $next_full_moon.value = format_days($tfull1 - now)
60
+ end
61
+
62
+
63
+ # Periodically update moon clocks. Reschedules itself to run
64
+ # again in 1000 milliseconds.
65
+ def update
66
+ set_moon_clocks
67
+ Tk.after(1000) {update}
68
+ end
69
+
70
+
71
+
72
+
73
+ $root = TkRoot.new { title 'Moon Clock' }
74
+ top = TkFrame.new($root)
75
+
76
+ # Local time
77
+ $current_time = TkVariable.new
78
+ TkLabel.new(top) {
79
+ font 'Courier 48 bold'
80
+ textvariable $current_time
81
+ grid(:row => 0, :column => 0, :columnspan => 2, :sticky => 'ew')
82
+ }
83
+ TkLabel.new(top) {
84
+ font 'Palatino 18 italic'
85
+ text 'Local Time'
86
+ grid(:row => 1, :column => 0, :columnspan => 2, :sticky => 'ew')
87
+ }
88
+
89
+
90
+ # Universal time
91
+ $universal_time = TkVariable.new
92
+ TkLabel.new(top) {
93
+ font 'Courier 36 bold'
94
+ textvariable $universal_time
95
+ grid(:row => 2, :column => 0, :columnspan => 2, :sticky => 'ew')
96
+ }
97
+ TkLabel.new(top) {
98
+ font 'Palatino 18 italic'
99
+ text 'Universal Time'
100
+ grid(:row => 3, :column => 0, :columnspan => 2, :sticky => 'ew')
101
+ }
102
+
103
+
104
+ # Lunar date
105
+ $lunar_date = TkVariable.new
106
+ TkLabel.new(top) {
107
+ font 'Courier 36 bold'
108
+ textvariable $lunar_date
109
+ grid(:row => 4, :column => 0, :columnspan => 2, :sticky => 'ew')
110
+ }
111
+ TkLabel.new(top) {
112
+ font 'Palatino 18 italic'
113
+ text 'Lunar Date'
114
+ grid(:row => 5, :column => 0, :columnspan => 2, :sticky => 'ew')
115
+ }
116
+
117
+
118
+ # Time since last new moon
119
+ $last_new_moon = TkVariable.new
120
+ TkLabel.new(top) {
121
+ font 'Courier 36 bold'
122
+ textvariable $last_new_moon
123
+ grid(:row => 6, :column => 0, :sticky => 'e')
124
+ }
125
+ TkLabel.new(top) {
126
+ font 'Palatino 18 italic'
127
+ text 'Last New Moon'
128
+ grid(:row => 7, :column => 0)
129
+ }
130
+
131
+ # Time until next new moon
132
+ $next_new_moon = TkVariable.new
133
+ TkLabel.new(top) {
134
+ font 'Courier 36 bold'
135
+ textvariable $next_new_moon
136
+ grid(:row => 6, :column => 1, :sticky => 'e')
137
+ }
138
+ TkLabel.new(top) {
139
+ font 'Palatino 18 italic'
140
+ text 'Next New Moon'
141
+ grid(:row => 7, :column => 1)
142
+ }
143
+
144
+ # Time since last full moon
145
+ $last_full_moon = TkVariable.new
146
+ TkLabel.new(top) {
147
+ font 'Courier 36 bold'
148
+ textvariable $last_full_moon
149
+ grid(:row => 8, :column => 0, :sticky => 'e')
150
+ }
151
+ TkLabel.new(top) {
152
+ font 'Palatino 18 italic'
153
+ text 'Last Full Moon'
154
+ grid(:row => 9, :column => 0)
155
+ }
156
+
157
+ # Time until next full moon
158
+ $next_full_moon = TkVariable.new
159
+ TkLabel.new(top) {
160
+ font 'Courier 36 bold'
161
+ textvariable $next_full_moon
162
+ grid(:row => 8, :column => 1, :sticky => 'e')
163
+ }
164
+ TkLabel.new(top) {
165
+ font 'Palatino 18 italic'
166
+ text 'Next Full Moon'
167
+ grid(:row => 9, :column => 1)
168
+ }
169
+
170
+
171
+ TkButton.new(top) {
172
+ text ' Quit '
173
+ command {exit}
174
+ grid(:row => 10, :column => 1)
175
+ }
176
+
177
+
178
+ top.pack(:fill => 'both')
179
+
180
+
181
+ # Start clock display
182
+ update
183
+
184
+ Tk.mainloop
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # moons
3
+
4
+ # Print a list of dates and times for phases of the moon for given
5
+ # year (default current year).
6
+
7
+ require File.join(File.dirname(__FILE__), '../lib/astro-algo')
8
+
9
+
10
+
11
+ if ARGV.length == 0
12
+ year = DateTime.now.year
13
+ else
14
+ year = ARGV[0].to_i
15
+ end
16
+
17
+ DateFormat = '%Y-%m-%d %H:%M:%S '
18
+
19
+ #puts ' ' + date.year.to_s
20
+ lunation = Astro.first_lunation_of_year(year)
21
+ next_year = DateTime.new(year + 1, 1, 1)
22
+
23
+ loop do
24
+ new_moon = Astro.date_of_moon(lunation, Astro::PhaseNew).to_utc
25
+ break if new_moon >= next_year
26
+ print new_moon.strftime(DateFormat)
27
+ print Astro.date_of_moon(lunation, Astro::PhaseFirstQuarter).to_utc.strftime(DateFormat)
28
+ print Astro.date_of_moon(lunation, Astro::PhaseFull).to_utc.strftime(DateFormat)
29
+ puts Astro.date_of_moon(lunation, Astro::PhaseLastQuarter).to_utc.strftime(DateFormat)
30
+ lunation += 1
31
+ end
@@ -0,0 +1,846 @@
1
+ # astro.rb
2
+ # Implementation of astronomical calculations from
3
+ # Jean Meuss, <i>Astronomical Algorithms</i>, 2nd English Edition,
4
+ # Willmann-Bell, Inc., Richmond, Virginia, 1999, with corrections as of June 15, 2005.
5
+
6
+
7
+ require 'date'
8
+
9
+ # Augment several classes with new methods.
10
+
11
+ class Numeric
12
+
13
+ # Convert from degrees to radians.
14
+ def to_rad
15
+ self * Math::PI / 180.0
16
+ end
17
+
18
+ # Convert from radians to degrees.
19
+ def to_deg
20
+ self * 180.0 / Math::PI
21
+ end
22
+
23
+ end
24
+
25
+
26
+ class Float
27
+
28
+ # Convert Float to Rational.
29
+ # Algorithm from Dave Burt: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/142199
30
+ def to_r
31
+ return Rational(0, 1) if self == 0.0
32
+ x = self
33
+ negative = false
34
+ if x < 0.0
35
+ x = -x
36
+ negative = true
37
+ end
38
+ f, e = Math.frexp(x)
39
+ # raise unless 0.5 <= f and f < 1.0
40
+ # x = f * 2**e exactly
41
+
42
+ # Suck up _chunk_ bits at a time; 28 is enough so that we suck
43
+ # up all bits in 2 iterations for all known binary double-
44
+ # precision formats, and small enough to fit in an int.
45
+ chunk = 28
46
+ top = 0
47
+ # invariant: x = (top + f) * 2**e exactly
48
+ while f > 0.0
49
+ f = Math.ldexp(f, chunk)
50
+ digit = f.to_i
51
+ raise unless digit >> chunk == 0
52
+ top = (top << chunk) | digit
53
+ f -= digit
54
+ # raise unless 0.0 <= f and f < 1.0
55
+ e -= chunk
56
+ end
57
+ # raise if top == 0
58
+
59
+ # now x = top * 2**e exactly; fold in 2**e
60
+ r = Rational(top, 1)
61
+ if e > 0
62
+ r *= 2**e
63
+ else
64
+ r /= 2**-e
65
+ end
66
+ negative ? -r : r
67
+ end
68
+ end
69
+
70
+
71
+ class Array
72
+
73
+ # Evaluate polynomial using Horner's method.
74
+ # Array consists of coefficients of a polynomial, the
75
+ # coefficient of the highest order term first, the
76
+ # constant coefficient last.
77
+ # Returns evaluation of polynomial at +x+.
78
+ #
79
+ # Example: evaluate the polynomial x**2 - 0.5*x + 3.0 where x = 2.0
80
+ # [1.0, -0.5, 3.0].poly_eval(2.0) # => 6.0
81
+ def poly_eval(x)
82
+ self.inject(0.0) {|p, a| p*x + a}
83
+ end
84
+
85
+ end
86
+
87
+
88
+ class DateTime
89
+
90
+ # Adjust dynamical time to UTC.
91
+ # Returns rational number of seconds.
92
+ def to_utc
93
+ self - Astro.delta_T(self).to_r / 86400 # convert from seconds to days
94
+ end
95
+
96
+ # Truncate date/time to date.
97
+ # Returns a Date object for the given DateTime.
98
+ def to_date
99
+ Date.new(year, month, day)
100
+ end
101
+
102
+ end
103
+
104
+
105
+ module Astro
106
+
107
+ VERSION = '0.0.1'
108
+
109
+ # Compute difference between dynamical time and UTC in seconds.
110
+ # See http://sunearth.gsfc.nasa.gov/eclipse/SEcat5/deltatpoly.html.
111
+ # Good from -1999 to +3000.
112
+ #
113
+ # Example: compute the difference between dynamical time and UTC for January 1, 2007.
114
+ # Astro.delta_T(DateTime.new(2007, 1, 1)) # => 65.465744703125
115
+ def Astro.delta_T(date)
116
+
117
+ year = date.year.to_f
118
+ y = year + (date.month.to_f - 0.5) / 12.0
119
+
120
+ case
121
+ when year < -500.0
122
+ u = (year - 1820.0) / 100.0
123
+ -20.0 + 32.0*u*u
124
+ when year < 500.0
125
+ u = y / 100.0
126
+ [0.0090316521, 0.022174192, -0.1798452, -5.952053, 33.78311, -1014.41, 10583.6].poly_eval(u)
127
+ when year < 1600.0
128
+ u = (y - 1000.0) / 100.0
129
+ [0.0083572073, -0.005050998, -0.8503463, 0.319781, 71.23472, -556.01, 1574.2].poly_eval(u)
130
+ when year < 1700.0
131
+ t = y - 1600.0
132
+ [1.0/7129.0, -0.01532, -0.9808, 120.0].poly_eval(t)
133
+ when year < 1800.0
134
+ t = y - 1700.0
135
+ [-1.0/1174000.0, 0.00013336, -0.0059285, 0.1603, 8.83].poly_eval(t)
136
+ when year < 1860.0
137
+ t = y - 1800.0
138
+ [0.000000000875, -0.0000001699, 0.0000121272, -0.00037436, 0.0041116, 0.0068612, -0.332447, 13.72].poly_eval(t)
139
+ when year < 1900.0
140
+ t = y - 1860.0
141
+ [1.0/233174.0, -0.0004473624, 0.01680668, -0.251754, 0.5737, 7.62].poly_eval(t)
142
+ when year < 1920.0
143
+ t = y - 1900.0
144
+ [-0.000197, 0.0061966, -0.0598939, 1.494119, -2.79].poly_eval(t)
145
+ when year < 1941.0
146
+ t = y - 1920.0
147
+ [0.0020936, -0.076100, 0.84493, 21.20].poly_eval(t)
148
+ when year < 1961.0
149
+ t = y - 1950.0
150
+ [1.0/2547.0, -1.0/233.0, 0.407, 29.07].poly_eval(t)
151
+ when year < 1986.0
152
+ t = y - 1975.0
153
+ [-1.0/718.0, -1.0/260.0, 1.067, 45.45].poly_eval(t)
154
+ when year < 2005.0
155
+ t = y - 2000.0
156
+ [0.00002373599, 0.000651814, 0.0017275, -0.060374, 0.3345, 63.86].poly_eval(t)
157
+ when year < 2050.0
158
+ t = y - 2000.0
159
+ [0.005589, 0.32217, 62.92].poly_eval(t)
160
+ when year < 2150.0
161
+ -20.0 + 32.0*((y - 1820.0)/100.0)**2 - 0.5628*(2150.0 - y)
162
+ else
163
+ u = (year - 1820.0) / 100.0
164
+ -20.0 + 32*u*u
165
+ end
166
+ end
167
+
168
+ # =Phases of the Moon
169
+
170
+ # :stopdoc:
171
+ # New and Old Moon
172
+ Table_49B = [ # page 351
173
+ # new moon full moon E F M M' O
174
+ [ 0.00002, 0.00002, 0, 0.0, 0.0, 4.0, 0.0],
175
+ [-0.00002, -0.00002, 0, 0.0, 1.0, 3.0, 0.0],
176
+ [-0.00002, -0.00002, 0, -2.0, -1.0, 1.0, 0.0],
177
+ [ 0.00003, 0.00003, 0, 2.0, -1.0, 1.0, 0.0],
178
+ [-0.00003, -0.00003, 0, 2.0, 1.0, 1.0, 0.0],
179
+ [ 0.00003, 0.00003, 0, 2.0, 0.0, 2.0, 0.0],
180
+ [ 0.00003, 0.00003, 0, -2.0, 1.0, 1.0, 0.0],
181
+ [ 0.00004, 0.00004, 0, 0.0, 3.0, 0.0, 0.0],
182
+ [ 0.00004, 0.00004, 0, -2.0, 0.0, 2.0, 0.0],
183
+ [-0.00007, -0.00007, 0, 0.0, 2.0, 1.0, 0.0],
184
+ [-0.00017, -0.00017, 0, 0.0, 0.0, 0.0, 1.0],
185
+ [-0.00024, -0.00024, 1, 0.0, -1.0, 2.0, 0.0],
186
+ [ 0.00038, 0.00038, 1, -2.0, 1.0, 0.0, 0.0],
187
+ [ 0.00042, 0.00042, 1, 2.0, 1.0, 0.0, 0.0],
188
+ [-0.00042, -0.00042, 0, 0.0, 0.0, 3.0, 0.0],
189
+ [ 0.00056, 0.00056, 1, 0.0, 1.0, 2.0, 0.0],
190
+ [-0.00057, -0.00057, 0, 2.0, 0.0, 1.0, 0.0],
191
+ [-0.00111, -0.00111, 0, -2.0, 0.0, 1.0, 0.0],
192
+ [ 0.00208, 0.00209, 2, 0.0, 2.0, 0.0, 0.0],
193
+ [-0.00514, -0.00515, 1, 0.0, 1.0, 1.0, 0.0],
194
+ [ 0.00739, 0.00734, 1, 0.0, -1.0, 1.0, 0.0],
195
+ [ 0.01039, 0.01043, 0, 2.0, 0.0, 0.0, 0.0],
196
+ [ 0.01608, 0.01614, 0, 0.0, 0.0, 2.0, 0.0],
197
+ [ 0.17241, 0.17302, 1, 0.0, 1.0, 0.0, 0.0],
198
+ [-0.40720, -0.40614, 0, 0.0, 0.0, 1.0, 0.0]
199
+ ]
200
+
201
+ # Planetary arguments
202
+ Table_49A = [ # page 351
203
+ # k T^2
204
+ [299.77, 0.107408, 0.000325, -0.009173],
205
+ [251.88, 0.016321, 0.000165, 0.0 ],
206
+ [251.83, 26.651886, 0.000164, 0.0 ],
207
+ [349.42, 36.412478, 0.000126, 0.0 ],
208
+ [ 84.66, 18.206239, 0.000110, 0.0 ],
209
+ [141.74, 53.303771, 0.000062, 0.0 ],
210
+ [207.14, 2.453732, 0.000060, 0.0 ],
211
+ [154.84, 7.306860, 0.000056, 0.0 ],
212
+ [ 34.52, 27.261239, 0.000047, 0.0 ],
213
+ [207.19, 0.121824, 0.000042, 0.0 ],
214
+ [291.34, 1.844379, 0.000040, 0.0 ],
215
+ [161.72, 24.198154, 0.000037, 0.0 ],
216
+ [239.56, 25.513099, 0.000035, 0.0 ],
217
+ [331.55, 3.592518, 0.000023, 0.0 ]
218
+ ]
219
+
220
+ # First and last quarter
221
+ Table_49C = [ # page 352
222
+ # E F M M' O
223
+ [-0.00002, 0, 0.0, 1.0, 3.0, 0.0],
224
+ [ 0.00002, 0, 2.0, -1.0, 1.0, 0.0],
225
+ [ 0.00002, 0, -2.0, 0.0, 2.0, 0.0],
226
+ [ 0.00003, 0, 0.0, 3.0, 0.0, 0.0],
227
+ [ 0.00003, 0, -2.0, 1.0, 1.0, 0.0],
228
+ [ 0.00004, 0, 0.0, -2.0, 1.0, 0.0],
229
+ [-0.00004, 0, 2.0, 1.0, 1.0, 0.0],
230
+ [ 0.00004, 0, 2.0, 0.0, 2.0, 0.0],
231
+ [-0.00005, 0, -2.0, -1.0, 1.0, 0.0],
232
+ [-0.00017, 0, 0.0, 0.0, 0.0, 1.0],
233
+ [ 0.00027, 1, 0.0, 1.0, 2.0, 0.0],
234
+ [-0.00028, 2, 0.0, 2.0, 1.0, 0.0],
235
+ [ 0.00032, 1, -2.0, 1.0, 0.0, 0.0],
236
+ [ 0.00032, 1, 2.0, 1.0, 0.0, 0.0],
237
+ [-0.00034, 1, 0.0, -1.0, 2.0, 0.0],
238
+ [-0.00040, 0, 0.0, 0.0, 3.0, 0.0],
239
+ [-0.00070, 0, 2.0, 0.0, 1.0, 0.0],
240
+ [-0.00180, 0, -2.0, 0.0, 1.0, 0.0],
241
+ [ 0.00204, 2, 0.0, 2.0, 0.0, 0.0],
242
+ [ 0.00454, 1, 0.0, -1.0, 1.0, 0.0],
243
+ [ 0.00804, 0, 2.0, 0.0, 0.0, 0.0],
244
+ [ 0.00862, 0, 0.0, 0.0, 2.0, 0.0],
245
+ [-0.01183, 1, 0.0, 1.0, 1.0, 0.0],
246
+ [ 0.17172, 1, 0.0, 1.0, 0.0, 0.0],
247
+ [-0.62801, 0, 0.0, 0.0, 1.0, 0.0]
248
+ ]
249
+ # :startdoc:
250
+
251
+ PhaseNew = 0
252
+ PhaseFirstQuarter = 1
253
+ PhaseFull = 2
254
+ PhaseLastQuarter = 3
255
+
256
+ # Returns DateTime for phase of Moon.
257
+ # Implementation of algorithm in chapter 49.
258
+ # k:: number of Moon. k = 0 is first New Moon in the year 2000.
259
+ # phase:: Astro::PhaseNew, Astro::PhaseFirstQuarter, Astro::PhaseFull, Astro::PhaseLastQuarter
260
+ #
261
+ # Example: compute the date and time of New Moon #87 (first lunation in 2007).
262
+ # Astro.date_of_moon(87, Astro::PhaseNew).asctime # => "Fri Jan 19 04:01:45 2007"
263
+ def Astro.date_of_moon(k, phase)
264
+
265
+ k += phase / 4.0
266
+
267
+ # t = Julian centuries
268
+ t = k / 1236.85
269
+
270
+ # Julian Ephemeris Days
271
+ jde = 2_451_550.097_66 +
272
+ 29.530_588_861 * k +
273
+ [0.000_000_000_73, -0.000_000_150, 0.000_154_37, 0.0, 0.0].poly_eval(t)
274
+
275
+ # Eccentricity of Earth's orbit
276
+ e = [-0.000_0074, -0.002_516, 1.0].poly_eval(t)
277
+
278
+ # Sun's mean anomaly
279
+ m = 2.5534 +
280
+ 29.105_356_70 * k +
281
+ [-0.000_000_11, -0.000_0014, 0.0, 0.0].poly_eval(t)
282
+
283
+ # Moon's mean anomaly
284
+ m_lun = 201.5643 +
285
+ 385.816_935_28 * k +
286
+ [-0.000_000_058, 0.000_012_38, 0.010_7582, 0.0, 0.0].poly_eval(t)
287
+
288
+ # Moon's argument of latitude
289
+ f = 160.7108 +
290
+ 390.670_502_84 * k +
291
+ [0.000_000_011, -0.000_002_27, -0.001_6118, 0.0, 0.0].poly_eval(t)
292
+
293
+ # omega = longitude of the ascending node of the lunar orbit
294
+ o = 124.7746 -
295
+ 1.563_755_88 * k +
296
+ [0.000_002_15, 0.002_0672, 0.0, 0.0].poly_eval(t)
297
+
298
+ if phase == PhaseNew || phase == PhaseFull # new moon or full moon
299
+
300
+ col = phase == PhaseNew ? 0 : 1 # choose column of coefficient
301
+ corr1 = 0.0
302
+ for term in Table_49B do
303
+ t1 = term[col]
304
+ term[2].times {t1 *= e}
305
+ t2 = term[3] * f
306
+ t2 += term[4] * m
307
+ t2 += term[5] * m_lun
308
+ t2 += term[6] * o
309
+ t2 = Math.sin((t2 % 360.0).to_rad)
310
+ corr1 += t1 * t2
311
+ end
312
+
313
+ jde += corr1
314
+
315
+ else # first or last quarter
316
+ corr1 = 0.0
317
+ for term in Table_49C do
318
+ t1 = term[0]
319
+ term[1].times {t1 *= e}
320
+ t2 = term[2] * f
321
+ t2 += term[3] * m
322
+ t2 += term[4] * m_lun
323
+ t2 += term[5] * o
324
+ t2 = Math.sin((t2 % 360.0).to_rad)
325
+ corr1 += t1 * t2
326
+ end
327
+ jde += corr1
328
+
329
+ w = 0.00002 * Math.cos((2.0*f).to_rad) +
330
+ 0.00002 * Math.cos((m_lun + m).to_rad) -
331
+ 0.00002 * Math.cos((m_lun - m).to_rad) +
332
+ 0.00026 * Math.cos(m_lun.to_rad) -
333
+ 0.00038 * e * Math.cos(m.to_rad) +
334
+ 0.00306
335
+
336
+ if phase == PhaseFirstQuarter
337
+ jde += w # first quarter
338
+ else
339
+ jde -= w # last quarter
340
+ end
341
+ end
342
+
343
+ # correction for all phases
344
+ corr2 = 0.0
345
+ t2 = t*t
346
+ for term in Table_49A do
347
+ a = term[0]
348
+ a += term[1] * k
349
+ a += term[3] * t2
350
+ corr2 += Math.sin((a % 360.0).to_rad) * term[2]
351
+ end
352
+
353
+ DateTime.jd((jde + corr2).to_r, 12)
354
+ end
355
+
356
+ # Find number of first lunation (New Moon) for a given year.
357
+ # Lunation 0 is the first New Moon in the year 2000.
358
+ # Returns an integer lunation number.
359
+ #
360
+ # Example: find first lunation of 1776.
361
+ # Astro.first_lunation_of_year(1776) # => -2770
362
+ def Astro.first_lunation_of_year(year)
363
+ k = ((year - 2000)*12.3685).floor
364
+ k += 1 while date_of_moon(k, PhaseNew).year < year
365
+ k
366
+ end
367
+
368
+
369
+ # :stopdoc:
370
+ # Vernal Equinox
371
+ Table_27C = [ # page 179
372
+ # sum(A cos (B + C*t))
373
+ # A B C
374
+ [ 8.0, 15.45, 16_859.074],
375
+ [ 9.0, 227.73, 1_222.114],
376
+ [ 12.0, 320.81, 34_777.259],
377
+ [ 12.0, 287.11, 31_931.756],
378
+ [ 12.0, 95.39, 14_577.848],
379
+ [ 14.0, 199.76, 31_436.921],
380
+ [ 16.0, 198.04, 62_894.029],
381
+ [ 17.0, 288.79, 4_562.452],
382
+ [ 18.0, 155.12, 67_555.328],
383
+ [ 29.0, 60.93, 4_443.417],
384
+ [ 44.0, 325.15, 31_555.956],
385
+ [ 45.0, 247.54, 29_929.562],
386
+ [ 50.0, 21.02, 2_281.226],
387
+ [ 52.0, 297.17, 150.678],
388
+ [ 58.0, 119.81, 33_718.147],
389
+ [ 70.0, 243.58, 9_037.513],
390
+ [ 74.0, 296.72, 3_034.906],
391
+ [ 77.0, 222.54, 65_928.934],
392
+ [136.0, 171.52, 22_518.443],
393
+ [156.0, 73.14, 45_036.886],
394
+ [182.0, 27.85, 445_267.112],
395
+ [199.0, 342.08, 20.186],
396
+ [203.0, 337.23, 32_964.467],
397
+ [485.0, 324.96, 1_934.136]
398
+ ]
399
+ # :startdoc:
400
+
401
+ # Compute date and time of Vernal Equinox for given year.
402
+ # Implementation of low accuracy algorithm in chapter 27.
403
+ # Good from about -1000 to +3000.
404
+ # Returns DateTime of Vernal Equinox.
405
+ #
406
+ # Example: date of Vernal Equinox of 1999.
407
+ # Astro.date_of_vernal_equinox_low_accuracy(1999).asctime # => "Sun Mar 21 01:46:59 1999"
408
+ def Astro.date_of_vernal_equinox_low_accuracy(year)
409
+ if year >= 1000 # +1000 to +3000
410
+ y = (year - 2000.0) / 1000.0
411
+
412
+ # Julian day of March mean equinox
413
+ jdme = [-0.000_57, -0.004_11, 0.051_69, 365_242.374_04, 2_451_623.809_84].poly_eval(y)
414
+
415
+ else # -1000 to +1000
416
+ y = year/1000.0
417
+
418
+ # Julian day of March mean equinox
419
+ jdme = [-0.000_71, 0.001_11, 0.061_34, 365_242.137_40, 1_721_139.291_89].poly_eval(y)
420
+
421
+ end
422
+
423
+ # Julian centuries from 2000
424
+ t = (jdme - 2_451_545.0) / 36525.0
425
+
426
+ w = (35_999.373 * t - 2.47).to_rad
427
+ lambda = 1.0 + 0.0334*Math.cos(w) + 0.0007 * Math.cos(2.0 * w)
428
+ s = Table_27C.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos((b + c*t).to_rad)}
429
+
430
+ ve = jdme + 0.000_01 * s / lambda
431
+
432
+ DateTime.jd(ve.to_r, 12)
433
+ end
434
+
435
+ # Compute date and time of Vernal Equinox for given year.
436
+ # Implementation of higher accuracy algorithm in chapter 27.
437
+ # Returns DateTime of Vernal Equinox.
438
+ #
439
+ # Example: date of Vernal Equinox of 1999.
440
+ # Astro.date_of_vernal_equinox(1999).asctime # => "Sun Mar 21 01:46:56 1999"
441
+ def Astro.date_of_vernal_equinox(year)
442
+ est_date = date_of_vernal_equinox_low_accuracy(year)
443
+
444
+ 5.times do |i|
445
+ sol_long = solar_longitude(est_date)
446
+ sol_long -= 360.0 if sol_long > 180.0
447
+ break if sol_long.abs < 0.000001
448
+ est_date += (58 * Math.sin(-sol_long.to_rad)).to_r
449
+ end
450
+
451
+ est_date
452
+ end
453
+
454
+
455
+ # :stopdoc:
456
+ # Solar Coordinates
457
+ Table_22A = [
458
+ # D M M' F omega coef of sin
459
+ [ 2, -1, 0, 2, 2, -3, 0],
460
+ [ 0, 0, 3, 2, 2, -3, 0],
461
+ [ 2, -1, -1, 2, 2, -3, 0],
462
+ [ 0, -1, 1, 2, 2, -3, 0],
463
+ [ 0, 1, 1, 0, 0, -3, 0],
464
+ [-1, -1, 1, 0, 0, -3, 0],
465
+ [ 0, 0, -2, 2, 2, -3, 0],
466
+ [ 0, 0, 1, 2, 0, 3, 0],
467
+ [ 1, 0, 0, 0, 0, -4, 0],
468
+ [-2, 1, 0, 0, 0, -4, 0],
469
+ [-1, 0, 1, 0, 0, -4, 0],
470
+ [ 0, 0, 1, -2, 0, 4, 0],
471
+ [-2, 1, 0, 2, 1, 4, 0],
472
+ [-2, 0, 2, 0, 1, 4, 0],
473
+ [ 0, 0, 2, 2, 1, -5, 0],
474
+ [-2, 0, 0, 0, 1, -5, 0],
475
+ [-2, -1, 0, 2, 1, -5, 0],
476
+ [ 0, -1, 1, 0, 0, 5, 0],
477
+ [ 2, 0, 0, 0, 1, -6, 0],
478
+ [ 2, 0, -2, 0, 1, -6, 0],
479
+ [-2, 0, 1, 2, 1, 6, 0],
480
+ [-2, 0, 2, 2, 2, 6, 0],
481
+ [ 2, 0, 1, 0, 0, 6, 0],
482
+ [ 2, 0, 0, 2, 1, -7, 0],
483
+ [ 0, -1, 0, 2, 2, -7, 0],
484
+ [-2, 1, 1, 0, 0, -7, 0],
485
+ [ 0, 1, 0, 2, 2, 7, 0],
486
+ [ 2, 0, 1, 2, 2, -8, 0],
487
+ [ 2, 0, -1, 2, 1, -10, 0],
488
+ [ 0, 0, 2, -2, 0, 11, 0],
489
+ [ 0, -1, 0, 0, 1, -12, 0],
490
+ [-2, 0, 1, 0, 1, -13, 0],
491
+ [ 0, 1, 0, 0, 1, -15, 0],
492
+ [-2, 2, 0, 2, 2, -16, 0.1],
493
+ [ 2, 0, -1, 0, 1, 16, 0],
494
+ [ 0, 2, 0, 0, 0, 17, -0.1],
495
+ [ 0, 0, -1, 2, 1, 21, 0],
496
+ [-2, 0, 0, 2, 0, -22, 0],
497
+ [ 0, 0, 0, 2, 0, 26, 0],
498
+ [-2, 0, 1, 2, 2, 29, 0],
499
+ [ 0, 0, 2, 0, 0, 29, 0],
500
+ [ 0, 0, 2, 2, 2, -31, 0],
501
+ [ 2, 0, 0, 2, 2, -38, 0],
502
+ [ 0, 0, -2, 2, 1, 46, 0],
503
+ [-2, 0, 2, 0, 0, 48, 0],
504
+ [ 0, 0, 1, 2, 1, -51, 0],
505
+ [ 0, 0, -1, 0, 1, -58, -0.1],
506
+ [ 2, 0, -1, 2, 2, -59, 0],
507
+ [ 0, 0, 1, 0, 1, 63, 0.1],
508
+ [ 2, 0, 0, 0, 0, 63, 0],
509
+ [ 0, 0, -1, 2, 2, 123, 0],
510
+ [-2, 0, 0, 2, 1, 129, 0.1],
511
+ [-2, 0, 1, 0, 0, -158, 0],
512
+ [-2, -1, 0, 2, 2, 217, -0.5],
513
+ [ 0, 0, 1, 2, 2, -301, 0],
514
+ [ 0, 0, 0, 2, 1, -386, -0.4],
515
+ [-2, 1, 0, 2, 2, -517, 1.2],
516
+ [ 0, 0, 1, 0, 0, 712, 0.1],
517
+ [ 0, 1, 0, 0, 0, 1426, -3.4],
518
+ [ 0, 0, 0, 0, 2, 2062, 0.2],
519
+ [ 0, 0, 0, 2, 2, -2274, -0.2],
520
+ [-2, 0, 0, 2, 2, -13187, -1.6],
521
+ [ 0, 0, 0, 0, 1, -171996, -174.2]
522
+ ]
523
+ # :startdoc:
524
+
525
+ # Compute nutation in longitude of Earth's pole for the given DateTime.
526
+ # Implementation of algorithm in chapter 22.
527
+ # Returns nutation in degrees.
528
+ def Astro.nutation_in_longitude(date)
529
+
530
+ # Julian centuries from the epoch J2000.0
531
+ t = (date.ajd.to_f - 2451545.0)/36525.0
532
+
533
+ # Mean elongation of the Moon from the Sun
534
+ d = [1.0/189_474.0, -0.001_9142, 445_267.111_480, 297.85036].poly_eval(t) % 360.0
535
+
536
+ # Mean anomaly of the Sun (Earth)
537
+ m = [-1.0/300_000.0, -0.000_1603, 35_999.050_340, 357.52772].poly_eval(t) % 360.0
538
+
539
+ # Mean anomaly of the Moon
540
+ m_lun = [1.0/56_250.0, 0.008_6972, 477_198.867_398, 134.96298].poly_eval(t) %360.0
541
+
542
+ # Moon's argument of latitude
543
+ f = [1.0/327_270.0, -0.003_6825, 483_202.017_538, 93.27191].poly_eval(t) % 360.0
544
+
545
+ # Longitude of the ascending node of the Moon's mean orbit on the ecliptic
546
+ omega = [1.0/450_000.0, 0.002_0708, -1934.136_261, 125.04452].poly_eval(t) % 360.0
547
+
548
+ nutation = 0.0
549
+ for term in Table_22A do
550
+ nutation += (term[5] + term[6]*t) *
551
+ Math.sin((term[0]*d + term[1]*m + term[2]*m_lun + term[3]*f + term[4]*omega).to_rad) / 36_000_000.0
552
+ end
553
+
554
+ nutation
555
+
556
+ end
557
+
558
+ # Compute longitude of the Sun for the given DateTime.
559
+ # Implementation of low accuracy algorithm in chapter 25.
560
+ # Returns longitude in degrees.
561
+ def Astro.solar_longitude_low_accuracy(date)
562
+
563
+ # t = Julian centuries from the epoch J2000.0 (2000 January 1.5 TD)
564
+ t = (date.ajd.to_f - 2451545.0)/36525.0
565
+
566
+ # Mean longitude of the Sun
567
+ mean_long = [0.000_3032, 36_000.769_83, 280.46646].poly_eval(t) % 360.0
568
+
569
+ # Mean anomaly of the Sun
570
+ mean_anomaly = [-0.000_1537, 35_999.050_29, 357.52911].poly_eval(t) % 360.0
571
+
572
+ # Center of the sun
573
+ m_rad = mean_anomaly.to_rad
574
+ c = [-0.000_014, -0.004_817, 1.914_602].poly_eval(t) * Math.sin(m_rad) +
575
+ [-0.000_101, 0.019_993].poly_eval(t) * Math.sin(2.0 * m_rad) +
576
+ 0.000_289 * Math.sin(3.0 * m_rad)
577
+
578
+ # True longitude
579
+ true_long = (mean_long + c) % 360.0
580
+
581
+ # Longitude corrected for nutation and the aberration
582
+ omega = (125.04 - 1934.136*t) % 360.0
583
+ apparent_long = (true_long - 0.00569 - 0.00478 * Math.sin(omega.to_rad)) % 360.0
584
+ apparent_long
585
+ end
586
+
587
+
588
+ # :stopdoc:
589
+ # Helio-centric coordinates of Earth
590
+ EarthL0 = [
591
+ # A B C
592
+ [ 25.0, 3.16, 4_690.48 ],
593
+ [ 30.0, 2.74, 1_349.87 ],
594
+ [ 30.0, 0.44, 83_996.85 ],
595
+ [ 33.0, 0.59, 17_789.85 ],
596
+ [ 36.0, 1.78, 6_812.77 ],
597
+ [ 36.0, 1.71, 2_352.87 ],
598
+ [ 37.0, 2.57, 1_059.38 ],
599
+ [ 37.0, 6.04, 10_213.29 ],
600
+ [ 39.0, 6.17, 10_447.39 ],
601
+ [ 41.0, 2.40, 19_651.05 ],
602
+ [ 41.0, 5.37, 8_429.24 ],
603
+ [ 49.0, 0.49, 1_194.45 ],
604
+ [ 51.0, 0.28, 5_856.48 ],
605
+ [ 52.0, 1.33, 1_748.02 ],
606
+ [ 52.0, 0.19, 12_139.55 ],
607
+ [ 56.0, 3.47, 6_279.55 ],
608
+ [ 56.0, 4.39, 14_143.50 ],
609
+ [ 57.0, 2.78, 6_286.60 ],
610
+ [ 61.0, 1.82, 7_084.90 ],
611
+ [ 62.0, 3.98, 8_827.39 ],
612
+ [ 70.0, 0.83, 9_437.76 ],
613
+ [ 74.0, 4.68, 801.82 ],
614
+ [ 74.0, 3.50, 3_154.69 ],
615
+ [ 75.0, 1.76, 5_088.63 ],
616
+ [ 79.0, 3.04, 12_036.46 ],
617
+ [ 80.0, 1.81, 17_260.15 ],
618
+ [ 85.0, 3.67, 71_430.70 ],
619
+ [ 85.0, 1.30, 6_275.96 ],
620
+ [ 86.0, 5.98, 161_000.69 ],
621
+ [ 98.0, 0.68, 155.42 ],
622
+ [ 99.0, 6.21, 2_146.17 ],
623
+ [ 102.0, 4.267, 7.114 ],
624
+ [ 102.0, 0.976, 15_720.839 ],
625
+ [ 103.0, 0.636, 4_694.003 ],
626
+ [ 115.0, 0.645, 0.980 ],
627
+ [ 126.0, 1.083, 20.775 ],
628
+ [ 132.0, 3.411, 2_942.463 ],
629
+ [ 156.0, 0.833, 213.299 ],
630
+ [ 202.0, 2.458, 6_069.777 ],
631
+ [ 205.0, 1.869, 5_573.143 ],
632
+ [ 206.0, 4.806, 2_544.314 ],
633
+ [ 243.0, 0.345, 5_486.778 ],
634
+ [ 271.0, 0.315, 10_977.079 ],
635
+ [ 284.0, 1.899, 796.298 ],
636
+ [ 317.0, 5.849, 11_790.629 ],
637
+ [ 357.0, 2.920, 0.067 ],
638
+ [ 492.0, 4.205, 775.523 ],
639
+ [ 505.0, 4.583, 18_849.228 ],
640
+ [ 753.0, 2.533, 5_507.553 ],
641
+ [ 780.0, 1.179, 5_223.694 ],
642
+ [ 857.0, 3.508, 398.149 ],
643
+ [ 902.0, 2.045, 26.298 ],
644
+ [ 990.0, 5.233, 5_884.927 ],
645
+ [ 1_199.0, 1.109_6, 1_577.343_5 ],
646
+ [ 1_273.0, 2.037_1, 529.691_0 ],
647
+ [ 1_324.0, 0.742_5, 11_506.769_8 ],
648
+ [ 2_343.0, 6.135_2, 3_930.209_7 ],
649
+ [ 2_676.0, 4.418_1, 7_860.419_4 ],
650
+ [ 3_136.0, 3.627_7, 77_713.771_5 ],
651
+ [ 3_418.0, 2.828_9, 3.523_1 ],
652
+ [ 3_497.0, 2.744_1, 5_753.384_9 ],
653
+ [ 34_894.0, 4.626_10, 12_566.151_70 ],
654
+ [ 3_341_656.0, 4.669_256_8, 6_283.075_850_0 ],
655
+ [175_347_046.0, 0.0, 0.0 ]
656
+ ]
657
+
658
+ EarthL1 = [
659
+ [ 6.0, 4.67, 4_690.48 ],
660
+ [ 6.0, 2.65, 9_437.76 ],
661
+ [ 8.0, 5.30, 2_352.87 ],
662
+ [ 9.0, 5.64, 951.72 ],
663
+ [ 9.0, 2.70, 242.73 ],
664
+ [ 10.0, 4.24, 1_349.87 ],
665
+ [ 10.0, 1.30, 6_286.60 ],
666
+ [ 11.0, 0.77, 553.57 ],
667
+ [ 12.0, 2.08, 4_694.00 ],
668
+ [ 12.0, 5.27, 1_194.45 ],
669
+ [ 12.0, 3.26, 5_088.63 ],
670
+ [ 12.0, 2.83, 1_748.02 ],
671
+ [ 15.0, 1.21, 10_977.08 ],
672
+ [ 16.0, 1.43, 2_146.17 ],
673
+ [ 16.0, 0.03, 2_544.31 ],
674
+ [ 17.0, 2.99, 6_275.96 ],
675
+ [ 19.0, 4.97, 213.30 ],
676
+ [ 19.0, 1.85, 5_486.78 ],
677
+ [ 21.0, 5.34, 0.98 ],
678
+ [ 29.0, 2.65, 7.11 ],
679
+ [ 36.0, 0.47, 775.52 ],
680
+ [ 45.0, 0.40, 796.30 ],
681
+ [ 56.0, 2.17, 155.42 ],
682
+ [ 59.0, 2.89, 5_223.69 ],
683
+ [ 67.0, 4.41, 5_507.55 ],
684
+ [ 68.0, 1.87, 398.15 ],
685
+ [ 72.0, 1.14, 529.69 ],
686
+ [ 93.0, 2.59, 18_849.23 ],
687
+ [ 109.0, 2.966, 1_577.344 ],
688
+ [ 119.0, 5.796, 26.298 ],
689
+ [ 425.0, 1.590, 3.523 ],
690
+ [ 4_303.0, 2.635_1, 12_566.151_7 ],
691
+ [ 206_059.0, 2.678_235, 6_283.075_850],
692
+ [628_331_966_747.0, 0.0, 0.0 ]
693
+ ]
694
+
695
+ EarthL2 = [
696
+ [ 2.0, 3.75, 0.98 ],
697
+ [ 2.0, 4.38, 5_233.69 ],
698
+ [ 3.0, 2.28, 553.57 ],
699
+ [ 3.0, 0.31, 398.15 ],
700
+ [ 3.0, 6.12, 529.69 ],
701
+ [ 3.0, 1.19, 242.73 ],
702
+ [ 3.0, 6.05, 5_507.55 ],
703
+ [ 3.0, 5.14, 796.30 ],
704
+ [ 4.0, 3.44, 5_573.14 ],
705
+ [ 4.0, 1.03, 7.11 ],
706
+ [ 5.0, 4.66, 1_577.34 ],
707
+ [ 7.0, 0.83, 775.52 ],
708
+ [ 9.0, 2.06, 77_713.77 ],
709
+ [ 10.0, 0.76, 18_849.23 ],
710
+ [ 16.0, 3.68, 155.42 ],
711
+ [ 16.0, 5.19, 26.30 ],
712
+ [ 27.0, 0.05, 3.52 ],
713
+ [ 309.0, 0.867, 12_566.152 ],
714
+ [ 8_720.0, 1.072_1, 6_283.075_8],
715
+ [52_919.0, 0.0, 0.0 ]
716
+ ]
717
+
718
+ EarthL3 = [
719
+ [ 1.0, 5.97, 242.73 ],
720
+ [ 1.0, 5.30, 18_849.23 ],
721
+ [ 1.0, 4.72, 3.52 ],
722
+ [ 3.0, 5.20, 155.42 ],
723
+ [ 17.0, 5.49, 12_566.15 ],
724
+ [ 35.0, 0.0, 0.0 ],
725
+ [289.0, 5.844, 6_283.076]
726
+ ]
727
+
728
+ EarthL4 = [
729
+ [ 1.0, 3.84, 12_566.15],
730
+ [ 8.0, 4.13, 6_283.08],
731
+ [114.0, 3.142, 0.0 ]
732
+ ]
733
+
734
+ EarthL5 = [
735
+ [ 1.0, 3.14, 0.0]
736
+ ]
737
+
738
+ EarthR0 = [
739
+ [ 26, 4.59, 10_447.39 ],
740
+ [ 28, 1.90, 6_279.55 ],
741
+ [ 28, 1.21, 6_286.60 ],
742
+ [ 32, 1.78, 398.15 ],
743
+ [ 32, 0.18, 5_088.63 ],
744
+ [ 33, 0.24, 7_084.90 ],
745
+ [ 35, 1.84, 2_942.46 ],
746
+ [ 36, 1.67, 12_036.46 ],
747
+ [ 37, 4.90, 12_139.55 ],
748
+ [ 37, 0.83, 19_651.05 ],
749
+ [ 38, 2.39, 8_827.39 ],
750
+ [ 39, 5.36, 4_694.00 ],
751
+ [ 43, 6.01, 6_275.96 ],
752
+ [ 45, 5.54, 9_437.76 ],
753
+ [ 47, 2.58, 775.52 ],
754
+ [ 49, 3.25, 2_544.31 ],
755
+ [ 56, 5.24, 71_430.70 ],
756
+ [ 57, 2.01, 83_996.85 ],
757
+ [ 63, 0.92, 529.69 ],
758
+ [ 65, 0.27, 17_260.15 ],
759
+ [ 86, 1.27, 161_000.69 ],
760
+ [ 86, 5.69, 15_720.84 ],
761
+ [ 98, 0.89, 6_069.78 ],
762
+ [ 110, 5.055, 5_486.778 ],
763
+ [ 175, 3.012, 18_849.228 ],
764
+ [ 186, 5.022, 10_977.079 ],
765
+ [ 212, 5.847, 1_577.344 ],
766
+ [ 243, 4.273, 11_790.629 ],
767
+ [ 307, 0.299, 5_573.143 ],
768
+ [ 329, 5.900, 5_223.694 ],
769
+ [ 346, 0.964, 5_507.553 ],
770
+ [ 472, 3.661, 5_884.927 ],
771
+ [ 542, 4.564, 3_930.210 ],
772
+ [ 925, 5.453, 11_506.770 ],
773
+ [ 1_576, 2.846_9, 7_860.419_4 ],
774
+ [ 1_628, 1.173_9, 5_753.384_9 ],
775
+ [ 3_084, 5.198_5, 77_713.771_5 ],
776
+ [ 13_956, 3.055_25, 12_566.151_70 ],
777
+ [ 1_670_700, 3.098_463_5, 6_283.075_850_0],
778
+ [100_013_989, 0, 0 ]
779
+ ]
780
+
781
+ EarthR1 = [
782
+ [ 9, 0.27, 5_486.78 ],
783
+ [ 9, 1.42, 6_275.96 ],
784
+ [ 10, 5.91, 10_977.08 ],
785
+ [ 18, 1.42, 1_577.34 ],
786
+ [ 25, 1.32, 5_223.69 ],
787
+ [ 31, 2.84, 5_507.55 ],
788
+ [ 32, 1.02, 18_849.23 ],
789
+ [ 702, 3.142, 0 ],
790
+ [ 1_721, 1.064_4, 12_566.151_7 ],
791
+ [103_019, 1.107_490, 6_283.075_850]
792
+ ]
793
+
794
+ EarthR2 = [
795
+ [ 3, 5.47, 18_849.23 ],
796
+ [ 6, 1.87, 5_573.14 ],
797
+ [ 9, 3.63, 77_713.77 ],
798
+ [ 12, 3.14, 0 ],
799
+ [ 124, 5.579, 12_566.152 ],
800
+ [4_359, 5.784_6, 6_283.075_8]
801
+ ]
802
+
803
+ EarthR3 = [
804
+ [ 7, 3.92, 12_566.15 ],
805
+ [145, 4.273, 6_283.076]
806
+ ]
807
+
808
+ EarthR4 = [
809
+ [4, 2.56, 6_283.08]
810
+ ]
811
+ # :startdoc:
812
+
813
+
814
+ # Compute longitude of Sun for given DateTime.
815
+ # Implementation of higher accuracy algorithm in chapter 25.
816
+ # Accurate to within 1" between the years -2000 and +6000.
817
+ # Returns longitude in degrees.
818
+ def Astro.solar_longitude(date)
819
+
820
+ # t = Julian millennia from the epoch J2000.0 (2000 January 1.5 TD)
821
+ t = (date.ajd.to_f - 2451545.0)/365250.0
822
+
823
+ l0 = EarthL0.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
824
+ l1 = EarthL1.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
825
+ l2 = EarthL2.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
826
+ l3 = EarthL3.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
827
+ l4 = EarthL4.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
828
+ l5 = EarthL5.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
829
+ long = (([l5, l4, l3, l2, l1, l0].poly_eval(t) * 1e-8).to_deg) % 360.0
830
+
831
+ r0 = EarthR0.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
832
+ r1 = EarthR1.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
833
+ r2 = EarthR2.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
834
+ r3 = EarthR3.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
835
+ r4 = EarthR4.inject(0.0) {|accum, (a, b, c)| accum + a * Math.cos(b + c*t)}
836
+ radius = [r4, r3, r2, r1, r0].poly_eval(t) * 1e-8
837
+ aberration = -0.0056916111/radius
838
+
839
+ long -= 180.0 # switch to Earth's perspective
840
+ long += -2.509167e-5 # convert to FK5 system
841
+ long += nutation_in_longitude(date)
842
+ long += aberration # correct for aberration
843
+ long % 360.0
844
+ end
845
+
846
+ end
@@ -0,0 +1,91 @@
1
+ # lunaryear.rb
2
+ # Lunar calendar library.
3
+ #
4
+ # Lunar months (or moonths) begin on the day of the New Moon in Coordinated
5
+ # Universal Time.
6
+ #
7
+ # The lunar year begins at the beginning of the moonth during which the
8
+ # Vernal Equinox occurs, usually March 20 or 21. Twelve moonths fall short
9
+ # of the Solar year by about 10 or 11 days, so an additional thirteenth
10
+ # moonth occurs every two or three years.
11
+
12
+ require File.join(File.dirname(__FILE__), 'astro-algo')
13
+
14
+ module LunarYear
15
+
16
+ # Calculate the lunar calendar date for the given DateTime.
17
+ # Returns [year, moonth, day].
18
+ def LunarYear.lunar_date(date)
19
+ year = date.year
20
+ lun0 = LunarYear.new_moon_before_vernal_equinox(year) # Year begins at Vernal Equinox
21
+ if date < Astro.date_of_moon(lun0, Astro::PhaseNew).to_date
22
+ year -= 1
23
+ lun0 = new_moon_before_vernal_equinox(year)
24
+ end
25
+ prev_moon = Astro.date_of_moon(lun0, Astro::PhaseNew).to_date
26
+ lun = lun0
27
+ loop do
28
+ lun += 1
29
+ new_moon = Astro.date_of_moon(lun, Astro::PhaseNew).to_date
30
+ break if new_moon > date
31
+ prev_moon = new_moon
32
+ end
33
+ moonth = lun - lun0 - 1
34
+ day = (date - prev_moon).to_i
35
+ [year, moonth, day]
36
+ end
37
+
38
+
39
+ # Find number of lunation (New Moon) just before Vernal Equinox for a given year.
40
+ # Lunation 0 is the first New Moon in the year 2000.
41
+ # Returns an integer lunation number.
42
+ #
43
+ # Example: first lunation before Vernal Equinox of the year 1984.
44
+ # LunarYear.new_moon_before_vernal_equinox(1984) # => -196
45
+ def LunarYear.new_moon_before_vernal_equinox(year)
46
+ equ = Astro.date_of_vernal_equinox(year)
47
+ lunation = Astro.first_lunation_of_year(year)
48
+ new_moon = Astro.date_of_moon(lunation, Astro::PhaseNew)
49
+ loop do
50
+ previous_new_moon = new_moon
51
+ lunation += 1
52
+ new_moon = Astro.date_of_moon(lunation, Astro::PhaseNew)
53
+ return lunation-1 if new_moon > equ
54
+ end
55
+ end
56
+
57
+
58
+ # Calculate the DateTime of the previous New and Full Moons
59
+ # and the DateTime of the next New and Full moons.
60
+ # Returns [previous New Moon, next New Moon, previous Full Moon, next Full Moon]
61
+ #
62
+ # Example: For November 18, 2007, calculate the date and time of the previous New Moon,
63
+ # the next New Moon, the previous Full Moon, and the next Full Moon.
64
+ # LunarYear.date_of_moons(DateTime.civil(2007, 11, 18)).collect {|d| d.asctime}
65
+ # #=> ["Fri Nov 9 23:03:07 2007", "Sun Dec 9 17:40:21 2007", "Fri Oct 26 04:51:33 2007", "Sat Nov 24 14:29:47 2007"]
66
+ def LunarYear.date_of_moons(date)
67
+ next_new_moon = nil
68
+ next_full_moon = nil
69
+
70
+ k = ((date.year - 2000)*12.3685).floor - 1
71
+ prev_new_moon = Astro.date_of_moon(k, Astro::PhaseNew).to_utc
72
+ lun = k
73
+ loop do
74
+ lun += 1
75
+ next_new_moon = Astro.date_of_moon(lun, Astro::PhaseNew).to_utc
76
+ break if next_new_moon > date
77
+ prev_new_moon = next_new_moon
78
+ end
79
+
80
+ lun = k
81
+ prev_full_moon = Astro.date_of_moon(k, Astro::PhaseFull).to_utc
82
+ loop do
83
+ lun += 1
84
+ next_full_moon = Astro.date_of_moon(lun, Astro::PhaseFull).to_utc
85
+ break if next_full_moon > date
86
+ prev_full_moon = next_full_moon
87
+ end
88
+
89
+ [prev_new_moon, next_new_moon, prev_full_moon, next_full_moon]
90
+ end
91
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: astro-algo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ""
6
+ authors:
7
+ - John P. Powers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2007-11-27 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: This library implements algorithms from Jean Meeus, Astronomical Algorithms, 2nd English Edition, Willmann-Bell, Inc., Richmond, Virginia, 1999, with corrections as of June 15, 2005.
17
+ email: john@jppowers.net
18
+ executables:
19
+ - equinox
20
+ - lunarcalendar
21
+ - moon_clock.rb
22
+ - moons
23
+ extensions: []
24
+
25
+ extra_rdoc_files: []
26
+
27
+ files:
28
+ - bin/moon_clock.rb
29
+ - lib/astro-algo.rb
30
+ - lib/lunaryear.rb
31
+ - README
32
+ - bin/equinox
33
+ - bin/lunarcalendar
34
+ - bin/moons
35
+ has_rdoc: true
36
+ homepage: http://astro-algo.rubyforge.org/astro-algo
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --title
40
+ - Astro API documentation
41
+ - --main
42
+ - README
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project: astro-algo
60
+ rubygems_version: 0.9.5
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: Implementation of algorithms in _Astronomical Algorithms_. Useful for computing phases of the moon.
64
+ test_files: []
65
+