Skip to content
Snippets Groups Projects
phases.js 8.2 KiB
Newer Older

	Copyright (C) 2024 Samuel Jimenez <therealsamueljimenez@gmail.com>

	JavaScript version
	Copyright 2011 Glad Deschrijver <glad.deschrijver@gmail.com>
	with the same license as below.
*/
/*
    This file is part of the kmoon application with explicit permission by the author
    Copyright 1996 Christopher Osburn <chris@speakeasy.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 3 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, see <http://www.gnu.org/licenses/>.
*/



.pragma library
// var LUNATION_OFFSET = 953//lunation of LUNATION_EPOCH in Brown Lunation Number
const LUNATION_OFFSET = 0//lunation of LUNATION_EPOCH using Meeus's Lunation Number
// LUNATION_EPOCH
//date of first new moon of 2000 (2000-01-06 18:14:00)
const LUNATION_EPOCH_MS = 947182440000// number of milliseconds between 1970-01-01 00:00:00 and epoch
const LUNATION_EPOCH_JD = 2451550.259722222//epoch in JD

// milliseconds in one day
const MS_PER_DAY = 86400000

// average lunation duration
const LUNATION_CYCLE_DURATION = 29.530588853
const LUNATION_CYCLE_DURATION_MS = 2551442877

/* DatefromJD(int) => Date
 *
 * convert a Julian Date to a JavaScript Date object
 */
function DatefromJD(jd)
	// convert to epoch time, then instantiate Date
	return new Date((jd - 2440587.5) * MS_PER_DAY)
/* radian(x) : x in Reals -> [-2 PI, 2 PI]
 *
 * convert x to radians from degrees
 */
function radian(x)
	return ((x % 360.0) * Math.PI/180)
/* getLunation(Date time) => int lunation
 *
 * Given time, returns number of lunation time falls in
 *
 */
function getLunation(time)

	// find lunation
	var lunation = Math.floor((time.getTime() - LUNATION_EPOCH_MS) / LUNATION_CYCLE_DURATION_MS) + LUNATION_OFFSET

	//verify result
	var currNew = DatefromJD(moonphasebylunation(lunation, 0))
	var nextNew
	while (currNew > time) {
		// math is off: log result
		console.log("Lunation number", lunation, "too high for", time.toISOString())
		lunation--
		currNew = DatefromJD(moonphasebylunation(lunation, 0))
	}
	nextNew = DatefromJD(moonphasebylunation(lunation + 1, 0))
	while (nextNew < time) {
		// math is off: log result
		console.log("Lunation number", lunation, "too low for", time.toISOString())
		lunation++
		nextNew = DatefromJD(moonphasebylunation(lunation + 1, 0))
	}

	return lunation;
}

