Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

CoffeeScript calculates the phase of the moon


May 09, 2021 CoffeeScript


Table of contents


Calculate the phase of the moon

Problem

You want to find out the phase of the moon.

Solution

The following code provides a lunar phase calculation scheme for calculating the given date:

# moonPhase.coffee

# Moon-phase calculator
# Roger W. Sinnott, Sky & Telescope, June 16, 2006
# http://www.skyandtelescope.com/observing/objects/javascript/moon_phases
#
# Translated to CoffeeScript by Mike Hatfield @WebCoding4Fun

proper_ang = (big) ->
    tmp = 0
    if big > 0
        tmp = big / 360.0
        tmp = (tmp - (~~tmp)) * 360.0
    else
        tmp = Math.ceil(Math.abs(big / 360.0))
        tmp = big + tmp * 360.0

    tmp

jdn = (date) ->  
    month = date.getMonth()
    day = date.getDate()
    year = date.getFullYear()
    zone = date.getTimezoneOffset() / 1440

    mm = month
    dd = day
    yy = year

    yyy = yy
    mmm = mm
    if mm < 3
        yyy = yyy - 1
        mmm = mm + 12

    day = dd + zone + 0.5
    a = ~~( yyy / 100 )
    b = 2 - a + ~~( a / 4 )
    jd = ~~( 365.25 * yyy ) + ~~( 30.6001 * ( mmm+ 1 ) ) + day + 1720994.5
    jd + b if jd > 2299160.4999999

moonElong = (jd) ->
    dr    = Math.PI / 180
    rd    = 1 / dr
    meeDT = Math.pow((jd - 2382148), 2) / (41048480 * 86400)
    meeT  = (jd + meeDT - 2451545.0) / 36525
    meeT2 = Math.pow(meeT, 2)
    meeT3 = Math.pow(meeT, 3)
    meeD  = 297.85 + (445267.1115 * meeT) - (0.0016300 * meeT2) + (meeT3 / 545868)
    meeD  = (proper_ang meeD) * dr
    meeM1 = 134.96 + (477198.8676 * meeT) + (0.0089970 * meeT2) + (meeT3 / 69699)
    meeM1 = (proper_ang meeM1) * dr
    meeM  = 357.53 + (35999.0503 * meeT)
    meeM  = (proper_ang meeM) * dr

    elong = meeD * rd + 6.29 * Math.sin( meeM1 )
    elong = elong     - 2.10 * Math.sin( meeM )
    elong = elong     + 1.27 * Math.sin( 2*meeD - meeM1 )
    elong = elong     + 0.66 * Math.sin( 2*meeD )
    elong = proper_ang elong
    elong = Math.round elong

    moonNum = ( ( elong + 6.43 ) / 360 ) * 28
    moonNum = ~~( moonNum )

    if moonNum is 28 then 0 else moonNum

getMoonPhase = (age) ->
    moonPhase = "new Moon"
    moonPhase = "first quarter" if age > 3 and age < 11 
    moonPhase = "full Moon"     if age > 10 and age < 18
    moonPhase = "last quarter"  if age > 17 and age < 25

    if ((age is 1) or (age is 8) or (age is 15) or (age is 22))
        moonPhase = "1 day past " + moonPhase

    if ((age is 2) or (age is 9) or (age is 16) or (age is 23))
        moonPhase = "2 days past " + moonPhase

    if ((age is 3) or (age is 1) or (age is 17) or (age is 24))
        moonPhase = "3 days past " + moonPhase

    if ((age is 4) or (age is 11) or (age is 18) or (age is 25))
        moonPhase = "3 days before " + moonPhase

    if ((age is 5) or (age is 12) or (age is 19) or (age is 26))
        moonPhase = "2 days before " + moonPhase

    if ((age is 6) or (age is 13) or (age is 20) or (age is 27))
        moonPhase = "1 day before " + moonPhase

    moonPhase

MoonPhase = exports? and exports or @MoonPhase = {}

class MoonPhase.Calculator
    getMoonDays: (date) ->
        jd = jdn date 
        moonElong jd

    getMoonPhase: (date) ->      
        jd = jdn date 
        getMoonPhase( moonElong jd )

Discuss

There are two ways this code shows a lunar phase calculator object. C alculator-getmoonphase returns the lunar phase of a date represented in text.

This can be used in browsers and .js browsers.

$ node
> var MoonPhase = require('./moonPhase.js');
 undefined
> var calc = new MoonPhase.Calculator();
 undefined
> calc.getMoonPhase(new Date());
 'full moon'
> calc.getMoonPhase(new Date(1972, 6, 30));
 '3 days before last quarter'