.stars { background: #000 url(‘https://media.vo.rs/s5.png') repeat top center; z-index: 0; }
class MoonService {
/**
* @param a
*
* @return mixed
*/
static fixangle(a) {
return (a - 360 * Math.floor(a / 360));
}
// KEPLER -- Solve the equation of Kepler.
/**
* @param m
* @param ecc
*
* @return float
*/
static kepler(m, ecc) {
let epsilon = 0.000001;
let e = m = MoonService.deg2rad(m);
let delta = 0;
do {
delta = e - ecc * Math.sin(e) - m;
e -= delta / (1 - ecc * Math.cos(e));
} while (Math.abs(delta) > epsilon);
return e;
}
static deg2rad(angle) {
return angle * 0.017453292519943295 // (angle / 180) * Math.PI;
}
/* Calculates time of the mean new Moon for a given
base date. This argument K to this function is the
precomputed synodic month index, given by:
K = (year - 1900) * 12.3685
where year is expressed as a year and fractional year.
*/
/**
* @return float
*/
get phaser() {
return this.phase;
}
set phaser(x){
this.phase = x;
}
/* Given a K value used to determine the mean phase of
the new moon, and a phase selector (0.0, 0.25, 0.5,
0.75), obtain the true, corrected phase time.
*/
/**
* @return float
*/
get illumination() {
return this.illum;
}
/* Find time of phases of the moon which surround the current date.
Five phases are found, starting and
ending with the new moons which bound the current lunation.
*/
/**
* @return float
*/
get ager() {
return this.age;
}
/* Convert UNIX timestamp to astronomical Julian time (i.e. Julian date plus day fraction). */
/**
* @return float
*/
get distance() {
return this.dist;
}
/**
* @return float
*/
diameter() {
return this.angdia;
}
/* functions for accessing results */
/**
* @return float
*/
sundistance() {
return this.sundist;
}
/**
* @return float
*/
sundiameter() {
return this.sunangdia;
}
/**
* @return mixed
*/
new_moon() {
return this.get_phase(0);
}
/**
* @param $n
*
* @return mixed
*/
get_phase($n) {
if (NULL === this.quarters) {
this.phasehunt();
}
return this.quarters[$n];
}
phasehunt() {
$sdate = MoonService.utctojulian(this.timestamp);
$adate = $sdate - 45;
$ats = this.timestamp - 86400 * 45;
console.log($ats);
$yy = MoonService.gmdate('Y', $ats);
$mm = MoonService.gmdate('n', $ats);
$k1 = Math.floor(($yy + (($mm - 1) * (1 / 12)) - 1900) * 12.3685);
$adate = $nt1 = this.meanphase($adate, $k1);
while (true) {
$adate += this.synmonth;
$k2 = $k1 + 1;
$nt2 = this.meanphase($adate, $k2);
// if nt2 is close to sdate, then mean phase isn't good enough, we have to be more accurate
if (abs($nt2 - $sdate) < 0.5) {
$nt2 = this.truephase($k2, 0.0);
}
if ($nt1 <= $sdate && $nt2 > $sdate) {
break;
}
$nt1 = $nt2;
$k1 = $k2;
}
// results in Julian dates
$data = [
this.truephase($k1, 0.0),
this.truephase($k1, 0.25),
this.truephase($k1, 0.5),
this.truephase($k1, 0.75),
this.truephase($k2, 0.0)
];
this.quarters = [];
$data.forEach(function (v, index) {
this.quarters.push((v - 2440587.5) * 86400)
});
}
static gmdate(format, timestamp) {
console.log('FIX ME!!!')
return 500;
}
/**
* @param $ts
*
* @return float
*/
/**
* @param $ts
*
* @return float
*/
static utctojulian($ts) {
return $ts / 86400 + 2440587.5;
}
/**
* @param sdate
* @param k
*
* @return float
*/
meanphase(sdate, k) {
// Time in Julian centuries from 1900 January 0.5
let t = (sdate - 2415020.0) / 36525;
let t2 = t * t;
let t3 = t2 * t;
let nt1 = 2415020.75933 + this.synmonth * k
+ 0.0001178 * t2
- 0.000000155 * t3
+ 0.00033 * Math.sin(MoonService.deg2rad(166.56 + 132.87 * t - 0.009173 * t2));
return $nt1;
}
/**
* @param k
* @param phase
*
* @return bool|float
*/
truephase(k, phase) {
let apcor = false;
k += $phase; // Add phase to new moon time
let t = k / 1236.85; // Time in Julian centuries from 1900 January 0.5
let t2 = t * t; // Square for frequent use
let t3 = t2 * t; // Cube for frequent use
let pt = 2415020.75933 // Mean time of phase
+ this.synmonth * k
+ 0.0001178 * t2
- 0.000000155 * t3
+ 0.00033 * Math.sin(MoonService.deg2rad(166.56 + 132.87 * t - 0.009173 * t2));
let m = 359.2242 + 29.10535608 * k - 0.0000333 * t2 - 0.00000347 * t3; // Sun's mean anomaly
let mprime = 306.0253 + 385.81691806 * k + 0.0107306 * t2 + 0.00001236 * t3; // Moon's mean anomaly
let f = 21.2964 + 390.67050646 * k - 0.0016528 * t2 - 0.00000239 * t3; // Moon's argument of latitude
if (phase < 0.01 || Math.abs(phase - 0.5) < 0.01) {
// Corrections for New and Full Moon
pt += (0.1734 - 0.000393 * t) * Math.sin(MoonService.deg2rad(m))
+ 0.0021 * Math.sin(MoonService.deg2rad(2 * m))
- 0.4068 * Math.sin(MoonService.deg2rad(mprime))
+ 0.0161 * Math.sin(MoonService.deg2rad(2 * mprime))
- 0.0004 * Math.sin(MoonService.deg2rad(3 * mprime))
+ 0.0104 * Math.sin(MoonService.deg2rad(2 * f))
- 0.0051 * Math.sin(MoonService.deg2rad(m + mprime))
- 0.0074 * Math.sin(MoonService.deg2rad(m - mprime))
+ 0.0004 * Math.sin(MoonService.deg2rad(2 * f + m))
- 0.0004 * Math.sin(MoonService.deg2rad(2 * f - m))
- 0.0006 * Math.sin(MoonService.deg2rad(2 * f + mprime))
+ 0.0010 * Math.sin(MoonService.deg2rad(2 * f - mprime))
+ 0.0005 * Math.sin(MoonService.deg2rad(m + 2 * mprime));
apcor = true;
} else {
if (Math.abs(phase - 0.25) < 0.01 || Math.abs(phase - 0.75) < 0.01) {
pt += (0.1721 - 0.0004 * t) * Math.sin(MoonService.deg2rad(m))
+ 0.0021 * Math.sin(MoonService.deg2rad(2 * m))
- 0.6280 * Math.sin(MoonService.deg2rad(mprime))
+ 0.0089 * Math.sin(MoonService.deg2rad(2 * mprime))
- 0.0004 * Math.sin(MoonService.deg2rad(3 * mprime))
+ 0.0079 * Math.sin(MoonService.deg2rad(2 * f))
- 0.0119 * Math.sin(MoonService.deg2rad(m + mprime))
- 0.0047 * Math.sin(MoonService.deg2rad(m - mprime))
+ 0.0003 * Math.sin(MoonService.deg2rad(2 * f + m))
- 0.0004 * Math.sin(MoonService.deg2rad(2 * f - m))
- 0.0006 * Math.sin(MoonService.deg2rad(2 * f + mprime))
+ 0.0021 * Math.sin(MoonService.deg2rad(2 * f - mprime))
+ 0.0003 * Math.sin(MoonService.deg2rad(m + 2 * mprime))
+ 0.0004 * Math.sin(MoonService.deg2rad(m - 2 * mprime))
- 0.0003 * Math.sin(MoonService.deg2rad(2 * m + mprime));
if (phase < 0.5) // First quarter correction
{
pt += 0.0028 - 0.0004 * Math.cos(MoonService.deg2rad(m)) + 0.0003 * Math.cos(MoonService.deg2rad(mprime));
} else // Last quarter correction
{
pt += -0.0028 + 0.0004 * Math.cos(MoonService.deg2rad(m)) - 0.0003 * Math.cos(MoonService.deg2rad(mprime));
}
apcor = true;
}
}
if (!apcor) // function was called with an invalid phase selector
{
return false;
}
return pt;
}
/**
* @return mixed
*/
first_quarter() {
return this.get_phase(1);
}
/**
* @return mixed
*/
full_moon() {
return this.get_phase(2);
}
/**
* @return mixed
*/
last_quarter() {
return this.get_phase(3);
}
/**
* @return mixed
*/
next_new_moon() {
return this.get_phase(4);
}
constructor(pdate = null) {
if (null === pdate) {
pdate = Math.round((new Date()).getTime() / 1000);
}
//set stuff because ecmascript 6 is ... special...
this.timestamp = 0;
this.phase = 0;
this.illum = 0;
this.age = 0;
this.dist = 0;
this.angdia = 0;
this.sundist = 0;
this.sunangdia = 0;
this.synmonth = 0;
this.quarters = 0;
/* Astronomical constants */
this.epoch = 2444238.5; // 1980 January 0.0
/* Constants defining the Sun's apparent orbit */
this.elonge = 278.833540; // Ecliptic longitude of the Sun at epoch 1980.0
this.elongp = 282.596403; // Ecliptic longitude of the Sun at perigee
this.eccent = 0.016718; // Eccentricity of Earth's orbit
this.sunsmax = 1.495985e8; // Semi-major axis of Earth's orbit, km
this.sunangsiz = 0.533128; // Sun's angular size, degrees, at semi-major axis distance
/* Elements of the Moon's orbit, epoch 1980.0 */
this.mmlong = 64.975464; // Moon's mean longitude at the epoch
this.mmlongp = 349.383063; // Mean longitude of the perigee at the epoch
this.mlnode = 151.950429; // Mean longitude of the node at the epoch
this.minc = 5.145396; // Inclination of the Moon's orbit
this.mecc = 0.054900; // Eccentricity of the Moon's orbit
this.mangsiz = 0.5181; // Moon's angular size at distance a from Earth
this.msmax = 384401; // Semi-major axis of Moon's orbit in km
this.mparallax = 0.9507; // Parallax at distance a from Earth
this.synmonth = 29.53058868; // Synodic month (new Moon to new Moon)
this.lunatbase = 2423436.0; // Base date for E. W. Brown's numbered series of lunations (1923 January 16)
/* Properties of the Earth */
// $earthrad = 6378.16; // Radius of Earth in kilometres
// $PI = 3.14159265358979323846; // Assume not near black hole
this.timestamp = pdate;
// pdate is coming in as a UNIX timestamp, so convert it to Julian
pdate = pdate / 86400 + 2440587.5;
/* Calculation of the Sun's position */
this.day = pdate - this.epoch; // Date within epoch
this.N = MoonService.fixangle((360 / 365.2422) * this.day); // Mean anomaly of the Sun
this.M = MoonService.fixangle(this.N + this.elonge - this.elongp); // Convert from perigee coordinates to epoch 1980.0
this.Ec = MoonService.kepler(this.M, this.eccent); // Solve equation of Kepler
this.Ec = Math.sqrt((1 + this.eccent) / (1 - this.eccent)) * Math.tan(this.Ec / 2);
this.Ec = 2 * MoonService.rad2deg(Math.atan(this.Ec)); // True anomaly
this.Lambdasun = MoonService.fixangle(this.Ec + this.elongp); // Sun's geocentric ecliptic longitude
this.F = ((1 + this.eccent * Math.cos(MoonService.deg2rad(this.Ec))) / (1 - this.eccent * this.eccent)); // Orbital distance factor
this.SunDist = this.sunsmax / this.F; // Distance to Sun in km
this.SunAng = this.F * this.sunangsiz; // Sun's angular size in degrees
/* Calculation of the Moon's position */
this.ml = MoonService.fixangle(13.1763966 * this.day + this.mmlong); // Moon's mean longitude
this.MM = MoonService.fixangle(this.ml - 0.1114041 * this.day - this.mmlongp); // Moon's mean anomaly
this.MN = MoonService.fixangle(this.mlnode - 0.0529539 * this.day); // Moon's ascending node mean longitude
this.Ev = 1.2739 * Math.sin(MoonService.deg2rad(2 * (this.ml - this.Lambdasun) - this.MM)); // Evection
this.Ae = 0.1858 * Math.sin(MoonService.deg2rad(this.M)); // Annual equation
this.A3 = 0.37 * Math.sin(MoonService.deg2rad(this.M)); // Correction term
this.MmP = this.MM + this.Ev - this.Ae - this.A3; // Corrected anomaly
this.mEc = 6.2886 * Math.sin(MoonService.deg2rad(this.MmP)); // Correction for the equation of the centre
this.A4 = 0.214 * Math.sin(MoonService.deg2rad(2 * this.MmP)); // Another correction term
this.lP = this.ml + this.Ev + this.mEc - this.Ae + this.A4; // Corrected longitude
this.V = 0.6583 * Math.sin(MoonService.deg2rad(2 * (this.lP - this.Lambdasun))); // Variation
this.lPP = this.lP + this.V; // True longitude
this.NP = this.MN - 0.16 * Math.sin(MoonService.deg2rad(this.M)); // Corrected longitude of the node
this.y = Math.sin(MoonService.deg2rad(this.lPP - this.NP)) * Math.cos(MoonService.deg2rad(this.minc)); // Y inclination coordinate
this.x = Math.cos(MoonService.deg2rad(this.lPP - this.NP)); // X inclination coordinate
this.Lambdamoon = MoonService.rad2deg(Math.atan2(this.y, this.x)) + this.NP; // Ecliptic longitude
this.BetaM = MoonService.rad2deg(Math.asin(Math.sin(MoonService.deg2rad(this.lPP - this.NP)) * Math.sin(MoonService.deg2rad(this.minc)))); // Ecliptic latitude
/* Calculation of the phase of the Moon */
this.MoonAge = this.lPP - this.Lambdasun; // Age of the Moon in degrees
this.MoonPhase = (1 - Math.cos(MoonService.deg2rad(this.MoonAge))) / 2; // Phase of the Moon
// Distance of moon from the centre of the Earth
this.MoonDist = (this.msmax * (1 - this.mecc * this.mecc)) / (1 + this.mecc * Math.cos(MoonService.deg2rad(this.MmP + this.mEc)));
this.MoonDFrac = this.MoonDist / this.msmax;
this.MoonAng = this.mangsiz / this.MoonDFrac; // Moon's angular diameter
// $MoonPar = $mparallax / $MoonDFrac; // Moon's parallax
// store results
this.phase = MoonService.fixangle(this.MoonAge) / 360; // Phase (0 to 1)
this.illum = this.MoonPhase; // Illuminated fraction (0 to 1)
this.age = this.synmonth * this.phase; // Age of moon (days)
this.dist = this.MoonDist; // Distance (kilometres)
this.angdia = this.MoonAng; // Angular diameter (degrees)
this.sundist = this.SunDist; // Distance to Sun (kilometres)
this.sunangdia = this.SunAng;
}
static rad2deg(angle) {
return angle * 57.29577951308232 // angle / Math.PI * 180
}
static drawmoon(inPhase, inIsWaxing){
let phase = inPhase;
let isWaxing = inIsWaxing;
let shadowColour = 'black';
let lightColour = 'yellow';
let outerColour = '';
let innerColour = '';
let diameter = 100;
let earthshine = 0.1;
let blur = 3;
if (phase < 0.5) {
outerColour = lightColour;
innerColour = shadowColour;
if (isWaxing) {
phase *= -1;
}
} else {
outerColour = shadowColour;
innerColour = lightColour;
phase = 1 - phase;
if (!isWaxing) {
phase *= -1;
}
}
let semiPhase = phase * 2;
let outerDiameter = diameter;
let absPhase = Math.abs(semiPhase);
let n = ((1 - absPhase) * outerDiameter / 2) | 0.01;
if (n === 0){
n ++;
}
let innerRadius = n / 2 + outerDiameter * outerDiameter / (8 * n);
let innerDiameter = innerRadius * 2;
let innerOffset = semiPhase > 0 ? (outerDiameter / 2 - n) : (-2 * innerRadius + outerDiameter / 2 + n);
let outerCss = 'position:relative;';
outerCss += 'height:' + outerDiameter + 'px;';
outerCss += 'width:' + outerDiameter + 'px;';
outerCss += 'border:1px solid black;';
outerCss += 'background-color:' + outerColour +';';
outerCss += 'border-radius:' + (outerDiameter/2) + 'px;';
outerCss += 'overflow:hidden;';
let blurredDiameter = innerDiameter - blur;
let blurredOffset = innerOffset + blur/2;
let innerCss = 'position:relative;';
innerCss += 'background-color:' + innerColour +';';
innerCss += 'border-radius:'+ (blurredDiameter/2) + 'px;';
innerCss += 'height:'+ blurredDiameter + 'px;';
innerCss += 'width:' + blurredDiameter + 'px;';
innerCss += 'left:'+blurredOffset+ 'px;';
innerCss += 'top:'+ ((outerDiameter-blurredDiameter)/2) + 'px;';
innerCss += 'box-shadow: 0px 0px ' + blur + 'px ' + blur + 'px ' + innerColour +';';
innerCss += 'opacity:' + (1 - earthshine)+';';
return '<div style="' + outerCss +'"><div style="' + innerCss + '"></div></div>';
}
}
/* * phase(): the terminator phase angle as a fraction of a full circle (i.e., 0 to 1). Both 0 and 1 correspond to a New Moon, and 0.5 corresponds to a Full Moon. illumination(): the illuminated fraction of the Moon (0 = New, 1 = Full). age(): the age of the Moon, in days. distance(): the distance of the Moon from the centre of the Earth (kilometres). diameter(): the angular diameter subtended by the Moon as seen by an observer at the centre of the Earth (radians). sundistance(): the distance to the Sun (kilometres). sundiameter(): the angular diameter subtended by the Sun as seen by an observer at the centre of the Earth (radians). new_moon(): the time of the last New Moon (UNIX timestamp). next_new_moon(): the time of the next New Moon (UNIX timestamp). full_moon(): the time of the Full Moon in the current lunar cycle (UNIX timestamp). first_quarter(): the time of the first quarter in the current lunar cycle (UNIX timestamp). last_quarter(): the time of the last quarter in the current lunar cycle (UNIX timestamp). */
/*
- is {{($moonService->phase() < 0.5) ? ‘waxing’ : ‘waning’}} and is currently {{round(($moonService->illumination()*100),2)}} % illuminated.
- is currently @include(‘convertible’,[‘unit’=>‘km’,’number’=>((floor($moonService->distance())))]) from earth centre.
- will be new on {{\Carbon\Carbon::createFromTimestamp($moonService->next_new_moon())->format(’l, jS \of F ‘)}} .
- will be full moon on on {{\Carbon\Carbon::createFromTimestamp($moonService->full_moon())->format(’l, jS \of F ‘)}} .
- is currently @include(‘convertible’,[‘unit’=>‘km’,’number’=>((floor($moonService->sundistance())))]) from earth centre.
- */
jQuery(function() { ms = new MoonService(); jQuery("#moondraw").html(MoonService.drawmoon(ms.illumination, (ms.phase < 0.5))); });