astro_moon 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +6 -0
- data/lib/astro/moon.rb +325 -0
- metadata +47 -0
data/README
ADDED
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
|
+
|