astro-algo 0.0.1

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.
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
+