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