/*
** moonphase.c
** 1996/02/11
**
** Copyright 1996, Christopher Osburn, Lunar Outreach Services,
** Non-commercial usage license granted to all.
**
** calculate phase of the moon per Meeus Ch. 47 (ISBN:9780943396613)
**
** Parameters:
**    int lun:  phase parameter.  This is the number of lunations
**              since the New Moon of 2000 January 6.
**
**    int phi:  another phase parameter, selecting the phase of the
**              moon.  0 = New, 1 = First Qtr, 2 = Full, 3 = Last Qtr
**
** Return:  Apparent JD of the needed phase
*/
function moonphasebylunation(lun, phi)
	var k = lun - LUNATION_OFFSET + phi / 4.0

	// Julian Ephemeris Day of phase event
	var JDE

	// time parameter, Julian Centuries since LUNATION_EPOCH (47.3)
	var T = k / 1236.85

	// Eccentricity anomaly (45.6)
	var E = 1.0 + T * (-0.002516 + -0.0000074 * T)

	// Sun's mean anomaly (47.4)
	var M = radian(2.5534 + 29.10535669 * k
		+ T * T * (-0.0000218 + -0.00000011 * T))
	// Moon's mean anomaly (47.5)
	var M1 = radian(201.5643 + 385.81693528 * k
		+ T * T * (0.0107438 + T * (0.00001239 + -0.000000058 * T)))
	// Moon's argument of latitude (47.6)
	var F = radian(160.7108 + 390.67050274 * k
		+ T * T * (-0.0016341 * T * (-0.00000227 + 0.000000011 * T)))
	// Moon's longitude of ascending node (47.7)
	var O = radian(124.7746 - 1.56375580 * k
		+ T * T * (0.0020691 + 0.00000215 * T))
	var planetaryArguments =
		0.000325 * Math.sin(radian(299.77 +  0.107408 * k - 0.009173 * T * T))
		+ 0.000165 * Math.sin(radian(251.88 +  0.016321 * k))
		+ 0.000164 * Math.sin(radian(251.83 + 26.651886 * k))
		+ 0.000126 * Math.sin(radian(349.42 + 36.412478 * k))
		+ 0.000110 * Math.sin(radian(84.66  + 18.206239 * k))
		+ 0.000062 * Math.sin(radian(141.74 + 53.303771 * k))
		+ 0.000060 * Math.sin(radian(207.14 +  2.453732 * k))
		+ 0.000056 * Math.sin(radian(154.84 +  7.306860 * k))
		+ 0.000047 * Math.sin(radian(34.52  + 27.261239 * k))
		+ 0.000042 * Math.sin(radian(207.19 +  0.121824 * k))
		+ 0.000040 * Math.sin(radian(291.34 +  1.844379 * k))
		+ 0.000037 * Math.sin(radian(161.72 + 24.198154 * k))
		+ 0.000035 * Math.sin(radian(239.56 + 25.513099 * k))
		+ 0.000023 * Math.sin(radian(331.55 +  3.592518 * k))

	// added correction for quarter phases
	var W

	//group First, Last Quarter
	var orbitalCorrectionIndex = 2-Math.abs(2-phi)

	var orbitalCorrectionCoeffArray = [
		// New Moon
		[+0.17241, +0.00208, +0.00004, -0.40720, +0.00739, -0.00514, +0.00000, -0.00007, -0.00111, -0.00057, +0.01608, +0.01039, -0.00024, +0.00056, -0.00042, -0.00002, +0.00002, +0.00038, +0.00042, -0.00017, +0.00004, +0.00003, -0.00002, +0.00003, +0.00003, -0.00003,],
		// First Quarter, Last Quarter,
		[+0.17172, +0.00204, +0.00003, -0.62801, +0.00454, -0.01183, +0.00004, -0.00028* E * E, -0.00180, -0.00070, +0.00862, +0.00804, -0.00034, +0.00027, -0.00040, -0.00002, +0.00000, +0.00032, +0.00032, -0.00017, +0.00002, +0.00004, -0.00005, +0.00002, +0.00003, -0.00004,],
		// New Moon
		[+0.17302, +0.00209, +0.00004, -0.40614, +0.00734, -0.00515, +0.00000, -0.00007, -0.00111, -0.00057, +0.01614, +0.01043, -0.00024, +0.00056, -0.00042, -0.00002, +0.00002, +0.00038, +0.00042, -0.00017, +0.00004, +0.00003, -0.00002, +0.00003, +0.00003, -0.00003,]]

	var orbitalCorrectionCoeffs =
		orbitalCorrectionCoeffArray[orbitalCorrectionIndex]

	var orbitalApproximation =
		  orbitalCorrectionCoeffs[0] * E     * Math.sin(M)
		+ orbitalCorrectionCoeffs[1] * E * E * Math.sin(2.0 * M)
		+ orbitalCorrectionCoeffs[2]         * Math.sin(3.0 * M)
		+ orbitalCorrectionCoeffs[3]         * Math.sin(M1)
		+ orbitalCorrectionCoeffs[4] * E     * Math.sin(M1 - M)
		+ orbitalCorrectionCoeffs[5] * E     * Math.sin(M1 + M)
		+ orbitalCorrectionCoeffs[6]         * Math.sin(M1 - 2.0 * M)
		+ orbitalCorrectionCoeffs[7]         * Math.sin(M1 + 2.0 * M)
		+ orbitalCorrectionCoeffs[8]         * Math.sin(M1 - 2.0 * F)
		+ orbitalCorrectionCoeffs[9]         * Math.sin(M1 + 2.0 * F)
		+ orbitalCorrectionCoeffs[10]        * Math.sin(2.0 * M1)
		+ orbitalCorrectionCoeffs[11]        * Math.sin(2.0 * F)
		+ orbitalCorrectionCoeffs[12] * E    * Math.sin(2.0 * M1 - M)
		+ orbitalCorrectionCoeffs[13] * E    * Math.sin(2.0 * M1 + M)
		+ orbitalCorrectionCoeffs[14]        * Math.sin(3.0 * M1)
		+ orbitalCorrectionCoeffs[15]        * Math.sin(3.0 * M1 + M)
		+ orbitalCorrectionCoeffs[16]        * Math.sin(4.0 * M1)
		+ orbitalCorrectionCoeffs[17] * E    * Math.sin(M - 2.0 * F)
		+ orbitalCorrectionCoeffs[18] * E    * Math.sin(M + 2.0 * F)
		+ orbitalCorrectionCoeffs[19]        * Math.sin(O)
		+ orbitalCorrectionCoeffs[20]        * Math.sin(2.0 * M1 - 2.0 * F)
		+ orbitalCorrectionCoeffs[21]        * Math.sin(2.0 * M1 + 2.0 * F)
		+ orbitalCorrectionCoeffs[22]        * Math.sin(M1 - M - 2.0 * F)
		+ orbitalCorrectionCoeffs[23]        * Math.sin(M1 - M + 2.0 * F)
		+ orbitalCorrectionCoeffs[24]        * Math.sin(M1 + M - 2.0 * F)
		+ orbitalCorrectionCoeffs[25]        * Math.sin(M1 + M + 2.0 * F)


	// this is the first approximation.  all else is for style points! (47.1)
	JDE = LUNATION_EPOCH_JD + (LUNATION_CYCLE_DURATION * k)
	    + T * T * (0.0001337 + T * (-0.000000150 + 0.00000000073 * T))

		+ orbitalApproximation
		+ planetaryArguments

	// extra correction for quarter phases
	if (orbitalCorrectionIndex == 1) {
		W = 0.00306
		- 0.00038 * E * Math.cos(M)
		+ 0.00026 * Math.cos(M1)
		- 0.00002 * Math.cos(M1 - M)
		+ 0.00002 * Math.cos(M1 + M)
		+ 0.00002 * Math.cos(2.0 * F)
		if (phi == 3){
			W = -W
		}
		JDE += W
	}

	return JDE