From 41df68d78d6b6f5f0f52d6a65afb142fcf119a8c Mon Sep 17 00:00:00 2001 From: "J. T. L" Date: Fri, 21 Sep 2018 09:34:39 -0400 Subject: [PATCH] v1.4.1: bring astro deps in line with api --- .eslintignore | 1 + .eslintrc.json | 3 ++ .gitignore | 5 ++- index.js | 4 +- lib/astro.js | 40 +++++++++++++++++ lib/julian.js | 13 ++++++ lib/lunar.js | 87 +++++++++++++++++++------------------ lib/solar.js | 105 ++++++++++++++++++++++---------------------- package.json | 24 +++++------ test/astro.js | 2 +- test/lunar.js | 115 +++++++++++++++++++++++++------------------------ test/solar.js | 22 +++++----- 12 files changed, 240 insertions(+), 181 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 lib/astro.js create mode 100644 lib/julian.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..3b6ee58 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +/coverage diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..f2493ac --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "@darkskyapp" +} diff --git a/.gitignore b/.gitignore index 642271f..4f52d76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -node_modules/ -coverage/ \ No newline at end of file +/.nyc_output +/coverage +/node_modules diff --git a/index.js b/index.js index 963fe8c..7c62ee0 100644 --- a/index.js +++ b/index.js @@ -31,8 +31,8 @@ function julian_to_date(t) { // Greenwich mean sidereal time, accurate to ~1 second. // http://aa.usno.navy.mil/faq/docs/GAST.php -const GMST0 = 18.697374558 * Math.PI / 12, - GMST1 = 24.06570982441908 * Math.PI / 12; +const GMST0 = 18.697374558 * Math.PI / 12; +const GMST1 = 24.06570982441908 * Math.PI / 12; function greenwich_mean_sidereal_time(jt) { return GMST0 + GMST1 * jt; diff --git a/lib/astro.js b/lib/astro.js new file mode 100644 index 0000000..05f6c5a --- /dev/null +++ b/lib/astro.js @@ -0,0 +1,40 @@ +"use strict"; +const Lunar = require("./lunar"); +const Solar = require("./solar"); + +class Astro { + constructor() { + this.lunar_cache = null; + this.solar_cache = null; + } + + lunar(time_ms) { + if(this.lunar_cache === null) { + this.lunar_cache = new Map(); + } + + let lunar = this.lunar_cache.get(time_ms); + if(lunar === undefined) { + lunar = new Lunar(time_ms); + this.lunar_cache.set(time_ms, lunar); + } + + return lunar; + } + + solar(time_ms) { + if(this.solar_cache === null) { + this.solar_cache = new Map(); + } + + let solar = this.solar_cache.get(time_ms); + if(solar === undefined) { + solar = new Solar(time_ms); + this.solar_cache.set(time_ms, solar); + } + + return solar; + } +} + +module.exports = Astro; diff --git a/lib/julian.js b/lib/julian.js new file mode 100644 index 0000000..61af8b6 --- /dev/null +++ b/lib/julian.js @@ -0,0 +1,13 @@ +"use strict"; +const J1970 = -10957.5; + +function to(ms) { + return J1970 + ms / 86400000; +} + +function from(t) { + return (t - J1970) * 86400000; +} + +exports.to = to; +exports.from = from; diff --git a/lib/lunar.js b/lib/lunar.js index e823570..5a35676 100644 --- a/lib/lunar.js +++ b/lib/lunar.js @@ -6,17 +6,17 @@ function limit_angle(radians) { } /* Lunar position, accurate to about ten minutes between 1950-2050, -* http://aa.quae.nl/en/reken/hemelpositie.html#2 */ -const Q0 = 218.316 * Math.PI / 180, - Q1 = 13.176396 * Math.PI / 180, - M0 = 134.963 * Math.PI / 180, - M1 = 13.064993 * Math.PI / 180, - F0 = 93.272 * Math.PI / 180, - F1 = 13.229350 * Math.PI / 180, - L0 = 6.289 * Math.PI / 180, - L1 = 5.128 * Math.PI / 180, - E0 = 23.439 * Math.PI / 180, - E1 = -0.00000036 * Math.PI / 180; + * http://aa.quae.nl/en/reken/hemelpositie.html#2 */ +const Q0 = 218.316 * Math.PI / 180; +const Q1 = 13.176396 * Math.PI / 180; +const M0 = 134.963 * Math.PI / 180; +const M1 = 13.064993 * Math.PI / 180; +const F0 = 93.272 * Math.PI / 180; +const F1 = 13.229350 * Math.PI / 180; +const L0 = 6.289 * Math.PI / 180; +const L1 = 5.128 * Math.PI / 180; +const E0 = 23.439 * Math.PI / 180; +const E1 = -0.00000036 * Math.PI / 180; /* Geocentric ecliptic longitude compared to the equinox */ function ecliptic_equinox_longitude(t) { @@ -87,8 +87,8 @@ function hour_angle(t, lon) { function hour_angle_iterative(t, lon, ha0) { // Iteratively improve by walking back in time toward a smaller hour angle. - const t1 = transit_direct(t, ha0), - ha1 = hour_angle(t1, lon) + ha0; + const t1 = transit_direct(t, ha0); + const ha1 = hour_angle(t1, lon) + ha0; return ha1; } @@ -104,7 +104,7 @@ function hour_angle_refined(t, lon) { } function hours_later(t, hrs) { - return t + hrs / 24; + return t + hrs / 24; } function transit(t, lat, lon) { @@ -123,17 +123,17 @@ function transit(t, lat, lon) { } function altitude(t, lat, lon) { - const decl = declination(t), - ha = hour_angle(t, lon); - + const decl = declination(t); + const ha = hour_angle(t, lon); + return Math.asin( - Math.sin(lat) * Math.sin(decl) + + Math.sin(lat) * Math.sin(decl) + Math.cos(lat) * Math.cos(decl) * Math.cos(ha) ); } /* http://www.stargazing.net/kepler/moonrise.html article */ -const PARALLAX = 0.0023212879051524586; +const PARALLAX = 0.0023212879051524586; function rise_and_set(t, lat, lon) { const h = -PARALLAX; @@ -145,34 +145,35 @@ function rise_and_set(t, lat, lon) { // Go in 2 hour chunks. for (let i=0; i <= 24; i+=2) { if (i !== 0) { - h1 = altitude(hours_later(t, i), lat, lon) - h; + h1 = altitude(hours_later(t, i), lat, lon) - h; } const h2 = altitude(hours_later(t, i+1), lat, lon) - h; // Fit h0, h1, h2 to a parabola - const a = (h2 + h0) / 2 - h1, - b = (h2 - h0) / 2, - xe = -b / (2 * a), // vertex of parabola - ye = (a * xe + b) * xe + h1; - + const a = (h2 + h0) / 2 - h1; + const b = (h2 - h0) / 2; + const xe = -b / (2 * a); // vertex of parabola + const ye = (a * xe + b) * xe + h1; + // Discriminant const d = b * b - 4 * a * h1; let roots = 0; - let x1, x2; + let x1; + let x2; // Count roots if (d >= 0) { const dx = Math.sqrt(d) / (Math.abs(a) * 2); x1 = xe - dx; x2 = xe + dx; - + if (Math.abs(x1) <= 1) { roots++; } if (Math.abs(x2) <= 1) { roots++; } - if (x1 < -1) { + if (x1 < -1) { x1 = x2; } } @@ -180,15 +181,17 @@ function rise_and_set(t, lat, lon) { if (roots === 1) { if (h0 < 0 && isNaN(moonrise)) { moonrise = i + x1; - } else if(isNaN(moonset)) { + } + else if(isNaN(moonset)) { moonset = i + x1; } - } else if (roots === 2) { + } + else if (roots === 2) { if(isNaN(moonrise)) { - moonrise = i + (ye < 0 ? x2 : x1); + moonrise = i + (ye < 0 ? x2 : x1); } if(isNaN(moonset)) { - moonset = i + (ye < 0 ? x1 : x2); + moonset = i + (ye < 0 ? x1 : x2); } } @@ -198,14 +201,14 @@ function rise_and_set(t, lat, lon) { } // Move two hours of altitude h0 = h2; - } + } return { moonrise: hours_later(t, moonrise), moonset: hours_later(t, moonset), }; } -/* In the next 24 hours */ +/* In the next 24 hours */ function rise(t, lat, lon) { return rise_and_set(t, lat, lon).moonrise; } @@ -214,12 +217,12 @@ function set(t, lat, lon) { return rise_and_set(t, lat, lon).moonset; } -exports.latitude = latitude; -exports.longitude = longitude; +exports.latitude = latitude; +exports.longitude = longitude; exports.right_ascension = right_ascension; -exports.declination = declination; -exports.altitude = altitude; -exports.transit = transit; -exports.rise_and_set = rise_and_set; -exports.rise = rise; -exports.set = set; +exports.declination = declination; +exports.altitude = altitude; +exports.transit = transit; +exports.rise_and_set = rise_and_set; +exports.rise = rise; +exports.set = set; diff --git a/lib/solar.js b/lib/solar.js index ef874e3..74395ed 100644 --- a/lib/solar.js +++ b/lib/solar.js @@ -3,14 +3,14 @@ const astro = require("../index"); // Solar position, accurate to within one arcminute between 1800-2200. // http://aa.usno.navy.mil/faq/docs/SunApprox.php -const G0 = 357.529 * Math.PI / 180, - G1 = 0.98560028 * Math.PI / 180, - Q0 = 280.459 * Math.PI / 180, - Q1 = 0.98564736 * Math.PI / 180, - L0 = 1.915 * Math.PI / 180, - L1 = 0.020 * Math.PI / 180, - E0 = 23.439 * Math.PI / 180, - E1 = -0.00000036 * Math.PI / 180; +const G0 = 357.529 * Math.PI / 180; +const G1 = 0.98560028 * Math.PI / 180; +const Q0 = 280.459 * Math.PI / 180; +const Q1 = 0.98564736 * Math.PI / 180; +const L0 = 1.915 * Math.PI / 180; +const L1 = 0.020 * Math.PI / 180; +const E0 = 23.439 * Math.PI / 180; +const E1 = -0.00000036 * Math.PI / 180; // Convert Julian date t to mean anomaly function mean_anomaly(t) { @@ -19,17 +19,17 @@ function mean_anomaly(t) { // Convert Julian date t to ecliptic longitude function longitude(t) { - const g = mean_anomaly(t), - sin_g = Math.sin(g), - sin_2g = Math.sin(2 * g); + const g = mean_anomaly(t); + const sin_g = Math.sin(g); + const sin_2g = Math.sin(2 * g); return Q0 + Q1 * t + L0 * sin_g + L1 * sin_2g; } // Derivative of the ecliptic longitude (found using Wolfram Alpha <3) function longitude_prime(t) { - const g = mean_anomaly(t), - cos_g = Math.cos(g), - cos_2g = 2 * cos_g * cos_g - 1; + const g = mean_anomaly(t); + const cos_g = Math.cos(g); + const cos_2g = 2 * cos_g * cos_g - 1; return Q1 + G1 * (L0 * cos_g + 2 * L1 * cos_2g); } @@ -65,23 +65,23 @@ function declination(t) { return Math.asin(Math.sin(longitude(t)) * Math.sin(obliquity(t))); } -const D0 = 1.00014, - D1 = -0.01671, - D2 = -0.00014; +const D0 = 1.00014; +const D1 = -0.01671; +const D2 = -0.00014; // Convert Julian date t to the distance between the sun and the Earth-Moon // barycenter in AU function distance(t) { - const g = mean_anomaly(t), - cos_g = Math.cos(g), - cos_2g = 2 * cos_g * cos_g - 1; + const g = mean_anomaly(t); + const cos_g = Math.cos(g); + const cos_2g = 2 * cos_g * cos_g - 1; return D0 + D1 * cos_g + D2 * cos_2g; } // Transit time constants. -const J0 = 0.0009, // Leap second correction - T0 = 0.0053, - T1 = -0.0069; +const J0 = 0.0009; // Leap second correction +const T0 = 0.0053; +const T1 = -0.0069; function julian_cycle(t, lon) { return Math.round(t - J0 + lon/(2 * Math.PI)); @@ -115,56 +115,55 @@ function altitude_julian(h0, rise, t, lat, lon) { // Notable solar elevation angles. // https://en.wikipedia.org/wiki/Sunrise#Angle // https://en.wikipedia.org/wiki/Twilight#Civil_twilight -const SUNRISE = -0.833 * Math.PI / 180, - DAWN = -6 * Math.PI / 180, - RISE = -1, - SET = 1; +const SUNRISE = -0.833 * Math.PI / 180; +const DAWN = -6 * Math.PI / 180; +const RISE = -1; +const SET = 1; function rise(t, lat, lon) { return altitude_julian(SUNRISE, RISE, t, lat, lon); } function set(t, lat, lon) { - return altitude_julian(SUNRISE, SET, t, lat, lon); + return altitude_julian(SUNRISE, SET, t, lat, lon); } function dawn(t, lat, lon) { - return altitude_julian(DAWN, RISE, t, lat, lon); + return altitude_julian(DAWN, RISE, t, lat, lon); } function dusk(t, lat, lon) { - return altitude_julian(DAWN, SET, t, lat, lon); + return altitude_julian(DAWN, SET, t, lat, lon); } -exports.longitude = longitude; +exports.longitude = longitude; exports.longitude_julian = longitude_julian; -exports.obliquity = obliquity; -exports.right_ascension = right_ascension; -exports.declination = declination; -exports.distance = distance; -exports.transit = transit; -exports.altitude_julian = altitude_julian; -exports.rise = rise; -exports.set = set; -exports.dawn = dawn; -exports.dusk = dusk; +exports.obliquity = obliquity; +exports.right_ascension = right_ascension; +exports.declination = declination; +exports.distance = distance; +exports.transit = transit; +exports.altitude_julian = altitude_julian; +exports.rise = rise; +exports.set = set; +exports.dawn = dawn; +exports.dusk = dusk; // Solar elevation angle. // https://en.wikipedia.org/wiki/Solar_zenith_angle // https://en.wikipedia.org/wiki/Hour_angle function sin_elevation(t, lat, lon) { - const l = longitude(t), - e = obliquity(t), - sin_l = Math.sin(l), - cos_l = Math.cos(l), - sin_e = Math.sin(e), - cos_e = Math.cos(e), - sin_d = sin_l * sin_e, // sin_declination - cos_d = Math.sqrt(1 - sin_d * sin_d), - ra = Math.atan2(sin_l * cos_e, cos_l), // right ascension - h = astro.local_sidereal_time(t, lon) - ra, // hour angle - cos_h = Math.cos(h); - + const l = longitude(t); + const e = obliquity(t); + const sin_l = Math.sin(l); + const cos_l = Math.cos(l); + const sin_e = Math.sin(e); + const cos_e = Math.cos(e); + const sin_d = sin_l * sin_e; // sin_declination + const cos_d = Math.sqrt(1 - sin_d * sin_d); + const ra = Math.atan2(sin_l * cos_e, cos_l); // right ascension + const h = astro.local_sidereal_time(t, lon) - ra; // hour angle + const cos_h = Math.cos(h); return Math.sin(lat) * sin_d + Math.cos(lat) * cos_d * cos_h; } diff --git a/package.json b/package.json index f2fd29d..859cb91 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,21 @@ { "name": "astro", - "version": "1.4.0", + "version": "1.4.1", "description": "Solar and lunar position calculations", "main": "index.js", - "dependencies": { - "chai": "^3.5.0", - "jshint": "^2.9.2" - }, "devDependencies": { - "chai": "^3.5.0", - "cloc": "^2.1.0", - "istanbul": "^0.4.4", - "jshint": "^2.9.2", - "mocha": "^3.0.2" + "@darkskyapp/eslint-config": "^1.1.1", + "chai": "^4.1.2", + "eslint": "^4.19.1", + "eslint-plugin-node": "^6.0.1", + "eslint-plugin-promise": "^3.7.0", + "nyc": "^11.6.0", + "mocha": "^5.0.5" }, "scripts": { - "stats": "cloc --exclude-dir=build,coverage,node_modules --exclude-lang=HTML,JSON,Markdown,XML,YAML --quiet .", - "lint": "jshint . --exclude build,coverage,node_modules,var", - "cover": "NODE_ENV=test istanbul cover _mocha -- --check-leaks --recursive", + "lint": "eslint .", + "lintfix": "eslint --fix .", + "cover": "NODE_ENV=test nyc -c false -r html mocha", "test": "npm run lint && npm run cover" }, "repository": { diff --git a/test/astro.js b/test/astro.js index 4c2eb04..02c9e88 100644 --- a/test/astro.js +++ b/test/astro.js @@ -2,4 +2,4 @@ describe("astro", () => { /* FIXME: lots of methods to test */ -}); \ No newline at end of file +}); diff --git a/test/lunar.js b/test/lunar.js index c54b65f..c98a1d1 100644 --- a/test/lunar.js +++ b/test/lunar.js @@ -1,57 +1,57 @@ "use strict"; -const astro = require("../index"), - lunar = require("../lib/lunar"), - expect = require("chai").expect; +const astro = require("../index"); +const lunar = require("../lib/lunar"); +const expect = require("chai").expect; describe("lunar", () => { /* FIXME: lots of methods to test */ /* http://aa.usno.navy.mil/data/docs/RS_OneDay.php */ const inputs = [ - { - // Albuquerque - t: astro.date_to_julian(new Date("2006-03-20T19:06:28.800Z")), - lat: 35.05, - lon: -106.62 - }, - { - // NYC starting at midnight for some day... - t: astro.date_to_julian(new Date("2016-03-13T00:00:00-0500")), - lat: 40.71, - lon: -74.01 - }, - { - // NYC ... later that day so we can get a diff moonrise time! - t: astro.date_to_julian(new Date("2016-03-13T12:00:00-0500")), - lat: 40.71, - lon: -74.01 - }, - { - // NYC ... a different period entirely... - t: astro.date_to_julian(new Date("2017-12-01T00:00:00-0500")), - lat: 40.71, - lon: -74.01 - }, - { - // NYC ... and forward in that period so we get no transit ... - t: astro.date_to_julian(new Date("2017-12-01T22:30:00-0500")), - lat: 40.71, - lon: -74.01 - }, - { - // NYC ... and forward in that period so we get next transit ... - t: astro.date_to_julian(new Date("2017-12-01T23:30:00-0500")), - lat: 40.71, - lon: -74.01 - }, - { - // Troy, NY .. but what's more important, right around the moon - // transit time!! - t: astro.date_to_julian(new Date("2016-01-01T05:00:00-0500")), - lat: 42.73, - lon: -73.68 - }, - ]; + { + // Albuquerque + t: astro.date_to_julian(new Date("2006-03-20T19:06:28.800Z")), + lat: 35.05, + lon: -106.62, + }, + { + // NYC starting at midnight for some day... + t: astro.date_to_julian(new Date("2016-03-13T00:00:00-0500")), + lat: 40.71, + lon: -74.01, + }, + { + // NYC ... later that day so we can get a diff moonrise time! + t: astro.date_to_julian(new Date("2016-03-13T12:00:00-0500")), + lat: 40.71, + lon: -74.01, + }, + { + // NYC ... a different period entirely... + t: astro.date_to_julian(new Date("2017-12-01T00:00:00-0500")), + lat: 40.71, + lon: -74.01, + }, + { + // NYC ... and forward in that period so we get no transit ... + t: astro.date_to_julian(new Date("2017-12-01T22:30:00-0500")), + lat: 40.71, + lon: -74.01, + }, + { + // NYC ... and forward in that period so we get next transit ... + t: astro.date_to_julian(new Date("2017-12-01T23:30:00-0500")), + lat: 40.71, + lon: -74.01, + }, + { + // Troy, NY .. but what's more important, right around the moon + // transit time!! + t: astro.date_to_julian(new Date("2016-01-01T05:00:00-0500")), + lat: 42.73, + lon: -73.68, + }, + ]; const rises = [ Date.parse("2006-03-21T00:16-0700"), @@ -85,17 +85,18 @@ describe("lunar", () => { function _check_calculation(input, result, lookup_function) { const calculation = lookup_function( - input.t, - input.lat * (Math.PI / 180), - input.lon * (Math.PI / 180) + input.t, + input.lat * (Math.PI / 180), + input.lon * (Math.PI / 180) ); const calculated_value = astro.julian_to_date(calculation).getTime(); if(isNaN(result)) { - expect(calculated_value).to.be.NaN; // jshint ignore:line - } else { - expect( - calculated_value - ).to.be.closeTo(result, 10 * 60 * 1000); // Allow error of 10min + expect(calculated_value).to.be.NaN; // jshint ignore:line + } + else { + expect( + calculated_value + ).to.be.closeTo(result, 10 * 60 * 1000); // Allow error of 10min } } @@ -107,13 +108,13 @@ describe("lunar", () => { it("should return the nearest lunar transit to a given time", () => { for(let i = 0; i < transits.length; i++) { - _check_calculation(inputs[i], transits[i], lunar.transit); + _check_calculation(inputs[i], transits[i], lunar.transit); } }); it("should return the next moonset to a given time", () => { for(let i = 0; i < sets.length; i++) { - _check_calculation(inputs[i], sets[i], lunar.set); + _check_calculation(inputs[i], sets[i], lunar.set); } }); diff --git a/test/solar.js b/test/solar.js index 2d5f611..829fa8d 100644 --- a/test/solar.js +++ b/test/solar.js @@ -1,17 +1,17 @@ "use strict"; -const astro = require("../index"), - solar = require("../lib/solar"), - expect = require("chai").expect; +const astro = require("../index"); +const solar = require("../lib/solar"); +const expect = require("chai").expect; describe("solar", () => { /* FIXME: lots of methods to test */ - /*http://aa.usno.navy.mil/rstt/onedaytable + /* http://aa.usno.navy.mil/rstt/onedaytable * ?ID=AA&year=2006&month=3&day=20&state=NM&place=Albuquerque */ - const date = new Date("2006-03-20T19:06:28.800Z"), - t = astro.date_to_julian(date), - lat = 35.05 * (Math.PI / 180), - lon = -106.62 * (Math.PI / 180); + const date = new Date("2006-03-20T19:06:28.800Z"); + const t = astro.date_to_julian(date); + const lat = 35.05 * (Math.PI / 180); + const lon = -106.62 * (Math.PI / 180); it("should return the nearest civil dawn to a given time", () => { expect(astro.julian_to_date(solar.dawn(t, lat, lon)).getTime()). @@ -25,8 +25,8 @@ describe("solar", () => { it("should return the nearest solar transit to a given time", () => { expect( - astro.julian_to_date(solar.transit(t, lat, lon)).getTime() - ). + astro.julian_to_date(solar.transit(t, lat, lon)).getTime() + ). to.be.closeTo(Date.parse("2006-03-20T12:14-0700"), 120000); }); @@ -39,4 +39,4 @@ describe("solar", () => { expect(astro.julian_to_date(solar.dusk(t, lat, lon)).getTime()). to.be.closeTo(Date.parse("2006-03-20T18:43-0700"), 120000); }); -}); \ No newline at end of file +});