186 lines
5.4 KiB
JavaScript
186 lines
5.4 KiB
JavaScript
/*!
|
|
* satellite-js v5.0.0
|
|
* (c) 2013 Shashwat Kandadai and UCSC
|
|
* https://github.com/shashwatak/satellite-js
|
|
* License: MIT
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.degreesLat = degreesLat;
|
|
exports.degreesLong = degreesLong;
|
|
exports.degreesToRadians = degreesToRadians;
|
|
exports.ecfToEci = ecfToEci;
|
|
exports.ecfToLookAngles = ecfToLookAngles;
|
|
exports.eciToEcf = eciToEcf;
|
|
exports.eciToGeodetic = eciToGeodetic;
|
|
exports.geodeticToEcf = geodeticToEcf;
|
|
exports.radiansLat = radiansLat;
|
|
exports.radiansLong = radiansLong;
|
|
exports.radiansToDegrees = radiansToDegrees;
|
|
var _constants = require("./constants");
|
|
function radiansToDegrees(radians) {
|
|
return radians * _constants.rad2deg;
|
|
}
|
|
function degreesToRadians(degrees) {
|
|
return degrees * _constants.deg2rad;
|
|
}
|
|
function degreesLat(radians) {
|
|
if (radians < -_constants.pi / 2 || radians > _constants.pi / 2) {
|
|
throw new RangeError('Latitude radians must be in range [-pi/2; pi/2].');
|
|
}
|
|
return radiansToDegrees(radians);
|
|
}
|
|
function degreesLong(radians) {
|
|
if (radians < -_constants.pi || radians > _constants.pi) {
|
|
throw new RangeError('Longitude radians must be in range [-pi; pi].');
|
|
}
|
|
return radiansToDegrees(radians);
|
|
}
|
|
function radiansLat(degrees) {
|
|
if (degrees < -90 || degrees > 90) {
|
|
throw new RangeError('Latitude degrees must be in range [-90; 90].');
|
|
}
|
|
return degreesToRadians(degrees);
|
|
}
|
|
function radiansLong(degrees) {
|
|
if (degrees < -180 || degrees > 180) {
|
|
throw new RangeError('Longitude degrees must be in range [-180; 180].');
|
|
}
|
|
return degreesToRadians(degrees);
|
|
}
|
|
function geodeticToEcf(geodetic) {
|
|
var longitude = geodetic.longitude,
|
|
latitude = geodetic.latitude,
|
|
height = geodetic.height;
|
|
var a = 6378.137;
|
|
var b = 6356.7523142;
|
|
var f = (a - b) / a;
|
|
var e2 = 2 * f - f * f;
|
|
var normal = a / Math.sqrt(1 - e2 * (Math.sin(latitude) * Math.sin(latitude)));
|
|
var x = (normal + height) * Math.cos(latitude) * Math.cos(longitude);
|
|
var y = (normal + height) * Math.cos(latitude) * Math.sin(longitude);
|
|
var z = (normal * (1 - e2) + height) * Math.sin(latitude);
|
|
return {
|
|
x: x,
|
|
y: y,
|
|
z: z
|
|
};
|
|
}
|
|
function eciToGeodetic(eci, gmst) {
|
|
// http://www.celestrak.com/columns/v02n03/
|
|
var a = 6378.137;
|
|
var b = 6356.7523142;
|
|
var R = Math.sqrt(eci.x * eci.x + eci.y * eci.y);
|
|
var f = (a - b) / a;
|
|
var e2 = 2 * f - f * f;
|
|
var longitude = Math.atan2(eci.y, eci.x) - gmst;
|
|
while (longitude < -_constants.pi) {
|
|
longitude += _constants.twoPi;
|
|
}
|
|
while (longitude > _constants.pi) {
|
|
longitude -= _constants.twoPi;
|
|
}
|
|
var kmax = 20;
|
|
var k = 0;
|
|
var latitude = Math.atan2(eci.z, Math.sqrt(eci.x * eci.x + eci.y * eci.y));
|
|
var C;
|
|
while (k < kmax) {
|
|
C = 1 / Math.sqrt(1 - e2 * (Math.sin(latitude) * Math.sin(latitude)));
|
|
latitude = Math.atan2(eci.z + a * C * e2 * Math.sin(latitude), R);
|
|
k += 1;
|
|
}
|
|
var height = R / Math.cos(latitude) - a * C;
|
|
return {
|
|
longitude: longitude,
|
|
latitude: latitude,
|
|
height: height
|
|
};
|
|
}
|
|
function ecfToEci(ecf, gmst) {
|
|
// ccar.colorado.edu/ASEN5070/handouts/coordsys.doc
|
|
//
|
|
// [X] [C -S 0][X]
|
|
// [Y] = [S C 0][Y]
|
|
// [Z]eci [0 0 1][Z]ecf
|
|
//
|
|
var X = ecf.x * Math.cos(gmst) - ecf.y * Math.sin(gmst);
|
|
var Y = ecf.x * Math.sin(gmst) + ecf.y * Math.cos(gmst);
|
|
var Z = ecf.z;
|
|
return {
|
|
x: X,
|
|
y: Y,
|
|
z: Z
|
|
};
|
|
}
|
|
function eciToEcf(eci, gmst) {
|
|
// ccar.colorado.edu/ASEN5070/handouts/coordsys.doc
|
|
//
|
|
// [X] [C -S 0][X]
|
|
// [Y] = [S C 0][Y]
|
|
// [Z]eci [0 0 1][Z]ecf
|
|
//
|
|
//
|
|
// Inverse:
|
|
// [X] [C S 0][X]
|
|
// [Y] = [-S C 0][Y]
|
|
// [Z]ecf [0 0 1][Z]eci
|
|
|
|
var x = eci.x * Math.cos(gmst) + eci.y * Math.sin(gmst);
|
|
var y = eci.x * -Math.sin(gmst) + eci.y * Math.cos(gmst);
|
|
var z = eci.z;
|
|
return {
|
|
x: x,
|
|
y: y,
|
|
z: z
|
|
};
|
|
}
|
|
function topocentric(observerGeodetic, satelliteEcf) {
|
|
// http://www.celestrak.com/columns/v02n02/
|
|
// TS Kelso's method, except I'm using ECF frame
|
|
// and he uses ECI.
|
|
|
|
var longitude = observerGeodetic.longitude,
|
|
latitude = observerGeodetic.latitude;
|
|
var observerEcf = geodeticToEcf(observerGeodetic);
|
|
var rx = satelliteEcf.x - observerEcf.x;
|
|
var ry = satelliteEcf.y - observerEcf.y;
|
|
var rz = satelliteEcf.z - observerEcf.z;
|
|
var topS = Math.sin(latitude) * Math.cos(longitude) * rx + Math.sin(latitude) * Math.sin(longitude) * ry - Math.cos(latitude) * rz;
|
|
var topE = -Math.sin(longitude) * rx + Math.cos(longitude) * ry;
|
|
var topZ = Math.cos(latitude) * Math.cos(longitude) * rx + Math.cos(latitude) * Math.sin(longitude) * ry + Math.sin(latitude) * rz;
|
|
return {
|
|
topS: topS,
|
|
topE: topE,
|
|
topZ: topZ
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {Object} tc
|
|
* @param {Number} tc.topS Positive horizontal vector S due south.
|
|
* @param {Number} tc.topE Positive horizontal vector E due east.
|
|
* @param {Number} tc.topZ Vector Z normal to the surface of the earth (up).
|
|
* @returns {Object}
|
|
*/
|
|
function topocentricToLookAngles(tc) {
|
|
var topS = tc.topS,
|
|
topE = tc.topE,
|
|
topZ = tc.topZ;
|
|
var rangeSat = Math.sqrt(topS * topS + topE * topE + topZ * topZ);
|
|
var El = Math.asin(topZ / rangeSat);
|
|
var Az = Math.atan2(-topE, topS) + _constants.pi;
|
|
return {
|
|
azimuth: Az,
|
|
elevation: El,
|
|
rangeSat: rangeSat // Range in km
|
|
};
|
|
}
|
|
|
|
function ecfToLookAngles(observerGeodetic, satelliteEcf) {
|
|
var topocentricCoords = topocentric(observerGeodetic, satelliteEcf);
|
|
return topocentricToLookAngles(topocentricCoords);
|
|
} |