Module: Astro::Moon

Defined in:
lib/astro/moon.rb

Overview

Provides functions for lunar phases and lunar month dates (eg. new/full moon)

This file is a thoughtless manual compilation of Astro::MoonPhase perl module (which, in turn, is influenced by moontool.c). I don’t know how it all works.

Defined Under Namespace

Classes: Phase, PhaseHunt

Constant Summary collapse

EPOCH =

Astronomical constants. 1980 January 0.0

2444238.5
ELONGE =

Constants defining the Sun’s apparent orbit.

ecliptic longitude of the Sun at epoch 1980.0

278.833540
ELONGP =

ecliptic longitude of the Sun at perigee

282.596403
ECCENT =

eccentricity of Earth’s orbit

0.016718
SUNSMAX =

semi-major axis of Earth’s orbit, km

1.495985e8
SUNANGSIZ =

sun’s angular size, degrees, at semi-major axis distance

0.533128
MMLONG =

moon’s mean longitude at the epoch

64.975464
MMLONGP =

mean longitude of the perigee at the epoch

349.383063
MLNODE =

mean longitude of the node at the epoch

151.950429
MINC =

inclination of the Moon’s orbit

5.145396
MECC =

eccentricity of the Moon’s orbit

0.054900
MANGSIZ =

moon’s angular size at distance a from Earth

0.5181
MSMAX =

semi-major axis of Moon’s orbit in km

384401.0
MPARALLAX =

parallax at distance a from Earth

0.9507
SYNMONTH =

synodic month (new Moon to new Moon)

29.53058868

Class Method Summary collapse

Class Method Details

.phase(dt = nil) ⇒ Object

Finds the lunar phase for the specified date. Takes a DateTime object (or creates one using now() function). Returns a Phase struct instance. (see Constants section)

# find the current moon illumination, in percents
Astro::Moon.phase.illumination * 100     # => 63.1104513958699

# find the current moon phase (new moon is 0 or 100,
# full moon is 50, etc)
Astro::Moon.phase.phase * 100            # => 70.7802812241989


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/astro/moon.rb', line 124

def phase(dt = nil)
    dt = DateTime.now if !dt
    pdate = dt.ajd

    # Calculation of the Sun's position.

    day = pdate - EPOCH			# date within epoch
    n = ((360 / 365.2422) * day) % 360.0
    m = (n + ELONGE - ELONGP) % 360.0	# convert from perigee
    # co-ordinates to epoch 1980.0
    ec = kepler(m, ECCENT)		# solve equation of Kepler
    ec = Math.sqrt((1 + ECCENT) / (1 - ECCENT)) * Math.tan(ec / 2)
    ec = 2 * todeg(Math.atan(ec))		# true anomaly
    lambdasun = (ec + ELONGP) % 360.0	# Sun's geocentric ecliptic
    # longitude
    # Orbital distance factor.
    f = ((1 + ECCENT * Math.cos(torad(ec))) / (1 - ECCENT * ECCENT))
    sundist = SUNSMAX / f		# distance to Sun in km
    sunang = f * SUNANGSIZ		# Sun's angular size in degrees

    # Calculation of the Moon's position.

    # Moon's mean longitude.
    ml = (13.1763966 * day + MMLONG) % 360.0

    # Moon's mean anomaly.
    mm = (ml - 0.1114041 * day - MMLONGP) % 360.0

    # Moon's ascending node mean longitude.
    mn = (MLNODE - 0.0529539 * day) % 360.0

    # Evection.
    ev = 1.2739 * Math.sin(torad(2 * (ml - lambdasun) - mm))

    # Annual equation.
    ae = 0.1858 * Math.sin(torad(m))

    # Correction term.
    a3 = 0.37 * Math.sin(torad(m))

    # Corrected anomaly.
    mmp = mm + ev - ae - a3

    # Correction for the equation of the centre.
    mec = 6.2886 * Math.sin(torad(mmp))

    # Another correction term.
    a4 = 0.214 * Math.sin(torad(2 * mmp))

    # Corrected longitude.
    lp = ml + ev + mec - ae + a4

    # Variation.
    v = 0.6583 * Math.sin(torad(2 * (lp - lambdasun)))

    # True longitude.
    lpp = lp + v

    # Corrected longitude of the node.
    np = mn - 0.16 * Math.sin(torad(m))

    # Y inclination coordinate.
    y = Math.sin(torad(lpp - np)) * Math.cos(torad(MINC))

    # X inclination coordinate.
    x = Math.cos(torad(lpp - np))

    # Ecliptic longitude.
    lambdamoon = todeg(Math.atan2(y, x))
    lambdamoon += np

    # Ecliptic latitude.
    betam = todeg(Math.asin(Math.sin(torad(lpp - np)) *
                            Math.sin(torad(MINC))))

    # Calculation of the phase of the Moon.

    # Age of the Moon in degrees.
    moonage = lpp - lambdasun

    # Phase of the Moon.
    moonphase = (1 - Math.cos(torad(moonage))) / 2

    # Calculate distance of moon from the centre of the Earth.

    moondist = (MSMAX * (1 - MECC * MECC)) /
        (1 + MECC * Math.cos(torad(mmp + mec)))

    # Calculate Moon's angular diameter.

    moondfrac = moondist / MSMAX
    moonang = MANGSIZ / moondfrac

    # Calculate Moon's parallax.

    moonpar = MPARALLAX / moondfrac

    pphase = moonphase
    mpfrac = (moonage % 360) / 360.0
    mage = SYNMONTH * mpfrac
    dist = moondist
    angdia = moonang
    sudist = sundist
    suangdia = sunang
    Phase.new(mpfrac, pphase, mage, dist, angdia, sudist, suangdia)
end

.phasehunt(date = nil) ⇒ Object

Finds the key dates of the specified lunar month. Takes a DateTime object (or creates one using now() function). Returns a PhaseHunt struct instance. (see Constants section)

# find the date/time of the full moon in this lunar month
Astro::Moon.phasehunt.moon_full.strftime("%D %T %Z") \
# => "03/04/07 02:17:40 +0300"


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/astro/moon.rb', line 81

def phasehunt(date = nil)
    date = DateTime.now if !date
    sdate = date.ajd

    adate = sdate - 45
    ad1 = DateTime.jd(adate)

    k1 = ((ad1.year + ((ad1.month - 1) *
                       (1.0 / 12.0)) - 1900) * 12.3685).floor

    adate = nt1 = meanphase(adate,  k1)

    while true
        adate += SYNMONTH
        k2 = k1 + 1
        nt2 = meanphase(adate, k2)
        break if nt1 <= sdate && nt2 > sdate
        nt1 = nt2
        k1 = k2  
    end

    return PhaseHunt.new(*[
                         truephase(k1, 0.0),
                         truephase(k1, 0.25),
                         truephase(k1, 0.5),
                         truephase(k1, 0.75),
                         truephase(k2, 0.0)
    ].map {
        |_| _.new_offset(date.offset)
    })
end