added getMoon functions

This commit is contained in:
Ali Camarata 2023-11-13 21:34:55 +07:00
parent aacc8ff81b
commit c1814c2783
9 changed files with 172 additions and 63 deletions

View file

@ -14,3 +14,7 @@ All notable changes to this project will be documented in this file.
- Moved timezone to main args and changed default behavior (major)
- Updated test cases and readme to reflect new usage (minor)
## [1.3.0] - 2013-11-13
- Major updates to getMoon with own functions

View file

@ -1,65 +1,35 @@
const { getEarthSunDistance } = require('./getEarthSunDistance');
const { getMoonPhase } = require('./getMoonPhase');
const { getMoonPosition } = require('./getMoonPosition');
const { getMoonIllumination } = require('./getMoonIllumination');
const { getMoonVisibility } = require('./getMoonVisibility');
function getMoon(date, accurate = true) {
const PI = Math.PI;
const rad = PI / 180;
const e = rad * 23.4397; // obliquity of the Earth
/**
* Calculates detailed moon visibility information.
*
* @param {Date} date - The date for which to calculate moon data.
* @param {number} [latitude=0] - Observer's latitude in decimal degrees.
* @param {number} [longitude=0] - Observer's longitude in decimal degrees.
* @param {number} [elevation=50] - Observer's elevation in meters above sea level. Default is 50 meters.
* @param {number} [temp=15] - Ambient temperature in degrees Celsius. Default is 15°C.
* @param {number} [pressure=1013.25] - Atmospheric pressure in hPa. Default is 1013.25 hPa (average sea level pressure).
* @param {number} [humidity=50] - Humidity in percentage. Default is 50%.
* @param {number} [clouds=0] - Cloudiness in percentage. Default is 0% (clear sky).
* @returns {Object} An object containing moon details: phase, position, illumination, and visibility.
*/
function getMoon(date, latitude = 0, longitude = 0, elevation = 50, temp = 15, pressure = 1013.25, humidity = 50, clouds = 0) {
const phase = getMoonPhase(date);
const position = getMoonPosition(date, latitude, longitude);
const illumination = getMoonIllumination(date);
function toDays(date) {
return (date - new Date(2000, 0, 1)) / 86400000;
}
function rightAscension(l, b) {
return Math.atan2(Math.sin(l) * Math.cos(e) - Math.tan(b) * Math.sin(e), Math.cos(l));
}
function declination(l, b) {
return Math.asin(Math.sin(b) * Math.cos(e) + Math.cos(b) * Math.sin(e) * Math.sin(l));
}
function sunCoords(d) {
const M = rad * (357.5291 + 0.98560028 * d);
const L = rad * (280.1470 + 360.9856235 * d) + (1.9148 - 0.004817 * d / 36525) * Math.sin(M) + 0.019993 - 0.000101 * d / 36525 * Math.cos(M);
return {
dec: declination(L, 0),
ra: rightAscension(L, 0)
};
}
function moonCoords(d) {
const L = rad * (218.316 + 13.176396 * d);
const M = rad * (134.963 + 13.064993 * d);
const F = rad * (93.272 + 13.229350 * d);
const l = L + rad * 6.289 * Math.sin(M); // Moon's mean longitude
const b = rad * 5.128 * Math.sin(F); // Moon's mean latitude
const dt = 385001 - 20905 * Math.cos(M); // Distance to the moon in km
return {
ra: rightAscension(l, b),
dec: declination(l, b),
dist: dt
};
}
const d = toDays(date);
const s = sunCoords(d);
const m = moonCoords(d);
// distance from Earth to Sun in km
const sdist = accurate ? getEarthSunDistance(date) : 149598000;
const phi = Math.acos(Math.sin(s.dec) * Math.sin(m.dec) + Math.cos(s.dec) * Math.cos(m.dec) * Math.cos(s.ra - m.ra));
const inc = Math.atan2(sdist * Math.sin(phi), m.dist - sdist * Math.cos(phi));
const angle = Math.atan2(Math.cos(s.dec) * Math.sin(s.ra - m.ra), Math.sin(s.dec) * Math.cos(m.dec) - Math.cos(s.dec) * Math.sin(m.dec) * Math.cos(s.ra - m.ra));
// Calculate visibility considering all factors
const visibility = getMoonVisibility(phase, position, illumination, elevation, temp, pressure, humidity, clouds);
return {
fraction: (1 + Math.cos(inc)) / 2,
phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI,
angle: angle
phase,
position,
illumination,
visibility
};
}
module.exports = {
getMoon
};
module.exports = { getMoon };

19
getMoonIllumination.js Normal file
View file

@ -0,0 +1,19 @@
// Import the SunCalc library
const suncalc = require('suncalc');
/**
* Calculates the moon's illumination for a given phase.
* @param {number} phase - The phase of the moon, from 0 (new moon) to 1 (full moon).
* @returns {Object} The moon's illumination details.
*/
function getMoonIllumination(date) {
const illuminationDetails = suncalc.getMoonIllumination(date);
return {
fraction: illuminationDetails.fraction, // Illuminated fraction of the moon; 0 = new moon, 1 = full moon
phase: illuminationDetails.phase, // Moon phase (0 to 1)
angle: illuminationDetails.angle // Angle in radians of the moon's bright limb
};
}
module.exports = { getMoonIllumination };

23
getMoonPhase.js Normal file
View file

@ -0,0 +1,23 @@
/**
* Calculates the current phase of the moon as a fraction.
* @param {Date} date - The date for which to calculate the moon phase.
* @returns {number} The moon phase as a fraction from 0 (new moon) to just under 1 (end of lunar cycle).
*/
function getMoonPhase(date) {
const synodicMonth = 29.530588861; // Average length of a synodic month in days
// Most recent known new moon: November 13, 2023, 03:27 AM
const knownNewMoon = new Date(Date.UTC(2023, 10, 13, 3, 27, 0));
// Convert both dates to the number of milliseconds since Unix Epoch and find the difference
const diffInMilliseconds = date - knownNewMoon;
// Convert the difference to days
const diffInDays = diffInMilliseconds / 1000 / 60 / 60 / 24;
// Calculate the phase as a fraction of the synodic month
const phase = (diffInDays % synodicMonth) / synodicMonth;
return phase;
}
module.exports = { getMoonPhase };

26
getMoonPosition.js Normal file
View file

@ -0,0 +1,26 @@
// Import the SunCalc library
const suncalc = require('suncalc');
/**
* Calculates detailed moon position information.
* @param {Date} date - The date and time for which to calculate the position.
* @param {number} latitude - Observer's latitude in decimal degrees.
* @param {number} longitude - Observer's longitude in decimal degrees.
* @returns {Object} The moon's position (azimuth, altitude), distance, and parallactic angle.
*/
function getMoonPosition(date, latitude, longitude) {
const moonPosition = suncalc.getMoonPosition(date, latitude, longitude);
// Convert azimuth and altitude from radians to degrees
const azimuth = moonPosition.azimuth * 180 / Math.PI;
const altitude = moonPosition.altitude * 180 / Math.PI;
return {
azimuth,
altitude,
distance: moonPosition.distance, // distance to moon in kilometers
parallacticAngle: moonPosition.parallacticAngle // parallactic angle in radians
};
}
module.exports = { getMoonPosition };

39
getMoonVisibility.js Normal file
View file

@ -0,0 +1,39 @@
/**
* Calculates detailed moon visibility information.
*
* @param {number} phase - The phase of the moon, from 0 (new moon) to 1 (next full moon).
* @param {Object} position - { azimuth, altitude, distance (km), parallacticAngle (radians) }
* @param {Object} illumination - { fraction, phase, angle }
* @param {number} [elevation=50] - Observer's elevation in meters above sea level. Default is 50 meters.
* @param {number} [temp=15] - Ambient temperature in degrees Celsius. Default is 15°C.
* @param {number} [pressure=1013.25] - Atmospheric pressure in hPa. Default is 1013.25 hPa (average sea level pressure).
* @param {number} [humidity=50] - Humidity in percentage. Default is 50%.
* @param {number} [clouds=0] - Cloudiness in percentage. Default is 0% (clear sky).
* @returns {Object} An object containing moon details: phase, position, illumination, and visibility.
*/
function getMoonVisibility(phase, position, illumination, elevation = 50, temp = 15, pressure = 1013.25, humidity = 50, clouds = 0) {
/** Placeholder Simplified Algorithm...
* This is a very simplified algorithm that is not intended to be precise or accurate.
* It is loosely based on average observations from astronomy journals and other sources.
* Using a window of earliest general visibility until near definite visibility is reached.
*/
const sMonth = 29.530588861;
const phaseHour = 1 / 24 / sMonth; // ~ 0.001410966333
const startVis = phaseHour * 15; // ~ 0.02116449584
const endofVis = phaseHour * 30; // ~ 0.04232899168
const visWindow = endofVis - startVis;
let visibility = 0;
if (phase > endofVis) {
visibility = 1;
} else if (phase > startVis) {
visibility = (phase - startVis) / visWindow;
}
return visibility;
}
module.exports = { getMoonVisibility };

12
package-lock.json generated
View file

@ -1,21 +1,27 @@
{
"name": "praycalc",
"version": "1.2.2",
"version": "1.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "praycalc",
"version": "1.2.2",
"version": "1.3.0",
"license": "ISC",
"dependencies": {
"nrel-spa": "^1.2.2"
"nrel-spa": "^1.2.2",
"suncalc": "^1.9.0"
}
},
"node_modules/nrel-spa": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/nrel-spa/-/nrel-spa-1.2.2.tgz",
"integrity": "sha512-2mycHd7PP0l+pPjPvrT6vr+PRHufCFOxsPcpaIOQ9GwhhfzgyTvSsLKPsqC6ZENNU65yq4PetT5XlWj3CoLjiQ=="
},
"node_modules/suncalc": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.9.0.tgz",
"integrity": "sha512-vMJ8Byp1uIPoj+wb9c1AdK4jpkSKVAywgHX0lqY7zt6+EWRRC3Z+0Ucfjy/0yxTVO1hwwchZe4uoFNqrIC24+A=="
}
}
}

View file

@ -1,6 +1,6 @@
{
"name": "praycalc",
"version": "1.2.3",
"version": "1.3.0",
"description": "Prayer times calculator using nrel-spa and custom formula for Fajr and Isha angles (as well as traditional static angle methods in the All function)",
"main": "index.js",
"scripts": {
@ -13,6 +13,7 @@
"author": "USF",
"license": "ISC",
"dependencies": {
"nrel-spa": "^1.2.2"
"nrel-spa": "^1.2.2",
"suncalc": "^1.9.0"
}
}

21
test-moon.js Normal file
View file

@ -0,0 +1,21 @@
const { getMoon } = require('./index');
const date = new Date();
/* NYC - minimum params
const city = "New York"
const lat = 40.7128;
const lng = -74.006;
*/
// Jakarta - all params
const city = "Jakarta"
const lat = -6.2088
const lng = 106.8456
// Get results
const get = getMoon(date, lat, lng);
// Print results
console.log(`\nTest: ${city} with current Date():\n`)
console.log("getMoon =", get, "\n");