astro_moon 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README +6 -0
  2. data/lib/astro/moon.rb +325 -0
  3. metadata +47 -0
data/README ADDED
@@ -0,0 +1,6 @@
1
+ Provides functions for lunar phases and lunar month dates (eg. new/full moon)
2
+
3
+ This file is a thoughtless manual compilation of Astro::MoonPhase perl
4
+ module (which, in turn, is influenced by moontool.c). I don't know
5
+ how it all works.
6
+
data/lib/astro/moon.rb ADDED
@@ -0,0 +1,325 @@
1
+ require 'date'
2
+
3
+ module Astro # :nodoc:
4
+ #
5
+ # Provides functions for lunar phases and lunar month dates (eg. new/full
6
+ # moon)
7
+ #
8
+ # This file is a thoughtless manual compilation of Astro::MoonPhase perl
9
+ # module (which, in turn, is influenced by moontool.c). I don't know
10
+ # how it all works.
11
+ module Moon # :doc:
12
+ class << self
13
+
14
+ # a container structure for phase() return value.
15
+ Phase = Struct.new(:phase, :illumination, :age, :distance, :angle,
16
+ :sun_distance, :sun_angle)
17
+
18
+ # a container structure for phasehunt() return value.
19
+ PhaseHunt = Struct.new(:moon_start, :first_quarter, :moon_full,
20
+ :last_quarter, :moon_end)
21
+ # Astronomical constants.
22
+ # 1980 January 0.0
23
+ EPOCH = 2444238.5
24
+
25
+ # Constants defining the Sun's apparent orbit.
26
+ #
27
+ # ecliptic longitude of the Sun at epoch 1980.0
28
+ ELONGE = 278.833540
29
+ # ecliptic longitude of the Sun at perigee
30
+ ELONGP = 282.596403
31
+ # eccentricity of Earth's orbit
32
+ ECCENT = 0.016718
33
+ # semi-major axis of Earth's orbit, km
34
+ SUNSMAX = 1.495985e8
35
+
36
+ # sun's angular size, degrees, at semi-major axis distance
37
+ SUNANGSIZ = 0.533128
38
+
39
+ # Elements of the Moon's orbit, epoch 1980.0.
40
+
41
+ # moon's mean longitude at the epoch
42
+ MMLONG = 64.975464
43
+ # mean longitude of the perigee at the epoch
44
+ MMLONGP = 349.383063
45
+ # mean longitude of the node at the epoch
46
+ MLNODE = 151.950429
47
+ # inclination of the Moon's orbit
48
+ MINC = 5.145396
49
+ # eccentricity of the Moon's orbit
50
+ MECC = 0.054900
51
+ # moon's angular size at distance a from Earth
52
+ MANGSIZ = 0.5181
53
+ # semi-major axis of Moon's orbit in km
54
+ MSMAX = 384401.0
55
+ # parallax at distance a from Earth
56
+ MPARALLAX = 0.9507
57
+ # synodic month (new Moon to new Moon)
58
+ SYNMONTH = 29.53058868
59
+
60
+
61
+ # Finds the key dates of the specified lunar month.
62
+ # Takes a DateTime object (or creates one using now() function).
63
+ # Returns a PhaseHunt struct instance.
64
+ #
65
+ # # find the date/time of the full moon in this lunar month
66
+ # Astro::Moon.phasehunt.moon_full.strftime("%D %T %Z") \
67
+ # # => "03/04/07 02:17:40 +0300"
68
+
69
+ def phasehunt(date = nil)
70
+ date = DateTime.now if !date
71
+ sdate = date.ajd
72
+
73
+ adate = sdate - 45
74
+ ad1 = DateTime.new1(adate)
75
+
76
+ k1 = ((ad1.year + ((ad1.month - 1) *
77
+ (1.0 / 12.0)) - 1900) * 12.3685).floor
78
+
79
+ adate = nt1 = meanphase(adate, k1)
80
+
81
+ while true
82
+ adate += SYNMONTH
83
+ k2 = k1 + 1
84
+ nt2 = meanphase(adate, k2)
85
+ break if nt1 <= sdate && nt2 > sdate
86
+ nt1 = nt2
87
+ k1 = k2
88
+ end
89
+
90
+ return PhaseHunt.new(*[
91
+ truephase(k1, 0.0),
92
+ truephase(k1, 0.25),
93
+ truephase(k1, 0.5),
94
+ truephase(k1, 0.75),
95
+ truephase(k2, 0.0)
96
+ ].map {
97
+ |_| _.new_offset(date.offset)
98
+ })
99
+ end
100
+
101
+ # Finds the lunar phase for the specified date.
102
+ # Takes a DateTime object (or creates one using now() function).
103
+ # Returns a Phase struct instance.
104
+ #
105
+ # # find the current moon illumination, in percents
106
+ # Astro::Moon.phase.illumination * 100 # => 63.1104513958699
107
+ #
108
+ # # find the current moon phase (new moon is 0 or 100,
109
+ # # full moon is 50, etc)
110
+ # Astro::Moon.phase.phase * 100 # => 70.7802812241989
111
+
112
+ def phase(dt = nil)
113
+ dt = DateTime.now if !dt
114
+ pdate = dt.ajd
115
+
116
+ # Calculation of the Sun's position.
117
+
118
+ day = pdate - EPOCH # date within epoch
119
+ n = ((360 / 365.2422) * day) % 360.0
120
+ m = (n + ELONGE - ELONGP) % 360.0 # convert from perigee
121
+ # co-ordinates to epoch 1980.0
122
+ ec = kepler(m, ECCENT) # solve equation of Kepler
123
+ ec = Math.sqrt((1 + ECCENT) / (1 - ECCENT)) * Math.tan(ec / 2)
124
+ ec = 2 * todeg(Math.atan(ec)) # true anomaly
125
+ lambdasun = (ec + ELONGP) % 360.0 # Sun's geocentric ecliptic
126
+ # longitude
127
+ # Orbital distance factor.
128
+ f = ((1 + ECCENT * Math.cos(torad(ec))) / (1 - ECCENT * ECCENT))
129
+ sundist = SUNSMAX / f # distance to Sun in km
130
+ sunang = f * SUNANGSIZ # Sun's angular size in degrees
131
+
132
+ # Calculation of the Moon's position.
133
+
134
+ # Moon's mean longitude.
135
+ ml = (13.1763966 * day + MMLONG) % 360.0
136
+
137
+ # Moon's mean anomaly.
138
+ mm = (ml - 0.1114041 * day - MMLONGP) % 360.0
139
+
140
+ # Moon's ascending node mean longitude.
141
+ mn = (MLNODE - 0.0529539 * day) % 360.0
142
+
143
+ # Evection.
144
+ ev = 1.2739 * Math.sin(torad(2 * (ml - lambdasun) - mm))
145
+
146
+ # Annual equation.
147
+ ae = 0.1858 * Math.sin(torad(m))
148
+
149
+ # Correction term.
150
+ a3 = 0.37 * Math.sin(torad(m))
151
+
152
+ # Corrected anomaly.
153
+ mmp = mm + ev - ae - a3
154
+
155
+ # Correction for the equation of the centre.
156
+ mec = 6.2886 * Math.sin(torad(mmp))
157
+
158
+ # Another correction term.
159
+ a4 = 0.214 * Math.sin(torad(2 * mmp))
160
+
161
+ # Corrected longitude.
162
+ lp = ml + ev + mec - ae + a4
163
+
164
+ # Variation.
165
+ v = 0.6583 * Math.sin(torad(2 * (lp - lambdasun)))
166
+
167
+ # True longitude.
168
+ lpp = lp + v
169
+
170
+ # Corrected longitude of the node.
171
+ np = mn - 0.16 * Math.sin(torad(m))
172
+
173
+ # Y inclination coordinate.
174
+ y = Math.sin(torad(lpp - np)) * Math.cos(torad(MINC))
175
+
176
+ # X inclination coordinate.
177
+ x = Math.cos(torad(lpp - np))
178
+
179
+ # Ecliptic longitude.
180
+ lambdamoon = todeg(Math.atan2(y, x))
181
+ lambdamoon += np
182
+
183
+ # Ecliptic latitude.
184
+ betam = todeg(Math.asin(Math.sin(torad(lpp - np)) *
185
+ Math.sin(torad(MINC))))
186
+
187
+ # Calculation of the phase of the Moon.
188
+
189
+ # Age of the Moon in degrees.
190
+ moonage = lpp - lambdasun
191
+
192
+ # Phase of the Moon.
193
+ moonphase = (1 - Math.cos(torad(moonage))) / 2
194
+
195
+ # Calculate distance of moon from the centre of the Earth.
196
+
197
+ moondist = (MSMAX * (1 - MECC * MECC)) /
198
+ (1 + MECC * Math.cos(torad(mmp + mec)))
199
+
200
+ # Calculate Moon's angular diameter.
201
+
202
+ moondfrac = moondist / MSMAX
203
+ moonang = MANGSIZ / moondfrac
204
+
205
+ # Calculate Moon's parallax.
206
+
207
+ moonpar = MPARALLAX / moondfrac
208
+
209
+ pphase = moonphase
210
+ mpfrac = (moonage % 360) / 360.0
211
+ mage = SYNMONTH * mpfrac
212
+ dist = moondist
213
+ angdia = moonang
214
+ sudist = sundist
215
+ suangdia = sunang
216
+ Phase.new(mpfrac, pphase, mage, dist, angdia, sudist, suangdia)
217
+ end
218
+
219
+ private
220
+
221
+ def torad(x) ; x * Math::PI / 180.0 ; end
222
+ def todeg(x) ; x * 180.0 / Math::PI ; end
223
+ def dsin(x) ; Math.sin(torad(x)) ; end
224
+ def dcos(x) ; Math.sin(torad(x)) ; end
225
+
226
+ def meanphase(sdate, k)
227
+ ## Time in Julian centuries from 1900 January 0.5
228
+ t = (sdate - 2415020.0) / 36525
229
+ t2 = t * t
230
+ t3 = t2 * t
231
+
232
+ nt1 = 2415020.75933 +
233
+ SYNMONTH * k +
234
+ 0.0001178 * t2 -
235
+ 0.000000155 * t3 +
236
+ 0.00033 * dsin(166.56 + 132.87 * t - 0.009173 * t2)
237
+ end
238
+
239
+ def truephase(k, phase)
240
+ apcor = 0
241
+
242
+ k += phase # add phase to new moon time
243
+ t = k / 1236.85 # time in Julian centuries from
244
+ # 1900 January 0.5
245
+ t2 = t * t # square for frequent use
246
+ t3 = t2 * t # cube for frequent use
247
+
248
+ # mean time of phase */
249
+ pt = 2415020.75933 +
250
+ SYNMONTH * k +
251
+ 0.0001178 * t2 -
252
+ 0.000000155 * t3 +
253
+ 0.00033 * dsin(166.56 + 132.87 * t - 0.009173 * t2)
254
+
255
+ # Sun's mean anomaly
256
+ m = 359.2242 + 29.10535608 * k - 0.0000333 * t2 - 0.00000347 * t3
257
+
258
+ # Moon's mean anomaly
259
+ mprime =
260
+ 306.0253 + 385.81691806 * k + 0.0107306 * t2 + 0.00001236 * t3
261
+
262
+ # Moon's argument of latitude
263
+ f = 21.2964 + 390.67050646 * k - 0.0016528 * t2 - 0.00000239 * t3
264
+
265
+ if phase < 0.01 || (phase - 0.5).abs < 0.01
266
+ # Corrections for New and Full Moon.
267
+
268
+ pt += (0.1734 - 0.000393 * t) * dsin(m) +
269
+ 0.0021 * dsin(2 * m) -
270
+ 0.4068 * dsin(mprime) +
271
+ 0.0161 * dsin(2 * mprime) -
272
+ 0.0004 * dsin(3 * mprime) +
273
+ 0.0104 * dsin(2 * f) -
274
+ 0.0051 * dsin(m + mprime) -
275
+ 0.0074 * dsin(m - mprime) +
276
+ 0.0004 * dsin(2 * f + m) -
277
+ 0.0004 * dsin(2 * f - m) -
278
+ 0.0006 * dsin(2 * f + mprime) +
279
+ 0.0010 * dsin(2 * f - mprime) +
280
+ 0.0005 * dsin(m + 2 * mprime)
281
+ apcor = 1
282
+ elsif (phase - 0.25).abs < 0.01 || (phase - 0.75).abs < 0.01
283
+ pt += (0.1721 - 0.0004 * t) * dsin(m) +
284
+ 0.0021 * dsin(2 * m) -
285
+ 0.6280 * dsin(mprime) +
286
+ 0.0089 * dsin(2 * mprime) -
287
+ 0.0004 * dsin(3 * mprime) +
288
+ 0.0079 * dsin(2 * f) -
289
+ 0.0119 * dsin(m + mprime) -
290
+ 0.0047 * dsin(m - mprime) +
291
+ 0.0003 * dsin(2 * f + m) -
292
+ 0.0004 * dsin(2 * f - m) -
293
+ 0.0006 * dsin(2 * f + mprime) +
294
+ 0.0021 * dsin(2 * f - mprime) +
295
+ 0.0003 * dsin(m + 2 * mprime) +
296
+ 0.0004 * dsin(m - 2 * mprime) -
297
+ 0.0003 * dsin(2 * m + mprime)
298
+
299
+ corr = 0.0028 - 0.0004 * dcos(m) + 0.0003 * dcos(mprime)
300
+ pt += (phase < 0.5 ? corr : -corr)
301
+ apcor = 1;
302
+ end
303
+
304
+ if !apcor || apcor == 0
305
+ raise "truephase() called with invalid phase selector (#{phase})."
306
+ end
307
+ return DateTime.new1(pt + 0.5)
308
+ end
309
+
310
+ def kepler(m, ecc)
311
+ m = torad(m)
312
+ e = m;
313
+ loop do
314
+ delta = e - ecc * Math.sin(e) - m
315
+ e -= delta / (1 - ecc * Math.cos(e))
316
+ break if delta.abs <= 1e-6
317
+ end
318
+ return e
319
+ end
320
+
321
+ end
322
+ end
323
+ end
324
+
325
+
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: astro_moon
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2007-03-10 00:00:00 +03:00
8
+ summary: A library for calculating the lunar phases and dates
9
+ require_paths:
10
+ - lib
11
+ email: dmitry.kim@gmail.com
12
+ homepage:
13
+ rubyforge_project:
14
+ description: This library calculates to high precision the lunar phases and dates.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - dmitry kim
31
+ files:
32
+ - lib/astro/moon.rb
33
+ - README
34
+ test_files: []
35
+
36
+ rdoc_options: []
37
+
38
+ extra_rdoc_files:
39
+ - README
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ requirements: []
45
+
46
+ dependencies: []
47
+