From 868d6b02fb2bfb81c4e3d7e809e54514e3a8b14d Mon Sep 17 00:00:00 2001
From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com>
Date: Wed, 1 May 2024 08:59:22 +0200
Subject: [PATCH] Playing with sats
---
application/config/migration.php | 2 +-
application/controllers/Satellite.php | 30 +
application/migrations/196_tle_table.php | 50 +
application/models/Satellite_model.php | 43 +-
application/views/interface_assets/header.php | 2 +
application/views/satellite/flightpath.php | 44 +
application/views/satellite/index.php | 2 +-
assets/icons/saticon.png | Bin 0 -> 1081 bytes
assets/js/sections/flightpath.js | 469 +++
assets/js/sections/satellite_functions.js | 3069 +++++++++++++++++
assets/js/sections/three-orbit-controls.js | 1022 ++++++
11 files changed, 4718 insertions(+), 15 deletions(-)
create mode 100644 application/migrations/196_tle_table.php
create mode 100644 application/views/satellite/flightpath.php
create mode 100644 assets/icons/saticon.png
create mode 100644 assets/js/sections/flightpath.js
create mode 100644 assets/js/sections/satellite_functions.js
create mode 100644 assets/js/sections/three-orbit-controls.js
diff --git a/application/config/migration.php b/application/config/migration.php
index aebf9841d..ce5a1727a 100644
--- a/application/config/migration.php
+++ b/application/config/migration.php
@@ -22,7 +22,7 @@ $config['migration_enabled'] = TRUE;
|
*/
-$config['migration_version'] = 195;
+$config['migration_version'] = 196;
/*
|--------------------------------------------------------------------------
diff --git a/application/controllers/Satellite.php b/application/controllers/Satellite.php
index 60a6180ee..ffd958b1a 100644
--- a/application/controllers/Satellite.php
+++ b/application/controllers/Satellite.php
@@ -138,4 +138,34 @@ class Satellite extends CI_Controller {
echo json_encode($sat_list, JSON_FORCE_OBJECT);
}
+ public function flightpath() {
+ $this->load->model('satellite_model');
+
+ $pageData['satellites'] = $this->satellite_model->get_all_satellites_with_tle();
+
+ $footerData = [];
+ $footerData['scripts'] = [
+ 'assets/js/sections/satellite.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/satellite.js")),
+ 'assets/js/sections/three-orbit-controls.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/three-orbit-controls.js")),
+ 'assets/js/leaflet/Leaflet.Antimeridian.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/leaflet/Leaflet.Antimeridian.js")),
+ 'assets/js/sections/satellite_functions.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/satellite_functions.js")),
+ 'assets/js/sections/flightpath.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/flightpath.js")),
+ ];
+
+ // Render Page
+ $pageData['page_title'] = "Satellite Flightpath";
+ $this->load->view('interface_assets/header', $pageData);
+ $this->load->view('satellite/flightpath');
+ $this->load->view('interface_assets/footer', $footerData);
+ }
+
+ public function get_tle() {
+ $sat = $this->security->xss_clean($this->input->post('sat'));
+ $this->load->model('satellite_model');
+ $satellite_data = $this->satellite_model->get_tle($sat);
+
+ header('Content-Type: application/json');
+ echo json_encode($satellite_data, JSON_FORCE_OBJECT);
+ }
+
}
diff --git a/application/migrations/196_tle_table.php b/application/migrations/196_tle_table.php
new file mode 100644
index 000000000..9f923e41b
--- /dev/null
+++ b/application/migrations/196_tle_table.php
@@ -0,0 +1,50 @@
+db->table_exists('tle')) {
+
+ $this->dbforge->add_field(array(
+ 'id' => array(
+ 'type' => 'INT',
+ 'constraint' => 6,
+ 'unsigned' => TRUE,
+ 'auto_increment' => TRUE,
+ 'null' => FALSE
+ ),
+ 'satelliteid' => array(
+ 'type' => 'INT',
+ 'constraint' => '6',
+ 'unsigned' => TRUE,
+ 'null' => FALSE,
+ ),
+ 'tle' => array(
+ 'type' => 'TEXT',
+ 'null' => TRUE,
+ ),
+ ));
+ $this->dbforge->add_key('id', TRUE);
+
+ $this->dbforge->create_table('tle');
+
+ $this->db->query("INSERT INTO tle (satelliteid, tle) select id, '1 53106U 22080B 24108.26757684 -.00000003 00000-0 00000-0 0 9991
+2 53106 70.1428 213.6795 0007982 119.5170 240.6286 6.42557181 41352' from satellite where name = 'IO-117'");
+
+ $this->db->query("INSERT INTO tle (satelliteid, tle) select id, '1 43700U 18090A 24108.89503844 .00000137 00000-0 00000-0 0 9999
+2 43700 0.0157 247.0647 0001348 170.1236 137.3539 1.00275203 19758' from satellite where name = 'QO-100'");
+
+ $this->db->query("INSERT INTO tle (satelliteid, tle) select id, '1 27607U 02058C 24108.20375939 .00004950 00000+0 68675-3 0 9992
+2 27607 64.5563 48.9470 0031938 141.6898 218.6483 14.78629101147515' from satellite where name = 'SO-50'");
+
+ }
+ }
+
+ public function down()
+ {
+ $this->dbforge->drop_table('tle');
+ }
+}
diff --git a/application/models/Satellite_model.php b/application/models/Satellite_model.php
index 7f0c4f770..46ece46b7 100644
--- a/application/models/Satellite_model.php
+++ b/application/models/Satellite_model.php
@@ -11,6 +11,15 @@ class Satellite_model extends CI_Model {
return $this->db->query($sql)->result();
}
+ function get_all_satellites_with_tle() {
+ $sql = "select satellite.id, satellite.name as satname, tle.tle
+ from satellite
+ join tle on satellite.id = tle.satelliteid
+ ";
+
+ return $this->db->query($sql)->result();
+ }
+
function delete($id) {
// Clean ID
$clean_id = $this->security->xss_clean($id);
@@ -102,19 +111,27 @@ class Satellite_model extends CI_Model {
return $query->result();
}
- function array_group_by($flds, $arr) {
- $groups = array();
- foreach ($arr as $rec) {
- $keys = array_map(function($f) use($rec) { return $rec[$f]; }, $flds);
- $k = implode('@', $keys);
- if (isset($groups[$k])) {
- $groups[$k][] = $rec;
- } else {
- $groups[$k] = array($rec);
- }
- }
- return $groups;
- }
+ function array_group_by($flds, $arr) {
+ $groups = array();
+ foreach ($arr as $rec) {
+ $keys = array_map(function($f) use($rec) { return $rec[$f]; }, $flds);
+ $k = implode('@', $keys);
+ if (isset($groups[$k])) {
+ $groups[$k][] = $rec;
+ } else {
+ $groups[$k] = array($rec);
+ }
+ }
+ return $groups;
+ }
+
+ function get_tle($sat) {
+ $this->db->select('satellite.name AS satellite, tle.tle');
+ $this->db->join('tle', 'satellite.id = tle.satelliteid');
+ $this->db->where('name', $sat);
+ $query = $this->db->get('satellite');
+ return $query->row();
+ }
}
diff --git a/application/views/interface_assets/header.php b/application/views/interface_assets/header.php
index 6db5bd680..d994fecfe 100644
--- a/application/views/interface_assets/header.php
+++ b/application/views/interface_assets/header.php
@@ -230,6 +230,8 @@
+ Satellite Flightpath
+
diff --git a/application/views/satellite/flightpath.php b/application/views/satellite/flightpath.php
new file mode 100644
index 000000000..43e175065
--- /dev/null
+++ b/application/views/satellite/flightpath.php
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
diff --git a/application/views/satellite/index.php b/application/views/satellite/index.php
index 6eaaabba9..cb818874a 100644
--- a/application/views/satellite/index.php
+++ b/application/views/satellite/index.php
@@ -8,7 +8,7 @@
-Satellites
+
diff --git a/assets/icons/saticon.png b/assets/icons/saticon.png
new file mode 100644
index 0000000000000000000000000000000000000000..e32656299053ac60ce6e1d8b95cb450130f27c62
GIT binary patch
literal 1081
zcmV-91jhS`P)
004R=
z004l4008;_004mL004C`008P>0026e000+nl3&F}00002VoOIv0063uBQgL0010qN
zS#tmY3ljhU3ljkVnw%H_00Xf}L_t(Y$F-AtOqOLB#(xjr3vy6D6&P5Xb4)y7B!ZeX
zLkevszku2qa99Q(r_9fH~+o
zetlxvxON0WzBfWt{^Z-)Qj^`grhion`l8E!R$ANemS+bNDO#xDFNJrw4aJ+dsS(zKne0@=V(KV
zKP`l+t3o!{)&=wo(ZLA1{A;~?k=maKM@;o6{l)c`;S?uLrtJR4XmAzP%{@?j8wWpP
zou9PLx(>|ad3#>-#blzPva%~?@^vueI@$hht*1Z*dM&KD7Yg$H*6L98L}y9BbSi{~
z2vftjO4ol*^KR?#uY_I4n0^N%Jdcjt$F6?6OM^g}#<%5v&pE5LWoF|k|(u$kq#tauMNxdPL)#xBsuo)lerCLmpu2%-5}
zS7(${y2Z%yezX{p)dskC#=e|=%Mk^QbR~sloDekIHV)IxBL~9kGYlbXPyv-bbRH05
zvA3;gFmmtnz`f4PJvpd3W@L2u#6Nn_h>vUQL&{ga=Igf|BeRUgZL0e;HH!#9v@yWx
zwaNR4dGpfNq)?bQu6>X~51f6kGS`koATyz-?5nZ+*igGt%fei4(h`Z9vRqrEpXvSM+^Qu-VL1!Jiw_m)3K
zyxZJd>c_5`8p
z1QST|{E&Ej^Tt;%uWpYHpgpYJoALgt^Er{(eE|=VG&!$D8PCHr&p-yYAFkrS{*`O&
zSu-0hgejQHg$GR2@{an154{Zz5Ej$y=OXreZCSrGo1x9^d^GdQACr3hU$JEU-nw{V
zQ&l1f9Sbs}d}!id14oCSgVZ_0wmd_cJ`^6xv!3l9X=?J^8Z~r~?o>bCRd`#-fTMJ0
zHL!%wO0WgS!TpEmyqnt?bFxfk;k)$ykJtHc*obRDWc!Em00000NkvXXu0mjfr*Q&r
literal 0
HcmV?d00001
diff --git a/assets/js/sections/flightpath.js b/assets/js/sections/flightpath.js
new file mode 100644
index 000000000..35f95c434
--- /dev/null
+++ b/assets/js/sections/flightpath.js
@@ -0,0 +1,469 @@
+var satmarker;
+var icon_dot_url = base_url + "assets/icons/saticon.png";
+var saticon = L.icon({ iconUrl: icon_dot_url, iconSize: [30, 30] });
+
+var sats = (function (L, d3, satelliteJs) {
+ var RADIANS = Math.PI / 180;
+ var DEGREES = 180 / Math.PI;
+ var R_EARTH = 6378.137; // equatorial radius (km)
+
+ /* =============================================== */
+ /* =============== CLOCK ========================= */
+ /* =============================================== */
+
+ /**
+ * Factory function for keeping track of elapsed time and rates.
+ */
+ function Clock() {
+ this._rate = 60; // 1ms elapsed : 60sec simulated
+ this._date = d3.now();
+ this._elapsed = 0;
+ };
+
+ Clock.prototype.date = function (timeInMs) {
+ if (!arguments.length) return this._date + (this._elapsed * this._rate);
+ this._date = timeInMs;
+ return this;
+ };
+
+ Clock.prototype.elapsed = function (ms) {
+ if (!arguments.length) return this._date - d3.now(); // calculates elapsed
+ this._elapsed = ms;
+ return this;
+ };
+
+ Clock.prototype.rate = function (secondsPerMsElapsed) {
+ if (!arguments.length) return this._rate;
+ this._rate = secondsPerMsElapsed;
+ return this;
+ };
+
+ /* ==================================================== */
+ /* =============== CONVERSION ========================= */
+ /* ==================================================== */
+
+ function satrecToFeature(satrec, date, props) { // DJ7NT: This is never called
+ var properties = props || {};
+ var positionAndVelocity = satelliteJs.propagate(satrec, date);
+ var gmst = satelliteJs.gstime(date);
+ var positionGd = satelliteJs.eciToGeodetic(positionAndVelocity.position, gmst);
+ properties.height = positionGd.height;
+ return {
+ type: "FeatureCollection",
+ "features": [ {
+ type: 'Feature',
+ properties: properties,
+ geometry: {
+ type: 'Point',
+ coordinates: [
+ positionGd.longitude * DEGREES,
+ positionGd.latitude * DEGREES
+ ]
+ }
+ },
+ {
+ type: 'Feature',
+ properties: {infoText: 'blabla'},
+ geometry: {
+ type: 'Point',
+ coordinates: [
+ positionGd.longitude * DEGREES,
+ positionGd.latitude * DEGREES
+ ]
+ }
+ }]
+ };
+ };
+ /* ==================================================== */
+ /* =============== TLE ================================ */
+ /* ==================================================== */
+
+ /**
+ * Factory function for working with TLE.
+ */
+ function TLE() {
+ this._properties;
+ this._date;
+ };
+ TLE.prototype._lines = function (arry) {
+ return arry.slice(0, 2);
+ };
+
+ TLE.prototype.satrecs = function (tles) {
+ return tles.map(function (d) {
+ return satelliteJs.twoline2satrec.apply(null, this._lines(d));
+ });
+ };
+
+ TLE.prototype.features = function (tles) {
+ var date = this._date || d3.now();
+
+ return tles.map(function (d) {
+ var satrec = satelliteJs.twoline2satrec.apply(null, this._lines(d));
+ return satrecToFeature(satrec, date, this._properties(d));
+ });
+ };
+
+ TLE.prototype.lines = function (func) {
+ if (!arguments.length) return this._lines;
+ this._lines = func;
+ return this;
+ };
+
+ TLE.prototype.properties = function (func) {
+ if (!arguments.length) return this._properties;
+ this._properties = func;
+ return this;
+ };
+
+ TLE.prototype.date = function (ms) {
+ if (!arguments.length) return this._date;
+ this._date = ms;
+ return this;
+ };
+
+
+ /* ==================================================== */
+ /* =============== PARSE ============================== */
+ /* ==================================================== */
+
+ /**
+ * Parses text file string of tle into groups.
+ * @return {string[][]} Like [['tle line 1', 'tle line 2'], ...]
+ */
+ function parseTle(tleString) {
+ // remove last newline so that we can properly split all the lines
+ var lines = tleString.replace(/\r?\n$/g, '').split(/\r?\n/);
+
+ return lines.reduce(function (acc, cur, index) {
+ if (index % 2 === 0) acc.push([]);
+ acc[acc.length - 1].push(cur);
+ return acc;
+ }, []);
+ };
+
+
+ /* ==================================================== */
+ /* =============== SATELLITE ========================== */
+ /* ==================================================== */
+
+ /**
+ * Satellite factory function that wraps satellitejs functionality
+ * and can compute footprints based on TLE and date
+ *
+ * @param {string[][]} tle two-line element
+ * @param {Date} date date to propagate with TLE
+ */
+ function Satellite(tle, date) {
+ this._satrec = satelliteJs.twoline2satrec(tle[0], tle[1]);
+ this._satNum = this._satrec.satnum; // NORAD Catalog Number
+
+ this._altitude; // km
+ this._position = {
+ lat: null,
+ lng: null
+ };
+ this._halfAngle; // degrees
+ this._date;
+ this._gmst;
+
+ this.setDate(date);
+ this.update();
+ this._orbitType = this.orbitTypeFromAlt(this._altitude); // LEO, MEO, or GEO
+ };
+
+ /**
+ * Updates satellite position and altitude based on current TLE and date
+ */
+ Satellite.prototype.update = function () {
+ try {
+ var positionAndVelocity = satelliteJs.propagate(this._satrec, this._date);
+ var positionGd = satelliteJs.eciToGeodetic(positionAndVelocity.position, this._gmst);
+
+ this._position = {
+ lat: positionGd.latitude * DEGREES,
+ lng: positionGd.longitude * DEGREES
+ };
+ this._altitude = positionGd.height;
+ satmarker.setLatLng(this._position);
+ } catch (e) {
+ // Malicious // non-calcable SAT Found
+ } finally {
+ return this;
+ }
+ };
+
+ /**
+ * @returns {GeoJSON.Polygon} GeoJSON describing the satellite's current footprint on the Earth
+ */
+ Satellite.prototype.getFootprint = function () {
+ var theta = this._halfAngle * RADIANS;
+
+ coreAngle = this._coreAngle(theta, this._altitude, R_EARTH) * DEGREES;
+
+ return d3.geoCircle()
+ .center([this._position.lng, this._position.lat])
+ .radius(coreAngle)();
+ };
+
+ Satellite.prototype.getLocation = function () {
+ return d3.geoCircle()
+ .center([this._position.lng, this._position.lat])
+ .radius(1)();
+ };
+
+ /**
+ * A conical satellite with half angle casts a circle on the Earth. Find the angle
+ * from the center of the earth to the radius of this circle
+ * @param {number} theta: Satellite half angle in radians
+ * @param {number} altitude Satellite altitude
+ * @param {number} r Earth radius
+ * @returns {number} core angle in radians
+ */
+ Satellite.prototype._coreAngle = function (theta, altitude, r) {
+ // if FOV is larger than Earth, assume it goes to the tangential point
+ // if (Math.sin(theta) != r / (altitude + r)) {
+ return Math.acos(r / (r + altitude));
+ // }
+ // return Math.abs(Math.asin((r + altitude) * Math.sin(theta) / r)) - theta;
+ };
+
+ Satellite.prototype.halfAngle = function (halfAngle) {
+ if (!arguments.length) return this._halfAngle;
+ this._halfAngle = halfAngle;
+ return this;
+ };
+
+ Satellite.prototype.satNum = function (satNum) {
+ if (!arguments.length) return this._satNum;
+ this._satNum = satNum;
+ return this;
+ };
+
+ Satellite.prototype.altitude = function (altitude) {
+ if (!arguments.length) return this._altitude;
+ this._altitude = altitude;
+ return this;
+ };
+
+ Satellite.prototype.position = function (position) {
+ if (!arguments.length) return this._position;
+ this._position = position;
+ return this;
+ };
+
+ Satellite.prototype.getOrbitType = function () {
+ return this._orbitType;
+ };
+
+ /**
+ * sets both the date and the Greenwich Mean Sidereal Time
+ * @param {Date} date
+ */
+ Satellite.prototype.setDate = function (date) {
+ this._date = date;
+ this._gmst = satelliteJs.gstime(date);
+ return this;
+ };
+
+ /**
+ * Maps an altitude to a type of satellite
+ * @param {number} altitude (in KM)
+ * @returns {'LEO' | 'MEO' | 'GEO'}
+ */
+ Satellite.prototype.orbitTypeFromAlt = function (altitude) {
+ console.log(altitude);
+ this._altitude = altitude || this._altitude;
+ return this._altitude < 1200 ? 'LEO' : this._altitude > 22000 ? 'GEO' : 'MEO';
+ };
+
+
+ /* =============================================== */
+ /* =============== LEAFLET MAP =================== */
+ /* =============================================== */
+
+ // Approximate date the tle data was aquired from https://www.space-track.org/#recent
+ // var TLE_DATA_DATE = new Date(2024, 04, 18).getTime();
+ var TLE_DATA_DATE = Date.now();
+
+ var leafletMap;
+ var attributionControl;
+ var activeClock;
+ var sats;
+ var svgLayer;
+
+ function projectPointCurry(map) {
+ return function (x, y) {
+ const point = map.latLngToLayerPoint(L.latLng(y, x));
+ this.stream.point(point.x, point.y);
+ }
+ };
+
+ function init() {
+ svgLayer = L.svg();
+ leafletMap = L.map('sat_map', {
+ zoom: 3,
+ center: [20, 0],
+ // attributionControl: false,
+ layers: [
+ L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ // noWrap: false,
+ }),
+ svgLayer
+ ]
+ });
+
+ // L.terminator().addTo(leafletMap);
+
+ // L.marker(["50","7"],{
+ // title: "blabla",
+ // zIndex: 1000}
+ // ).addTo(leafletMap);
+
+ satmarker = L.marker(
+ [0, 0], {
+ icon: saticon,
+ title: 'sat',
+ zIndex: 1000,
+ }
+ ).addTo(leafletMap);
+
+ attributionControl = L.control.attribution({
+ prefix: ''
+ }).addTo(leafletMap);
+
+ var transform = d3.geoTransform({
+ point: projectPointCurry(leafletMap)
+ });
+
+ path = d3.geoPath()
+ .projection(transform)
+ .pointRadius(2.5);
+ };
+
+ function updateSats(date) {
+ sats.forEach(function (sat) {
+ sat.setDate(date).update();
+ });
+ return sats
+ };
+
+ /**
+ * Create satellite objects for each record in the TLEs and begin animation
+ * @param {string[][]} parsedTles
+ */
+ function initSats(parsedTles) {
+ activeClock = new Clock()
+ .rate(1)
+ .date(TLE_DATA_DATE);
+ sats = parsedTles.map(function (tle) {
+ var sat = new Satellite(tle, new Date(2024, 4, 18));
+ sat.halfAngle(30);
+ // sat.halfAngle(sat.getOrbitType() === 'LEO' ? Math.random() * (30 - 15) + 15 : Math.random() * 4 + 1);
+ return sat;
+ });
+
+ leafletMap.on('zoom', draw);
+
+ window.requestAnimationFrame(animateSats);
+ return sats;
+ };
+
+ function invertProjection(projection) {
+ return function (x, y) {
+ const point = projection.invert([x, y]);
+ this.stream.point(point[0], point[1]);
+ };
+ }
+
+ function clipMercator(geoJson) {
+ const mercator = d3.geoMercator();
+ const inverseMercator = d3.geoTransform({
+ point: invertProjection(mercator)
+ });
+ // D3 geoProject handles Mercator clipping
+ const newJson = d3.geoProject(geoJson, mercator);
+ return d3.geoProject(newJson, inverseMercator);
+ }
+
+ function draw() {
+ var transform = d3.geoTransform({
+ point: projectPointCurry(leafletMap)
+ });
+ var geoPath = d3.geoPath()
+ .projection(transform);
+
+ d3.select(svgLayer._container)
+ .selectAll('.footprint')
+ .data(sats, function (sat) {
+ return sat._satNum; // DJ7NT: This is the Number of the SAT
+ })
+ .join(
+ function (enter) {
+ return enter.append('path').attr('class', function (sat) {
+ return 'footprint footprint--' + sat.getOrbitType();
+ });
+ },
+ function (update) {
+ return update;
+ },
+ function (exit) {
+ return exit.remove();
+ }
+ ).attr('d', function (sat) {
+ // return geoPath(clipMercator(sat.getLocation())); // DJ7NT: this is the "point" of the SAT
+ let xx= geoPath(clipMercator(sat.getFootprint()));
+ return xx;
+ });
+ };
+
+ function animateSats(elapsed) {
+ var dateInMs = activeClock.elapsed(elapsed)
+ .date();
+ var date = new Date(dateInMs);
+ attributionControl.setPrefix(date);
+
+ updateSats(date);
+ draw();
+ window.requestAnimationFrame(animateSats);
+ }
+
+ function start(data) {
+ init();
+ initSats(parseTle(data.tle));
+ }
+
+ return {
+ start: start
+};
+
+
+ }(window.L, window.d3, window.satellite))
+
+function plot_sat() {
+ var container = L.DomUtil.get('sat_map');
+ if(container != null){
+ container._leaflet_id = null;
+ container.remove();
+
+ }
+
+ amap = $('#sat_map').val();
+ if (amap == undefined) {
+ $("#satcontainer").append('');
+ }
+
+ $.ajax({
+ url: base_url + 'index.php/satellite/get_tle',
+ type: 'post',
+ data: {
+ sat: $("#sats").val(),
+ },
+ success: function (data) {
+ sats.start(data);
+ },
+ error: function (data) {
+ alert('Something went wrong!');
+ },
+ });
+}
diff --git a/assets/js/sections/satellite_functions.js b/assets/js/sections/satellite_functions.js
new file mode 100644
index 000000000..b1ac01dbe
--- /dev/null
+++ b/assets/js/sections/satellite_functions.js
@@ -0,0 +1,3069 @@
+/*!
+ * satellite-js v3.0.0
+ * (c) 2013 Shashwat Kandadai and UCSC
+ * https://github.com/shashwatak/satellite-js
+ * License: MIT
+ */
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.satellite = factory());
+}(this, (function () { 'use strict';
+
+ var pi = Math.PI;
+ var twoPi = pi * 2;
+ var deg2rad = pi / 180.0;
+ var rad2deg = 180 / pi;
+ var minutesPerDay = 1440.0;
+ var mu = 398600.5; // in km3 / s2
+
+ var earthRadius = 6378.137; // in km
+
+ var xke = 60.0 / Math.sqrt(earthRadius * earthRadius * earthRadius / mu);
+ var tumin = 1.0 / xke;
+ var j2 = 0.00108262998905;
+ var j3 = -0.00000253215306;
+ var j4 = -0.00000161098761;
+ var j3oj2 = j3 / j2;
+ var x2o3 = 2.0 / 3.0;
+
+ var constants = /*#__PURE__*/Object.freeze({
+ pi: pi,
+ twoPi: twoPi,
+ deg2rad: deg2rad,
+ rad2deg: rad2deg,
+ minutesPerDay: minutesPerDay,
+ mu: mu,
+ earthRadius: earthRadius,
+ xke: xke,
+ tumin: tumin,
+ j2: j2,
+ j3: j3,
+ j4: j4,
+ j3oj2: j3oj2,
+ x2o3: x2o3
+ });
+
+ /* -----------------------------------------------------------------------------
+ *
+ * procedure days2mdhms
+ *
+ * this procedure converts the day of the year, days, to the equivalent month
+ * day, hour, minute and second.
+ *
+ * algorithm : set up array for the number of days per month
+ * find leap year - use 1900 because 2000 is a leap year
+ * loop through a temp value while the value is < the days
+ * perform int conversions to the correct day and month
+ * convert remainder into h m s using type conversions
+ *
+ * author : david vallado 719-573-2600 1 mar 2001
+ *
+ * inputs description range / units
+ * year - year 1900 .. 2100
+ * days - julian day of the year 0.0 .. 366.0
+ *
+ * outputs :
+ * mon - month 1 .. 12
+ * day - day 1 .. 28,29,30,31
+ * hr - hour 0 .. 23
+ * min - minute 0 .. 59
+ * sec - second 0.0 .. 59.999
+ *
+ * locals :
+ * dayofyr - day of year
+ * temp - temporary extended values
+ * inttemp - temporary int value
+ * i - index
+ * lmonth[12] - int array containing the number of days per month
+ *
+ * coupling :
+ * none.
+ * --------------------------------------------------------------------------- */
+ function days2mdhms(year, days) {
+ var lmonth = [31, year % 4 === 0 ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+ var dayofyr = Math.floor(days); // ----------------- find month and day of month ----------------
+
+ var i = 1;
+ var inttemp = 0;
+
+ while (dayofyr > inttemp + lmonth[i - 1] && i < 12) {
+ inttemp += lmonth[i - 1];
+ i += 1;
+ }
+
+ var mon = i;
+ var day = dayofyr - inttemp; // ----------------- find hours minutes and seconds -------------
+
+ var temp = (days - dayofyr) * 24.0;
+ var hr = Math.floor(temp);
+ temp = (temp - hr) * 60.0;
+ var minute = Math.floor(temp);
+ var sec = (temp - minute) * 60.0;
+ return {
+ mon: mon,
+ day: day,
+ hr: hr,
+ minute: minute,
+ sec: sec
+ };
+ }
+ /* -----------------------------------------------------------------------------
+ *
+ * procedure jday
+ *
+ * this procedure finds the julian date given the year, month, day, and time.
+ * the julian date is defined by each elapsed day since noon, jan 1, 4713 bc.
+ *
+ * algorithm : calculate the answer in one step for efficiency
+ *
+ * author : david vallado 719-573-2600 1 mar 2001
+ *
+ * inputs description range / units
+ * year - year 1900 .. 2100
+ * mon - month 1 .. 12
+ * day - day 1 .. 28,29,30,31
+ * hr - universal time hour 0 .. 23
+ * min - universal time min 0 .. 59
+ * sec - universal time sec 0.0 .. 59.999
+ *
+ * outputs :
+ * jd - julian date days from 4713 bc
+ *
+ * locals :
+ * none.
+ *
+ * coupling :
+ * none.
+ *
+ * references :
+ * vallado 2007, 189, alg 14, ex 3-14
+ *
+ * --------------------------------------------------------------------------- */
+
+ function jdayInternal(year, mon, day, hr, minute, sec) {
+ var msec = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0;
+ return 367.0 * year - Math.floor(7 * (year + Math.floor((mon + 9) / 12.0)) * 0.25) + Math.floor(275 * mon / 9.0) + day + 1721013.5 + ((msec / 60000 + sec / 60.0 + minute) / 60.0 + hr) / 24.0 // ut in days
+ // # - 0.5*sgn(100.0*year + mon - 190002.5) + 0.5;
+ ;
+ }
+
+ function jday(year, mon, day, hr, minute, sec, msec) {
+ if (year instanceof Date) {
+ var date = year;
+ return jdayInternal(date.getUTCFullYear(), date.getUTCMonth() + 1, // Note, this function requires months in range 1-12.
+ date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds());
+ }
+
+ return jdayInternal(year, mon, day, hr, minute, sec, msec);
+ }
+ /* -----------------------------------------------------------------------------
+ *
+ * procedure invjday
+ *
+ * this procedure finds the year, month, day, hour, minute and second
+ * given the julian date. tu can be ut1, tdt, tdb, etc.
+ *
+ * algorithm : set up starting values
+ * find leap year - use 1900 because 2000 is a leap year
+ * find the elapsed days through the year in a loop
+ * call routine to find each individual value
+ *
+ * author : david vallado 719-573-2600 1 mar 2001
+ *
+ * inputs description range / units
+ * jd - julian date days from 4713 bc
+ *
+ * outputs :
+ * year - year 1900 .. 2100
+ * mon - month 1 .. 12
+ * day - day 1 .. 28,29,30,31
+ * hr - hour 0 .. 23
+ * min - minute 0 .. 59
+ * sec - second 0.0 .. 59.999
+ *
+ * locals :
+ * days - day of year plus fractional
+ * portion of a day days
+ * tu - julian centuries from 0 h
+ * jan 0, 1900
+ * temp - temporary double values
+ * leapyrs - number of leap years from 1900
+ *
+ * coupling :
+ * days2mdhms - finds month, day, hour, minute and second given days and year
+ *
+ * references :
+ * vallado 2007, 208, alg 22, ex 3-13
+ * --------------------------------------------------------------------------- */
+
+ function invjday(jd, asArray) {
+ // --------------- find year and days of the year -
+ var temp = jd - 2415019.5;
+ var tu = temp / 365.25;
+ var year = 1900 + Math.floor(tu);
+ var leapyrs = Math.floor((year - 1901) * 0.25); // optional nudge by 8.64x10-7 sec to get even outputs
+
+ var days = temp - ((year - 1900) * 365.0 + leapyrs) + 0.00000000001; // ------------ check for case of beginning of a year -----------
+
+ if (days < 1.0) {
+ year -= 1;
+ leapyrs = Math.floor((year - 1901) * 0.25);
+ days = temp - ((year - 1900) * 365.0 + leapyrs);
+ } // ----------------- find remaing data -------------------------
+
+
+ var mdhms = days2mdhms(year, days);
+ var mon = mdhms.mon,
+ day = mdhms.day,
+ hr = mdhms.hr,
+ minute = mdhms.minute;
+ var sec = mdhms.sec - 0.00000086400;
+
+ if (asArray) {
+ return [year, mon, day, hr, minute, Math.floor(sec)];
+ }
+
+ return new Date(Date.UTC(year, mon - 1, day, hr, minute, Math.floor(sec)));
+ }
+
+ /* -----------------------------------------------------------------------------
+ *
+ * procedure dpper
+ *
+ * this procedure provides deep space long period periodic contributions
+ * to the mean elements. by design, these periodics are zero at epoch.
+ * this used to be dscom which included initialization, but it's really a
+ * recurring function.
+ *
+ * author : david vallado 719-573-2600 28 jun 2005
+ *
+ * inputs :
+ * e3 -
+ * ee2 -
+ * peo -
+ * pgho -
+ * pho -
+ * pinco -
+ * plo -
+ * se2 , se3 , sgh2, sgh3, sgh4, sh2, sh3, si2, si3, sl2, sl3, sl4 -
+ * t -
+ * xh2, xh3, xi2, xi3, xl2, xl3, xl4 -
+ * zmol -
+ * zmos -
+ * ep - eccentricity 0.0 - 1.0
+ * inclo - inclination - needed for lyddane modification
+ * nodep - right ascension of ascending node
+ * argpp - argument of perigee
+ * mp - mean anomaly
+ *
+ * outputs :
+ * ep - eccentricity 0.0 - 1.0
+ * inclp - inclination
+ * nodep - right ascension of ascending node
+ * argpp - argument of perigee
+ * mp - mean anomaly
+ *
+ * locals :
+ * alfdp -
+ * betdp -
+ * cosip , sinip , cosop , sinop ,
+ * dalf -
+ * dbet -
+ * dls -
+ * f2, f3 -
+ * pe -
+ * pgh -
+ * ph -
+ * pinc -
+ * pl -
+ * sel , ses , sghl , sghs , shl , shs , sil , sinzf , sis ,
+ * sll , sls
+ * xls -
+ * xnoh -
+ * zf -
+ * zm -
+ *
+ * coupling :
+ * none.
+ *
+ * references :
+ * hoots, roehrich, norad spacetrack report #3 1980
+ * hoots, norad spacetrack report #6 1986
+ * hoots, schumacher and glover 2004
+ * vallado, crawford, hujsak, kelso 2006
+ ----------------------------------------------------------------------------*/
+
+ function dpper(satrec, options) {
+ var e3 = satrec.e3,
+ ee2 = satrec.ee2,
+ peo = satrec.peo,
+ pgho = satrec.pgho,
+ pho = satrec.pho,
+ pinco = satrec.pinco,
+ plo = satrec.plo,
+ se2 = satrec.se2,
+ se3 = satrec.se3,
+ sgh2 = satrec.sgh2,
+ sgh3 = satrec.sgh3,
+ sgh4 = satrec.sgh4,
+ sh2 = satrec.sh2,
+ sh3 = satrec.sh3,
+ si2 = satrec.si2,
+ si3 = satrec.si3,
+ sl2 = satrec.sl2,
+ sl3 = satrec.sl3,
+ sl4 = satrec.sl4,
+ t = satrec.t,
+ xgh2 = satrec.xgh2,
+ xgh3 = satrec.xgh3,
+ xgh4 = satrec.xgh4,
+ xh2 = satrec.xh2,
+ xh3 = satrec.xh3,
+ xi2 = satrec.xi2,
+ xi3 = satrec.xi3,
+ xl2 = satrec.xl2,
+ xl3 = satrec.xl3,
+ xl4 = satrec.xl4,
+ zmol = satrec.zmol,
+ zmos = satrec.zmos;
+ var init = options.init,
+ opsmode = options.opsmode;
+ var ep = options.ep,
+ inclp = options.inclp,
+ nodep = options.nodep,
+ argpp = options.argpp,
+ mp = options.mp; // Copy satellite attributes into local variables for convenience
+ // and symmetry in writing formulae.
+
+ var alfdp;
+ var betdp;
+ var cosip;
+ var sinip;
+ var cosop;
+ var sinop;
+ var dalf;
+ var dbet;
+ var dls;
+ var f2;
+ var f3;
+ var pe;
+ var pgh;
+ var ph;
+ var pinc;
+ var pl;
+ var sinzf;
+ var xls;
+ var xnoh;
+ var zf;
+ var zm; // ---------------------- constants -----------------------------
+
+ var zns = 1.19459e-5;
+ var zes = 0.01675;
+ var znl = 1.5835218e-4;
+ var zel = 0.05490; // --------------- calculate time varying periodics -----------
+
+ zm = zmos + zns * t; // be sure that the initial call has time set to zero
+
+ if (init === 'y') {
+ zm = zmos;
+ }
+
+ zf = zm + 2.0 * zes * Math.sin(zm);
+ sinzf = Math.sin(zf);
+ f2 = 0.5 * sinzf * sinzf - 0.25;
+ f3 = -0.5 * sinzf * Math.cos(zf);
+ var ses = se2 * f2 + se3 * f3;
+ var sis = si2 * f2 + si3 * f3;
+ var sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf;
+ var sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf;
+ var shs = sh2 * f2 + sh3 * f3;
+ zm = zmol + znl * t;
+
+ if (init === 'y') {
+ zm = zmol;
+ }
+
+ zf = zm + 2.0 * zel * Math.sin(zm);
+ sinzf = Math.sin(zf);
+ f2 = 0.5 * sinzf * sinzf - 0.25;
+ f3 = -0.5 * sinzf * Math.cos(zf);
+ var sel = ee2 * f2 + e3 * f3;
+ var sil = xi2 * f2 + xi3 * f3;
+ var sll = xl2 * f2 + xl3 * f3 + xl4 * sinzf;
+ var sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf;
+ var shll = xh2 * f2 + xh3 * f3;
+ pe = ses + sel;
+ pinc = sis + sil;
+ pl = sls + sll;
+ pgh = sghs + sghl;
+ ph = shs + shll;
+
+ if (init === 'n') {
+ pe -= peo;
+ pinc -= pinco;
+ pl -= plo;
+ pgh -= pgho;
+ ph -= pho;
+ inclp += pinc;
+ ep += pe;
+ sinip = Math.sin(inclp);
+ cosip = Math.cos(inclp);
+ /* ----------------- apply periodics directly ------------ */
+ // sgp4fix for lyddane choice
+ // strn3 used original inclination - this is technically feasible
+ // gsfc used perturbed inclination - also technically feasible
+ // probably best to readjust the 0.2 limit value and limit discontinuity
+ // 0.2 rad = 11.45916 deg
+ // use next line for original strn3 approach and original inclination
+ // if (inclo >= 0.2)
+ // use next line for gsfc version and perturbed inclination
+
+ if (inclp >= 0.2) {
+ ph /= sinip;
+ pgh -= cosip * ph;
+ argpp += pgh;
+ nodep += ph;
+ mp += pl;
+ } else {
+ // ---- apply periodics with lyddane modification ----
+ sinop = Math.sin(nodep);
+ cosop = Math.cos(nodep);
+ alfdp = sinip * sinop;
+ betdp = sinip * cosop;
+ dalf = ph * cosop + pinc * cosip * sinop;
+ dbet = -ph * sinop + pinc * cosip * cosop;
+ alfdp += dalf;
+ betdp += dbet;
+ nodep %= twoPi; // sgp4fix for afspc written intrinsic functions
+ // nodep used without a trigonometric function ahead
+
+ if (nodep < 0.0 && opsmode === 'a') {
+ nodep += twoPi;
+ }
+
+ xls = mp + argpp + cosip * nodep;
+ dls = pl + pgh - pinc * nodep * sinip;
+ xls += dls;
+ xnoh = nodep;
+ nodep = Math.atan2(alfdp, betdp); // sgp4fix for afspc written intrinsic functions
+ // nodep used without a trigonometric function ahead
+
+ if (nodep < 0.0 && opsmode === 'a') {
+ nodep += twoPi;
+ }
+
+ if (Math.abs(xnoh - nodep) > pi) {
+ if (nodep < xnoh) {
+ nodep += twoPi;
+ } else {
+ nodep -= twoPi;
+ }
+ }
+
+ mp += pl;
+ argpp = xls - mp - cosip * nodep;
+ }
+ }
+
+ return {
+ ep: ep,
+ inclp: inclp,
+ nodep: nodep,
+ argpp: argpp,
+ mp: mp
+ };
+ }
+
+ /*-----------------------------------------------------------------------------
+ *
+ * procedure dscom
+ *
+ * this procedure provides deep space common items used by both the secular
+ * and periodics subroutines. input is provided as shown. this routine
+ * used to be called dpper, but the functions inside weren't well organized.
+ *
+ * author : david vallado 719-573-2600 28 jun 2005
+ *
+ * inputs :
+ * epoch -
+ * ep - eccentricity
+ * argpp - argument of perigee
+ * tc -
+ * inclp - inclination
+ * nodep - right ascension of ascending node
+ * np - mean motion
+ *
+ * outputs :
+ * sinim , cosim , sinomm , cosomm , snodm , cnodm
+ * day -
+ * e3 -
+ * ee2 -
+ * em - eccentricity
+ * emsq - eccentricity squared
+ * gam -
+ * peo -
+ * pgho -
+ * pho -
+ * pinco -
+ * plo -
+ * rtemsq -
+ * se2, se3 -
+ * sgh2, sgh3, sgh4 -
+ * sh2, sh3, si2, si3, sl2, sl3, sl4 -
+ * s1, s2, s3, s4, s5, s6, s7 -
+ * ss1, ss2, ss3, ss4, ss5, ss6, ss7, sz1, sz2, sz3 -
+ * sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33 -
+ * xgh2, xgh3, xgh4, xh2, xh3, xi2, xi3, xl2, xl3, xl4 -
+ * nm - mean motion
+ * z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33 -
+ * zmol -
+ * zmos -
+ *
+ * locals :
+ * a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 -
+ * betasq -
+ * cc -
+ * ctem, stem -
+ * x1, x2, x3, x4, x5, x6, x7, x8 -
+ * xnodce -
+ * xnoi -
+ * zcosg , zsing , zcosgl , zsingl , zcosh , zsinh , zcoshl , zsinhl ,
+ * zcosi , zsini , zcosil , zsinil ,
+ * zx -
+ * zy -
+ *
+ * coupling :
+ * none.
+ *
+ * references :
+ * hoots, roehrich, norad spacetrack report #3 1980
+ * hoots, norad spacetrack report #6 1986
+ * hoots, schumacher and glover 2004
+ * vallado, crawford, hujsak, kelso 2006
+ ----------------------------------------------------------------------------*/
+
+ function dscom(options) {
+ var epoch = options.epoch,
+ ep = options.ep,
+ argpp = options.argpp,
+ tc = options.tc,
+ inclp = options.inclp,
+ nodep = options.nodep,
+ np = options.np;
+ var a1;
+ var a2;
+ var a3;
+ var a4;
+ var a5;
+ var a6;
+ var a7;
+ var a8;
+ var a9;
+ var a10;
+ var cc;
+ var x1;
+ var x2;
+ var x3;
+ var x4;
+ var x5;
+ var x6;
+ var x7;
+ var x8;
+ var zcosg;
+ var zsing;
+ var zcosh;
+ var zsinh;
+ var zcosi;
+ var zsini;
+ var ss1;
+ var ss2;
+ var ss3;
+ var ss4;
+ var ss5;
+ var ss6;
+ var ss7;
+ var sz1;
+ var sz2;
+ var sz3;
+ var sz11;
+ var sz12;
+ var sz13;
+ var sz21;
+ var sz22;
+ var sz23;
+ var sz31;
+ var sz32;
+ var sz33;
+ var s1;
+ var s2;
+ var s3;
+ var s4;
+ var s5;
+ var s6;
+ var s7;
+ var z1;
+ var z2;
+ var z3;
+ var z11;
+ var z12;
+ var z13;
+ var z21;
+ var z22;
+ var z23;
+ var z31;
+ var z32;
+ var z33; // -------------------------- constants -------------------------
+
+ var zes = 0.01675;
+ var zel = 0.05490;
+ var c1ss = 2.9864797e-6;
+ var c1l = 4.7968065e-7;
+ var zsinis = 0.39785416;
+ var zcosis = 0.91744867;
+ var zcosgs = 0.1945905;
+ var zsings = -0.98088458; // --------------------- local variables ------------------------
+
+ var nm = np;
+ var em = ep;
+ var snodm = Math.sin(nodep);
+ var cnodm = Math.cos(nodep);
+ var sinomm = Math.sin(argpp);
+ var cosomm = Math.cos(argpp);
+ var sinim = Math.sin(inclp);
+ var cosim = Math.cos(inclp);
+ var emsq = em * em;
+ var betasq = 1.0 - emsq;
+ var rtemsq = Math.sqrt(betasq); // ----------------- initialize lunar solar terms ---------------
+
+ var peo = 0.0;
+ var pinco = 0.0;
+ var plo = 0.0;
+ var pgho = 0.0;
+ var pho = 0.0;
+ var day = epoch + 18261.5 + tc / 1440.0;
+ var xnodce = (4.5236020 - 9.2422029e-4 * day) % twoPi;
+ var stem = Math.sin(xnodce);
+ var ctem = Math.cos(xnodce);
+ var zcosil = 0.91375164 - 0.03568096 * ctem;
+ var zsinil = Math.sqrt(1.0 - zcosil * zcosil);
+ var zsinhl = 0.089683511 * stem / zsinil;
+ var zcoshl = Math.sqrt(1.0 - zsinhl * zsinhl);
+ var gam = 5.8351514 + 0.0019443680 * day;
+ var zx = 0.39785416 * stem / zsinil;
+ var zy = zcoshl * ctem + 0.91744867 * zsinhl * stem;
+ zx = Math.atan2(zx, zy);
+ zx += gam - xnodce;
+ var zcosgl = Math.cos(zx);
+ var zsingl = Math.sin(zx); // ------------------------- do solar terms ---------------------
+
+ zcosg = zcosgs;
+ zsing = zsings;
+ zcosi = zcosis;
+ zsini = zsinis;
+ zcosh = cnodm;
+ zsinh = snodm;
+ cc = c1ss;
+ var xnoi = 1.0 / nm;
+ var lsflg = 0;
+
+ while (lsflg < 2) {
+ lsflg += 1;
+ a1 = zcosg * zcosh + zsing * zcosi * zsinh;
+ a3 = -zsing * zcosh + zcosg * zcosi * zsinh;
+ a7 = -zcosg * zsinh + zsing * zcosi * zcosh;
+ a8 = zsing * zsini;
+ a9 = zsing * zsinh + zcosg * zcosi * zcosh;
+ a10 = zcosg * zsini;
+ a2 = cosim * a7 + sinim * a8;
+ a4 = cosim * a9 + sinim * a10;
+ a5 = -sinim * a7 + cosim * a8;
+ a6 = -sinim * a9 + cosim * a10;
+ x1 = a1 * cosomm + a2 * sinomm;
+ x2 = a3 * cosomm + a4 * sinomm;
+ x3 = -a1 * sinomm + a2 * cosomm;
+ x4 = -a3 * sinomm + a4 * cosomm;
+ x5 = a5 * sinomm;
+ x6 = a6 * sinomm;
+ x7 = a5 * cosomm;
+ x8 = a6 * cosomm;
+ z31 = 12.0 * x1 * x1 - 3.0 * x3 * x3;
+ z32 = 24.0 * x1 * x2 - 6.0 * x3 * x4;
+ z33 = 12.0 * x2 * x2 - 3.0 * x4 * x4;
+ z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * emsq;
+ z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * emsq;
+ z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * emsq;
+ z11 = -6.0 * a1 * a5 + emsq * (-24.0 * x1 * x7 - 6.0 * x3 * x5);
+ z12 = -6.0 * (a1 * a6 + a3 * a5) + emsq * (-24.0 * (x2 * x7 + x1 * x8) + -6.0 * (x3 * x6 + x4 * x5));
+ z13 = -6.0 * a3 * a6 + emsq * (-24.0 * x2 * x8 - 6.0 * x4 * x6);
+ z21 = 6.0 * a2 * a5 + emsq * (24.0 * x1 * x5 - 6.0 * x3 * x7);
+ z22 = 6.0 * (a4 * a5 + a2 * a6) + emsq * (24.0 * (x2 * x5 + x1 * x6) - 6.0 * (x4 * x7 + x3 * x8));
+ z23 = 6.0 * a4 * a6 + emsq * (24.0 * x2 * x6 - 6.0 * x4 * x8);
+ z1 = z1 + z1 + betasq * z31;
+ z2 = z2 + z2 + betasq * z32;
+ z3 = z3 + z3 + betasq * z33;
+ s3 = cc * xnoi;
+ s2 = -0.5 * s3 / rtemsq;
+ s4 = s3 * rtemsq;
+ s1 = -15.0 * em * s4;
+ s5 = x1 * x3 + x2 * x4;
+ s6 = x2 * x3 + x1 * x4;
+ s7 = x2 * x4 - x1 * x3; // ----------------------- do lunar terms -------------------
+
+ if (lsflg === 1) {
+ ss1 = s1;
+ ss2 = s2;
+ ss3 = s3;
+ ss4 = s4;
+ ss5 = s5;
+ ss6 = s6;
+ ss7 = s7;
+ sz1 = z1;
+ sz2 = z2;
+ sz3 = z3;
+ sz11 = z11;
+ sz12 = z12;
+ sz13 = z13;
+ sz21 = z21;
+ sz22 = z22;
+ sz23 = z23;
+ sz31 = z31;
+ sz32 = z32;
+ sz33 = z33;
+ zcosg = zcosgl;
+ zsing = zsingl;
+ zcosi = zcosil;
+ zsini = zsinil;
+ zcosh = zcoshl * cnodm + zsinhl * snodm;
+ zsinh = snodm * zcoshl - cnodm * zsinhl;
+ cc = c1l;
+ }
+ }
+
+ var zmol = (4.7199672 + (0.22997150 * day - gam)) % twoPi;
+ var zmos = (6.2565837 + 0.017201977 * day) % twoPi; // ------------------------ do solar terms ----------------------
+
+ var se2 = 2.0 * ss1 * ss6;
+ var se3 = 2.0 * ss1 * ss7;
+ var si2 = 2.0 * ss2 * sz12;
+ var si3 = 2.0 * ss2 * (sz13 - sz11);
+ var sl2 = -2.0 * ss3 * sz2;
+ var sl3 = -2.0 * ss3 * (sz3 - sz1);
+ var sl4 = -2.0 * ss3 * (-21.0 - 9.0 * emsq) * zes;
+ var sgh2 = 2.0 * ss4 * sz32;
+ var sgh3 = 2.0 * ss4 * (sz33 - sz31);
+ var sgh4 = -18.0 * ss4 * zes;
+ var sh2 = -2.0 * ss2 * sz22;
+ var sh3 = -2.0 * ss2 * (sz23 - sz21); // ------------------------ do lunar terms ----------------------
+
+ var ee2 = 2.0 * s1 * s6;
+ var e3 = 2.0 * s1 * s7;
+ var xi2 = 2.0 * s2 * z12;
+ var xi3 = 2.0 * s2 * (z13 - z11);
+ var xl2 = -2.0 * s3 * z2;
+ var xl3 = -2.0 * s3 * (z3 - z1);
+ var xl4 = -2.0 * s3 * (-21.0 - 9.0 * emsq) * zel;
+ var xgh2 = 2.0 * s4 * z32;
+ var xgh3 = 2.0 * s4 * (z33 - z31);
+ var xgh4 = -18.0 * s4 * zel;
+ var xh2 = -2.0 * s2 * z22;
+ var xh3 = -2.0 * s2 * (z23 - z21);
+ return {
+ snodm: snodm,
+ cnodm: cnodm,
+ sinim: sinim,
+ cosim: cosim,
+ sinomm: sinomm,
+ cosomm: cosomm,
+ day: day,
+ e3: e3,
+ ee2: ee2,
+ em: em,
+ emsq: emsq,
+ gam: gam,
+ peo: peo,
+ pgho: pgho,
+ pho: pho,
+ pinco: pinco,
+ plo: plo,
+ rtemsq: rtemsq,
+ se2: se2,
+ se3: se3,
+ sgh2: sgh2,
+ sgh3: sgh3,
+ sgh4: sgh4,
+ sh2: sh2,
+ sh3: sh3,
+ si2: si2,
+ si3: si3,
+ sl2: sl2,
+ sl3: sl3,
+ sl4: sl4,
+ s1: s1,
+ s2: s2,
+ s3: s3,
+ s4: s4,
+ s5: s5,
+ s6: s6,
+ s7: s7,
+ ss1: ss1,
+ ss2: ss2,
+ ss3: ss3,
+ ss4: ss4,
+ ss5: ss5,
+ ss6: ss6,
+ ss7: ss7,
+ sz1: sz1,
+ sz2: sz2,
+ sz3: sz3,
+ sz11: sz11,
+ sz12: sz12,
+ sz13: sz13,
+ sz21: sz21,
+ sz22: sz22,
+ sz23: sz23,
+ sz31: sz31,
+ sz32: sz32,
+ sz33: sz33,
+ xgh2: xgh2,
+ xgh3: xgh3,
+ xgh4: xgh4,
+ xh2: xh2,
+ xh3: xh3,
+ xi2: xi2,
+ xi3: xi3,
+ xl2: xl2,
+ xl3: xl3,
+ xl4: xl4,
+ nm: nm,
+ z1: z1,
+ z2: z2,
+ z3: z3,
+ z11: z11,
+ z12: z12,
+ z13: z13,
+ z21: z21,
+ z22: z22,
+ z23: z23,
+ z31: z31,
+ z32: z32,
+ z33: z33,
+ zmol: zmol,
+ zmos: zmos
+ };
+ }
+
+ /*-----------------------------------------------------------------------------
+ *
+ * procedure dsinit
+ *
+ * this procedure provides deep space contributions to mean motion dot due
+ * to geopotential resonance with half day and one day orbits.
+ *
+ * author : david vallado 719-573-2600 28 jun 2005
+ *
+ * inputs :
+ * cosim, sinim-
+ * emsq - eccentricity squared
+ * argpo - argument of perigee
+ * s1, s2, s3, s4, s5 -
+ * ss1, ss2, ss3, ss4, ss5 -
+ * sz1, sz3, sz11, sz13, sz21, sz23, sz31, sz33 -
+ * t - time
+ * tc -
+ * gsto - greenwich sidereal time rad
+ * mo - mean anomaly
+ * mdot - mean anomaly dot (rate)
+ * no - mean motion
+ * nodeo - right ascension of ascending node
+ * nodedot - right ascension of ascending node dot (rate)
+ * xpidot -
+ * z1, z3, z11, z13, z21, z23, z31, z33 -
+ * eccm - eccentricity
+ * argpm - argument of perigee
+ * inclm - inclination
+ * mm - mean anomaly
+ * xn - mean motion
+ * nodem - right ascension of ascending node
+ *
+ * outputs :
+ * em - eccentricity
+ * argpm - argument of perigee
+ * inclm - inclination
+ * mm - mean anomaly
+ * nm - mean motion
+ * nodem - right ascension of ascending node
+ * irez - flag for resonance 0-none, 1-one day, 2-half day
+ * atime -
+ * d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433 -
+ * dedt -
+ * didt -
+ * dmdt -
+ * dndt -
+ * dnodt -
+ * domdt -
+ * del1, del2, del3 -
+ * ses , sghl , sghs , sgs , shl , shs , sis , sls
+ * theta -
+ * xfact -
+ * xlamo -
+ * xli -
+ * xni
+ *
+ * locals :
+ * ainv2 -
+ * aonv -
+ * cosisq -
+ * eoc -
+ * f220, f221, f311, f321, f322, f330, f441, f442, f522, f523, f542, f543 -
+ * g200, g201, g211, g300, g310, g322, g410, g422, g520, g521, g532, g533 -
+ * sini2 -
+ * temp -
+ * temp1 -
+ * theta -
+ * xno2 -
+ *
+ * coupling :
+ * getgravconst
+ *
+ * references :
+ * hoots, roehrich, norad spacetrack report #3 1980
+ * hoots, norad spacetrack report #6 1986
+ * hoots, schumacher and glover 2004
+ * vallado, crawford, hujsak, kelso 2006
+ ----------------------------------------------------------------------------*/
+
+ function dsinit(options) {
+ var cosim = options.cosim,
+ argpo = options.argpo,
+ s1 = options.s1,
+ s2 = options.s2,
+ s3 = options.s3,
+ s4 = options.s4,
+ s5 = options.s5,
+ sinim = options.sinim,
+ ss1 = options.ss1,
+ ss2 = options.ss2,
+ ss3 = options.ss3,
+ ss4 = options.ss4,
+ ss5 = options.ss5,
+ sz1 = options.sz1,
+ sz3 = options.sz3,
+ sz11 = options.sz11,
+ sz13 = options.sz13,
+ sz21 = options.sz21,
+ sz23 = options.sz23,
+ sz31 = options.sz31,
+ sz33 = options.sz33,
+ t = options.t,
+ tc = options.tc,
+ gsto = options.gsto,
+ mo = options.mo,
+ mdot = options.mdot,
+ no = options.no,
+ nodeo = options.nodeo,
+ nodedot = options.nodedot,
+ xpidot = options.xpidot,
+ z1 = options.z1,
+ z3 = options.z3,
+ z11 = options.z11,
+ z13 = options.z13,
+ z21 = options.z21,
+ z23 = options.z23,
+ z31 = options.z31,
+ z33 = options.z33,
+ ecco = options.ecco,
+ eccsq = options.eccsq;
+ var emsq = options.emsq,
+ em = options.em,
+ argpm = options.argpm,
+ inclm = options.inclm,
+ mm = options.mm,
+ nm = options.nm,
+ nodem = options.nodem,
+ irez = options.irez,
+ atime = options.atime,
+ d2201 = options.d2201,
+ d2211 = options.d2211,
+ d3210 = options.d3210,
+ d3222 = options.d3222,
+ d4410 = options.d4410,
+ d4422 = options.d4422,
+ d5220 = options.d5220,
+ d5232 = options.d5232,
+ d5421 = options.d5421,
+ d5433 = options.d5433,
+ dedt = options.dedt,
+ didt = options.didt,
+ dmdt = options.dmdt,
+ dnodt = options.dnodt,
+ domdt = options.domdt,
+ del1 = options.del1,
+ del2 = options.del2,
+ del3 = options.del3,
+ xfact = options.xfact,
+ xlamo = options.xlamo,
+ xli = options.xli,
+ xni = options.xni;
+ var f220;
+ var f221;
+ var f311;
+ var f321;
+ var f322;
+ var f330;
+ var f441;
+ var f442;
+ var f522;
+ var f523;
+ var f542;
+ var f543;
+ var g200;
+ var g201;
+ var g211;
+ var g300;
+ var g310;
+ var g322;
+ var g410;
+ var g422;
+ var g520;
+ var g521;
+ var g532;
+ var g533;
+ var sini2;
+ var temp;
+ var temp1;
+ var xno2;
+ var ainv2;
+ var aonv;
+ var cosisq;
+ var eoc;
+ var q22 = 1.7891679e-6;
+ var q31 = 2.1460748e-6;
+ var q33 = 2.2123015e-7;
+ var root22 = 1.7891679e-6;
+ var root44 = 7.3636953e-9;
+ var root54 = 2.1765803e-9;
+ var rptim = 4.37526908801129966e-3; // equates to 7.29211514668855e-5 rad/sec
+
+ var root32 = 3.7393792e-7;
+ var root52 = 1.1428639e-7;
+ var znl = 1.5835218e-4;
+ var zns = 1.19459e-5; // -------------------- deep space initialization ------------
+
+ irez = 0;
+
+ if (nm < 0.0052359877 && nm > 0.0034906585) {
+ irez = 1;
+ }
+
+ if (nm >= 8.26e-3 && nm <= 9.24e-3 && em >= 0.5) {
+ irez = 2;
+ } // ------------------------ do solar terms -------------------
+
+
+ var ses = ss1 * zns * ss5;
+ var sis = ss2 * zns * (sz11 + sz13);
+ var sls = -zns * ss3 * (sz1 + sz3 - 14.0 - 6.0 * emsq);
+ var sghs = ss4 * zns * (sz31 + sz33 - 6.0);
+ var shs = -zns * ss2 * (sz21 + sz23); // sgp4fix for 180 deg incl
+
+ if (inclm < 5.2359877e-2 || inclm > pi - 5.2359877e-2) {
+ shs = 0.0;
+ }
+
+ if (sinim !== 0.0) {
+ shs /= sinim;
+ }
+
+ var sgs = sghs - cosim * shs; // ------------------------- do lunar terms ------------------
+
+ dedt = ses + s1 * znl * s5;
+ didt = sis + s2 * znl * (z11 + z13);
+ dmdt = sls - znl * s3 * (z1 + z3 - 14.0 - 6.0 * emsq);
+ var sghl = s4 * znl * (z31 + z33 - 6.0);
+ var shll = -znl * s2 * (z21 + z23); // sgp4fix for 180 deg incl
+
+ if (inclm < 5.2359877e-2 || inclm > pi - 5.2359877e-2) {
+ shll = 0.0;
+ }
+
+ domdt = sgs + sghl;
+ dnodt = shs;
+
+ if (sinim !== 0.0) {
+ domdt -= cosim / sinim * shll;
+ dnodt += shll / sinim;
+ } // ----------- calculate deep space resonance effects --------
+
+
+ var dndt = 0.0;
+ var theta = (gsto + tc * rptim) % twoPi;
+ em += dedt * t;
+ inclm += didt * t;
+ argpm += domdt * t;
+ nodem += dnodt * t;
+ mm += dmdt * t; // sgp4fix for negative inclinations
+ // the following if statement should be commented out
+ // if (inclm < 0.0)
+ // {
+ // inclm = -inclm;
+ // argpm = argpm - pi;
+ // nodem = nodem + pi;
+ // }
+ // -------------- initialize the resonance terms -------------
+
+ if (irez !== 0) {
+ aonv = Math.pow(nm / xke, x2o3); // ---------- geopotential resonance for 12 hour orbits ------
+
+ if (irez === 2) {
+ cosisq = cosim * cosim;
+ var emo = em;
+ em = ecco;
+ var emsqo = emsq;
+ emsq = eccsq;
+ eoc = em * emsq;
+ g201 = -0.306 - (em - 0.64) * 0.440;
+
+ if (em <= 0.65) {
+ g211 = 3.616 - 13.2470 * em + 16.2900 * emsq;
+ g310 = -19.302 + 117.3900 * em - 228.4190 * emsq + 156.5910 * eoc;
+ g322 = -18.9068 + 109.7927 * em - 214.6334 * emsq + 146.5816 * eoc;
+ g410 = -41.122 + 242.6940 * em - 471.0940 * emsq + 313.9530 * eoc;
+ g422 = -146.407 + 841.8800 * em - 1629.014 * emsq + 1083.4350 * eoc;
+ g520 = -532.114 + 3017.977 * em - 5740.032 * emsq + 3708.2760 * eoc;
+ } else {
+ g211 = -72.099 + 331.819 * em - 508.738 * emsq + 266.724 * eoc;
+ g310 = -346.844 + 1582.851 * em - 2415.925 * emsq + 1246.113 * eoc;
+ g322 = -342.585 + 1554.908 * em - 2366.899 * emsq + 1215.972 * eoc;
+ g410 = -1052.797 + 4758.686 * em - 7193.992 * emsq + 3651.957 * eoc;
+ g422 = -3581.690 + 16178.110 * em - 24462.770 * emsq + 12422.520 * eoc;
+
+ if (em > 0.715) {
+ g520 = -5149.66 + 29936.92 * em - 54087.36 * emsq + 31324.56 * eoc;
+ } else {
+ g520 = 1464.74 - 4664.75 * em + 3763.64 * emsq;
+ }
+ }
+
+ if (em < 0.7) {
+ g533 = -919.22770 + 4988.6100 * em - 9064.7700 * emsq + 5542.21 * eoc;
+ g521 = -822.71072 + 4568.6173 * em - 8491.4146 * emsq + 5337.524 * eoc;
+ g532 = -853.66600 + 4690.2500 * em - 8624.7700 * emsq + 5341.4 * eoc;
+ } else {
+ g533 = -37995.780 + 161616.52 * em - 229838.20 * emsq + 109377.94 * eoc;
+ g521 = -51752.104 + 218913.95 * em - 309468.16 * emsq + 146349.42 * eoc;
+ g532 = -40023.880 + 170470.89 * em - 242699.48 * emsq + 115605.82 * eoc;
+ }
+
+ sini2 = sinim * sinim;
+ f220 = 0.75 * (1.0 + 2.0 * cosim + cosisq);
+ f221 = 1.5 * sini2;
+ f321 = 1.875 * sinim * (1.0 - 2.0 * cosim - 3.0 * cosisq);
+ f322 = -1.875 * sinim * (1.0 + 2.0 * cosim - 3.0 * cosisq);
+ f441 = 35.0 * sini2 * f220;
+ f442 = 39.3750 * sini2 * sini2;
+ f522 = 9.84375 * sinim * (sini2 * (1.0 - 2.0 * cosim - 5.0 * cosisq) + 0.33333333 * (-2.0 + 4.0 * cosim + 6.0 * cosisq));
+ f523 = sinim * (4.92187512 * sini2 * (-2.0 - 4.0 * cosim + 10.0 * cosisq) + 6.56250012 * (1.0 + 2.0 * cosim - 3.0 * cosisq));
+ f542 = 29.53125 * sinim * (2.0 - 8.0 * cosim + cosisq * (-12.0 + 8.0 * cosim + 10.0 * cosisq));
+ f543 = 29.53125 * sinim * (-2.0 - 8.0 * cosim + cosisq * (12.0 + 8.0 * cosim - 10.0 * cosisq));
+ xno2 = nm * nm;
+ ainv2 = aonv * aonv;
+ temp1 = 3.0 * xno2 * ainv2;
+ temp = temp1 * root22;
+ d2201 = temp * f220 * g201;
+ d2211 = temp * f221 * g211;
+ temp1 *= aonv;
+ temp = temp1 * root32;
+ d3210 = temp * f321 * g310;
+ d3222 = temp * f322 * g322;
+ temp1 *= aonv;
+ temp = 2.0 * temp1 * root44;
+ d4410 = temp * f441 * g410;
+ d4422 = temp * f442 * g422;
+ temp1 *= aonv;
+ temp = temp1 * root52;
+ d5220 = temp * f522 * g520;
+ d5232 = temp * f523 * g532;
+ temp = 2.0 * temp1 * root54;
+ d5421 = temp * f542 * g521;
+ d5433 = temp * f543 * g533;
+ xlamo = (mo + nodeo + nodeo - (theta + theta)) % twoPi;
+ xfact = mdot + dmdt + 2.0 * (nodedot + dnodt - rptim) - no;
+ em = emo;
+ emsq = emsqo;
+ } // ---------------- synchronous resonance terms --------------
+
+
+ if (irez === 1) {
+ g200 = 1.0 + emsq * (-2.5 + 0.8125 * emsq);
+ g310 = 1.0 + 2.0 * emsq;
+ g300 = 1.0 + emsq * (-6.0 + 6.60937 * emsq);
+ f220 = 0.75 * (1.0 + cosim) * (1.0 + cosim);
+ f311 = 0.9375 * sinim * sinim * (1.0 + 3.0 * cosim) - 0.75 * (1.0 + cosim);
+ f330 = 1.0 + cosim;
+ f330 *= 1.875 * f330 * f330;
+ del1 = 3.0 * nm * nm * aonv * aonv;
+ del2 = 2.0 * del1 * f220 * g200 * q22;
+ del3 = 3.0 * del1 * f330 * g300 * q33 * aonv;
+ del1 = del1 * f311 * g310 * q31 * aonv;
+ xlamo = (mo + nodeo + argpo - theta) % twoPi;
+ xfact = mdot + xpidot + dmdt + domdt + dnodt - (no + rptim);
+ } // ------------ for sgp4, initialize the integrator ----------
+
+
+ xli = xlamo;
+ xni = no;
+ atime = 0.0;
+ nm = no + dndt;
+ }
+
+ return {
+ em: em,
+ argpm: argpm,
+ inclm: inclm,
+ mm: mm,
+ nm: nm,
+ nodem: nodem,
+ irez: irez,
+ atime: atime,
+ d2201: d2201,
+ d2211: d2211,
+ d3210: d3210,
+ d3222: d3222,
+ d4410: d4410,
+ d4422: d4422,
+ d5220: d5220,
+ d5232: d5232,
+ d5421: d5421,
+ d5433: d5433,
+ dedt: dedt,
+ didt: didt,
+ dmdt: dmdt,
+ dndt: dndt,
+ dnodt: dnodt,
+ domdt: domdt,
+ del1: del1,
+ del2: del2,
+ del3: del3,
+ xfact: xfact,
+ xlamo: xlamo,
+ xli: xli,
+ xni: xni
+ };
+ }
+
+ /* -----------------------------------------------------------------------------
+ *
+ * function gstime
+ *
+ * this function finds the greenwich sidereal time.
+ *
+ * author : david vallado 719-573-2600 1 mar 2001
+ *
+ * inputs description range / units
+ * jdut1 - julian date in ut1 days from 4713 bc
+ *
+ * outputs :
+ * gstime - greenwich sidereal time 0 to 2pi rad
+ *
+ * locals :
+ * temp - temporary variable for doubles rad
+ * tut1 - julian centuries from the
+ * jan 1, 2000 12 h epoch (ut1)
+ *
+ * coupling :
+ * none
+ *
+ * references :
+ * vallado 2004, 191, eq 3-45
+ * --------------------------------------------------------------------------- */
+
+ function gstimeInternal(jdut1) {
+ var tut1 = (jdut1 - 2451545.0) / 36525.0;
+ var temp = -6.2e-6 * tut1 * tut1 * tut1 + 0.093104 * tut1 * tut1 + (876600.0 * 3600 + 8640184.812866) * tut1 + 67310.54841; // # sec
+
+ temp = temp * deg2rad / 240.0 % twoPi; // 360/86400 = 1/240, to deg, to rad
+ // ------------------------ check quadrants ---------------------
+
+ if (temp < 0.0) {
+ temp += twoPi;
+ }
+
+ return temp;
+ }
+
+ function gstime() {
+ if ((arguments.length <= 0 ? undefined : arguments[0]) instanceof Date || arguments.length > 1) {
+ return gstimeInternal(jday.apply(void 0, arguments));
+ }
+
+ return gstimeInternal.apply(void 0, arguments);
+ }
+
+ /*-----------------------------------------------------------------------------
+ *
+ * procedure initl
+ *
+ * this procedure initializes the sgp4 propagator. all the initialization is
+ * consolidated here instead of having multiple loops inside other routines.
+ *
+ * author : david vallado 719-573-2600 28 jun 2005
+ *
+ * inputs :
+ * ecco - eccentricity 0.0 - 1.0
+ * epoch - epoch time in days from jan 0, 1950. 0 hr
+ * inclo - inclination of satellite
+ * no - mean motion of satellite
+ * satn - satellite number
+ *
+ * outputs :
+ * ainv - 1.0 / a
+ * ao - semi major axis
+ * con41 -
+ * con42 - 1.0 - 5.0 cos(i)
+ * cosio - cosine of inclination
+ * cosio2 - cosio squared
+ * eccsq - eccentricity squared
+ * method - flag for deep space 'd', 'n'
+ * omeosq - 1.0 - ecco * ecco
+ * posq - semi-parameter squared
+ * rp - radius of perigee
+ * rteosq - square root of (1.0 - ecco*ecco)
+ * sinio - sine of inclination
+ * gsto - gst at time of observation rad
+ * no - mean motion of satellite
+ *
+ * locals :
+ * ak -
+ * d1 -
+ * del -
+ * adel -
+ * po -
+ *
+ * coupling :
+ * getgravconst
+ * gstime - find greenwich sidereal time from the julian date
+ *
+ * references :
+ * hoots, roehrich, norad spacetrack report #3 1980
+ * hoots, norad spacetrack report #6 1986
+ * hoots, schumacher and glover 2004
+ * vallado, crawford, hujsak, kelso 2006
+ ----------------------------------------------------------------------------*/
+
+ function initl(options) {
+ var ecco = options.ecco,
+ epoch = options.epoch,
+ inclo = options.inclo,
+ opsmode = options.opsmode;
+ var no = options.no; // sgp4fix use old way of finding gst
+ // ----------------------- earth constants ---------------------
+ // sgp4fix identify constants and allow alternate values
+ // ------------- calculate auxillary epoch quantities ----------
+
+ var eccsq = ecco * ecco;
+ var omeosq = 1.0 - eccsq;
+ var rteosq = Math.sqrt(omeosq);
+ var cosio = Math.cos(inclo);
+ var cosio2 = cosio * cosio; // ------------------ un-kozai the mean motion -----------------
+
+ var ak = Math.pow(xke / no, x2o3);
+ var d1 = 0.75 * j2 * (3.0 * cosio2 - 1.0) / (rteosq * omeosq);
+ var delPrime = d1 / (ak * ak);
+ var adel = ak * (1.0 - delPrime * delPrime - delPrime * (1.0 / 3.0 + 134.0 * delPrime * delPrime / 81.0));
+ delPrime = d1 / (adel * adel);
+ no /= 1.0 + delPrime;
+ var ao = Math.pow(xke / no, x2o3);
+ var sinio = Math.sin(inclo);
+ var po = ao * omeosq;
+ var con42 = 1.0 - 5.0 * cosio2;
+ var con41 = -con42 - cosio2 - cosio2;
+ var ainv = 1.0 / ao;
+ var posq = po * po;
+ var rp = ao * (1.0 - ecco);
+ var method = 'n'; // sgp4fix modern approach to finding sidereal time
+
+ var gsto;
+
+ if (opsmode === 'a') {
+ // sgp4fix use old way of finding gst
+ // count integer number of days from 0 jan 1970
+ var ts70 = epoch - 7305.0;
+ var ds70 = Math.floor(ts70 + 1.0e-8);
+ var tfrac = ts70 - ds70; // find greenwich location at epoch
+
+ var c1 = 1.72027916940703639e-2;
+ var thgr70 = 1.7321343856509374;
+ var fk5r = 5.07551419432269442e-15;
+ var c1p2p = c1 + twoPi;
+ gsto = (thgr70 + c1 * ds70 + c1p2p * tfrac + ts70 * ts70 * fk5r) % twoPi;
+
+ if (gsto < 0.0) {
+ gsto += twoPi;
+ }
+ } else {
+ gsto = gstime(epoch + 2433281.5);
+ }
+
+ return {
+ no: no,
+ method: method,
+ ainv: ainv,
+ ao: ao,
+ con41: con41,
+ con42: con42,
+ cosio: cosio,
+ cosio2: cosio2,
+ eccsq: eccsq,
+ omeosq: omeosq,
+ posq: posq,
+ rp: rp,
+ rteosq: rteosq,
+ sinio: sinio,
+ gsto: gsto
+ };
+ }
+
+ /*-----------------------------------------------------------------------------
+ *
+ * procedure dspace
+ *
+ * this procedure provides deep space contributions to mean elements for
+ * perturbing third body. these effects have been averaged over one
+ * revolution of the sun and moon. for earth resonance effects, the
+ * effects have been averaged over no revolutions of the satellite.
+ * (mean motion)
+ *
+ * author : david vallado 719-573-2600 28 jun 2005
+ *
+ * inputs :
+ * d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433 -
+ * dedt -
+ * del1, del2, del3 -
+ * didt -
+ * dmdt -
+ * dnodt -
+ * domdt -
+ * irez - flag for resonance 0-none, 1-one day, 2-half day
+ * argpo - argument of perigee
+ * argpdot - argument of perigee dot (rate)
+ * t - time
+ * tc -
+ * gsto - gst
+ * xfact -
+ * xlamo -
+ * no - mean motion
+ * atime -
+ * em - eccentricity
+ * ft -
+ * argpm - argument of perigee
+ * inclm - inclination
+ * xli -
+ * mm - mean anomaly
+ * xni - mean motion
+ * nodem - right ascension of ascending node
+ *
+ * outputs :
+ * atime -
+ * em - eccentricity
+ * argpm - argument of perigee
+ * inclm - inclination
+ * xli -
+ * mm - mean anomaly
+ * xni -
+ * nodem - right ascension of ascending node
+ * dndt -
+ * nm - mean motion
+ *
+ * locals :
+ * delt -
+ * ft -
+ * theta -
+ * x2li -
+ * x2omi -
+ * xl -
+ * xldot -
+ * xnddt -
+ * xndt -
+ * xomi -
+ *
+ * coupling :
+ * none -
+ *
+ * references :
+ * hoots, roehrich, norad spacetrack report #3 1980
+ * hoots, norad spacetrack report #6 1986
+ * hoots, schumacher and glover 2004
+ * vallado, crawford, hujsak, kelso 2006
+ ----------------------------------------------------------------------------*/
+
+ function dspace(options) {
+ var irez = options.irez,
+ d2201 = options.d2201,
+ d2211 = options.d2211,
+ d3210 = options.d3210,
+ d3222 = options.d3222,
+ d4410 = options.d4410,
+ d4422 = options.d4422,
+ d5220 = options.d5220,
+ d5232 = options.d5232,
+ d5421 = options.d5421,
+ d5433 = options.d5433,
+ dedt = options.dedt,
+ del1 = options.del1,
+ del2 = options.del2,
+ del3 = options.del3,
+ didt = options.didt,
+ dmdt = options.dmdt,
+ dnodt = options.dnodt,
+ domdt = options.domdt,
+ argpo = options.argpo,
+ argpdot = options.argpdot,
+ t = options.t,
+ tc = options.tc,
+ gsto = options.gsto,
+ xfact = options.xfact,
+ xlamo = options.xlamo,
+ no = options.no;
+ var atime = options.atime,
+ em = options.em,
+ argpm = options.argpm,
+ inclm = options.inclm,
+ xli = options.xli,
+ mm = options.mm,
+ xni = options.xni,
+ nodem = options.nodem,
+ nm = options.nm;
+ var fasx2 = 0.13130908;
+ var fasx4 = 2.8843198;
+ var fasx6 = 0.37448087;
+ var g22 = 5.7686396;
+ var g32 = 0.95240898;
+ var g44 = 1.8014998;
+ var g52 = 1.0508330;
+ var g54 = 4.4108898;
+ var rptim = 4.37526908801129966e-3; // equates to 7.29211514668855e-5 rad/sec
+
+ var stepp = 720.0;
+ var stepn = -720.0;
+ var step2 = 259200.0;
+ var delt;
+ var x2li;
+ var x2omi;
+ var xl;
+ var xldot;
+ var xnddt;
+ var xndt;
+ var xomi;
+ var dndt = 0.0;
+ var ft = 0.0; // ----------- calculate deep space resonance effects -----------
+
+ var theta = (gsto + tc * rptim) % twoPi;
+ em += dedt * t;
+ inclm += didt * t;
+ argpm += domdt * t;
+ nodem += dnodt * t;
+ mm += dmdt * t; // sgp4fix for negative inclinations
+ // the following if statement should be commented out
+ // if (inclm < 0.0)
+ // {
+ // inclm = -inclm;
+ // argpm = argpm - pi;
+ // nodem = nodem + pi;
+ // }
+
+ /* - update resonances : numerical (euler-maclaurin) integration - */
+
+ /* ------------------------- epoch restart ---------------------- */
+ // sgp4fix for propagator problems
+ // the following integration works for negative time steps and periods
+ // the specific changes are unknown because the original code was so convoluted
+ // sgp4fix take out atime = 0.0 and fix for faster operation
+
+ if (irez !== 0) {
+ // sgp4fix streamline check
+ if (atime === 0.0 || t * atime <= 0.0 || Math.abs(t) < Math.abs(atime)) {
+ atime = 0.0;
+ xni = no;
+ xli = xlamo;
+ } // sgp4fix move check outside loop
+
+
+ if (t > 0.0) {
+ delt = stepp;
+ } else {
+ delt = stepn;
+ }
+
+ var iretn = 381; // added for do loop
+
+ while (iretn === 381) {
+ // ------------------- dot terms calculated -------------
+ // ----------- near - synchronous resonance terms -------
+ if (irez !== 2) {
+ xndt = del1 * Math.sin(xli - fasx2) + del2 * Math.sin(2.0 * (xli - fasx4)) + del3 * Math.sin(3.0 * (xli - fasx6));
+ xldot = xni + xfact;
+ xnddt = del1 * Math.cos(xli - fasx2) + 2.0 * del2 * Math.cos(2.0 * (xli - fasx4)) + 3.0 * del3 * Math.cos(3.0 * (xli - fasx6));
+ xnddt *= xldot;
+ } else {
+ // --------- near - half-day resonance terms --------
+ xomi = argpo + argpdot * atime;
+ x2omi = xomi + xomi;
+ x2li = xli + xli;
+ xndt = d2201 * Math.sin(x2omi + xli - g22) + d2211 * Math.sin(xli - g22) + d3210 * Math.sin(xomi + xli - g32) + d3222 * Math.sin(-xomi + xli - g32) + d4410 * Math.sin(x2omi + x2li - g44) + d4422 * Math.sin(x2li - g44) + d5220 * Math.sin(xomi + xli - g52) + d5232 * Math.sin(-xomi + xli - g52) + d5421 * Math.sin(xomi + x2li - g54) + d5433 * Math.sin(-xomi + x2li - g54);
+ xldot = xni + xfact;
+ xnddt = d2201 * Math.cos(x2omi + xli - g22) + d2211 * Math.cos(xli - g22) + d3210 * Math.cos(xomi + xli - g32) + d3222 * Math.cos(-xomi + xli - g32) + d5220 * Math.cos(xomi + xli - g52) + d5232 * Math.cos(-xomi + xli - g52) + 2.0 * d4410 * Math.cos(x2omi + x2li - g44) + d4422 * Math.cos(x2li - g44) + d5421 * Math.cos(xomi + x2li - g54) + d5433 * Math.cos(-xomi + x2li - g54);
+ xnddt *= xldot;
+ } // ----------------------- integrator -------------------
+ // sgp4fix move end checks to end of routine
+
+
+ if (Math.abs(t - atime) >= stepp) {
+ iretn = 381;
+ } else {
+ ft = t - atime;
+ iretn = 0;
+ }
+
+ if (iretn === 381) {
+ xli += xldot * delt + xndt * step2;
+ xni += xndt * delt + xnddt * step2;
+ atime += delt;
+ }
+ }
+
+ nm = xni + xndt * ft + xnddt * ft * ft * 0.5;
+ xl = xli + xldot * ft + xndt * ft * ft * 0.5;
+
+ if (irez !== 1) {
+ mm = xl - 2.0 * nodem + 2.0 * theta;
+ dndt = nm - no;
+ } else {
+ mm = xl - nodem - argpm + theta;
+ dndt = nm - no;
+ }
+
+ nm = no + dndt;
+ }
+
+ return {
+ atime: atime,
+ em: em,
+ argpm: argpm,
+ inclm: inclm,
+ xli: xli,
+ mm: mm,
+ xni: xni,
+ nodem: nodem,
+ dndt: dndt,
+ nm: nm
+ };
+ }
+
+ /*----------------------------------------------------------------------------
+ *
+ * procedure sgp4
+ *
+ * this procedure is the sgp4 prediction model from space command. this is an
+ * updated and combined version of sgp4 and sdp4, which were originally
+ * published separately in spacetrack report //3. this version follows the
+ * methodology from the aiaa paper (2006) describing the history and
+ * development of the code.
+ *
+ * author : david vallado 719-573-2600 28 jun 2005
+ *
+ * inputs :
+ * satrec - initialised structure from sgp4init() call.
+ * tsince - time since epoch (minutes)
+ *
+ * outputs :
+ * r - position vector km
+ * v - velocity km/sec
+ * return code - non-zero on error.
+ * 1 - mean elements, ecc >= 1.0 or ecc < -0.001 or a < 0.95 er
+ * 2 - mean motion less than 0.0
+ * 3 - pert elements, ecc < 0.0 or ecc > 1.0
+ * 4 - semi-latus rectum < 0.0
+ * 5 - epoch elements are sub-orbital
+ * 6 - satellite has decayed
+ *
+ * locals :
+ * am -
+ * axnl, aynl -
+ * betal -
+ * cosim , sinim , cosomm , sinomm , cnod , snod , cos2u ,
+ * sin2u , coseo1 , sineo1 , cosi , sini , cosip , sinip ,
+ * cosisq , cossu , sinsu , cosu , sinu
+ * delm -
+ * delomg -
+ * dndt -
+ * eccm -
+ * emsq -
+ * ecose -
+ * el2 -
+ * eo1 -
+ * eccp -
+ * esine -
+ * argpm -
+ * argpp -
+ * omgadf -
+ * pl -
+ * r -
+ * rtemsq -
+ * rdotl -
+ * rl -
+ * rvdot -
+ * rvdotl -
+ * su -
+ * t2 , t3 , t4 , tc
+ * tem5, temp , temp1 , temp2 , tempa , tempe , templ
+ * u , ux , uy , uz , vx , vy , vz
+ * inclm - inclination
+ * mm - mean anomaly
+ * nm - mean motion
+ * nodem - right asc of ascending node
+ * xinc -
+ * xincp -
+ * xl -
+ * xlm -
+ * mp -
+ * xmdf -
+ * xmx -
+ * xmy -
+ * nodedf -
+ * xnode -
+ * nodep -
+ * np -
+ *
+ * coupling :
+ * getgravconst-
+ * dpper
+ * dspace
+ *
+ * references :
+ * hoots, roehrich, norad spacetrack report //3 1980
+ * hoots, norad spacetrack report //6 1986
+ * hoots, schumacher and glover 2004
+ * vallado, crawford, hujsak, kelso 2006
+ ----------------------------------------------------------------------------*/
+
+ function sgp4(satrec, tsince) {
+ /* eslint-disable no-param-reassign */
+ var coseo1;
+ var sineo1;
+ var cosip;
+ var sinip;
+ var cosisq;
+ var delm;
+ var delomg;
+ var eo1;
+ var argpm;
+ var argpp;
+ var su;
+ var t3;
+ var t4;
+ var tc;
+ var tem5;
+ var temp;
+ var tempa;
+ var tempe;
+ var templ;
+ var inclm;
+ var mm;
+ var nm;
+ var nodem;
+ var xincp;
+ var xlm;
+ var mp;
+ var nodep;
+ /* ------------------ set mathematical constants --------------- */
+ // sgp4fix divisor for divide by zero check on inclination
+ // the old check used 1.0 + cos(pi-1.0e-9), but then compared it to
+ // 1.5 e-12, so the threshold was changed to 1.5e-12 for consistency
+
+ var temp4 = 1.5e-12;
+ var vkmpersec = earthRadius * xke / 60.0; // --------------------- clear sgp4 error flag -----------------
+
+ satrec.t = tsince;
+ satrec.error = 0; // ------- update for secular gravity and atmospheric drag -----
+
+ var xmdf = satrec.mo + satrec.mdot * satrec.t;
+ var argpdf = satrec.argpo + satrec.argpdot * satrec.t;
+ var nodedf = satrec.nodeo + satrec.nodedot * satrec.t;
+ argpm = argpdf;
+ mm = xmdf;
+ var t2 = satrec.t * satrec.t;
+ nodem = nodedf + satrec.nodecf * t2;
+ tempa = 1.0 - satrec.cc1 * satrec.t;
+ tempe = satrec.bstar * satrec.cc4 * satrec.t;
+ templ = satrec.t2cof * t2;
+
+ if (satrec.isimp !== 1) {
+ delomg = satrec.omgcof * satrec.t; // sgp4fix use mutliply for speed instead of pow
+
+ var delmtemp = 1.0 + satrec.eta * Math.cos(xmdf);
+ delm = satrec.xmcof * (delmtemp * delmtemp * delmtemp - satrec.delmo);
+ temp = delomg + delm;
+ mm = xmdf + temp;
+ argpm = argpdf - temp;
+ t3 = t2 * satrec.t;
+ t4 = t3 * satrec.t;
+ tempa = tempa - satrec.d2 * t2 - satrec.d3 * t3 - satrec.d4 * t4;
+ tempe += satrec.bstar * satrec.cc5 * (Math.sin(mm) - satrec.sinmao);
+ templ = templ + satrec.t3cof * t3 + t4 * (satrec.t4cof + satrec.t * satrec.t5cof);
+ }
+
+ nm = satrec.no;
+ var em = satrec.ecco;
+ inclm = satrec.inclo;
+
+ if (satrec.method === 'd') {
+ tc = satrec.t;
+ var dspaceOptions = {
+ irez: satrec.irez,
+ d2201: satrec.d2201,
+ d2211: satrec.d2211,
+ d3210: satrec.d3210,
+ d3222: satrec.d3222,
+ d4410: satrec.d4410,
+ d4422: satrec.d4422,
+ d5220: satrec.d5220,
+ d5232: satrec.d5232,
+ d5421: satrec.d5421,
+ d5433: satrec.d5433,
+ dedt: satrec.dedt,
+ del1: satrec.del1,
+ del2: satrec.del2,
+ del3: satrec.del3,
+ didt: satrec.didt,
+ dmdt: satrec.dmdt,
+ dnodt: satrec.dnodt,
+ domdt: satrec.domdt,
+ argpo: satrec.argpo,
+ argpdot: satrec.argpdot,
+ t: satrec.t,
+ tc: tc,
+ gsto: satrec.gsto,
+ xfact: satrec.xfact,
+ xlamo: satrec.xlamo,
+ no: satrec.no,
+ atime: satrec.atime,
+ em: em,
+ argpm: argpm,
+ inclm: inclm,
+ xli: satrec.xli,
+ mm: mm,
+ xni: satrec.xni,
+ nodem: nodem,
+ nm: nm
+ };
+ var dspaceResult = dspace(dspaceOptions);
+ em = dspaceResult.em;
+ argpm = dspaceResult.argpm;
+ inclm = dspaceResult.inclm;
+ mm = dspaceResult.mm;
+ nodem = dspaceResult.nodem;
+ nm = dspaceResult.nm;
+ }
+
+ if (nm <= 0.0) {
+ // printf("// error nm %f\n", nm);
+ satrec.error = 2; // sgp4fix add return
+
+ return [false, false];
+ }
+
+ var am = Math.pow(xke / nm, x2o3) * tempa * tempa;
+ nm = xke / Math.pow(am, 1.5);
+ em -= tempe; // fix tolerance for error recognition
+ // sgp4fix am is fixed from the previous nm check
+
+ if (em >= 1.0 || em < -0.001) {
+ // || (am < 0.95)
+ // printf("// error em %f\n", em);
+ satrec.error = 1; // sgp4fix to return if there is an error in eccentricity
+
+ return [false, false];
+ } // sgp4fix fix tolerance to avoid a divide by zero
+
+
+ if (em < 1.0e-6) {
+ em = 1.0e-6;
+ }
+
+ mm += satrec.no * templ;
+ xlm = mm + argpm + nodem;
+ nodem %= twoPi;
+ argpm %= twoPi;
+ xlm %= twoPi;
+ mm = (xlm - argpm - nodem) % twoPi; // ----------------- compute extra mean quantities -------------
+
+ var sinim = Math.sin(inclm);
+ var cosim = Math.cos(inclm); // -------------------- add lunar-solar periodics --------------
+
+ var ep = em;
+ xincp = inclm;
+ argpp = argpm;
+ nodep = nodem;
+ mp = mm;
+ sinip = sinim;
+ cosip = cosim;
+
+ if (satrec.method === 'd') {
+ var dpperParameters = {
+ inclo: satrec.inclo,
+ init: 'n',
+ ep: ep,
+ inclp: xincp,
+ nodep: nodep,
+ argpp: argpp,
+ mp: mp,
+ opsmode: satrec.operationmod
+ };
+ var dpperResult = dpper(satrec, dpperParameters);
+ ep = dpperResult.ep;
+ nodep = dpperResult.nodep;
+ argpp = dpperResult.argpp;
+ mp = dpperResult.mp;
+ xincp = dpperResult.inclp;
+
+ if (xincp < 0.0) {
+ xincp = -xincp;
+ nodep += pi;
+ argpp -= pi;
+ }
+
+ if (ep < 0.0 || ep > 1.0) {
+ // printf("// error ep %f\n", ep);
+ satrec.error = 3; // sgp4fix add return
+
+ return [false, false];
+ }
+ } // -------------------- long period periodics ------------------
+
+
+ if (satrec.method === 'd') {
+ sinip = Math.sin(xincp);
+ cosip = Math.cos(xincp);
+ satrec.aycof = -0.5 * j3oj2 * sinip; // sgp4fix for divide by zero for xincp = 180 deg
+
+ if (Math.abs(cosip + 1.0) > 1.5e-12) {
+ satrec.xlcof = -0.25 * j3oj2 * sinip * (3.0 + 5.0 * cosip) / (1.0 + cosip);
+ } else {
+ satrec.xlcof = -0.25 * j3oj2 * sinip * (3.0 + 5.0 * cosip) / temp4;
+ }
+ }
+
+ var axnl = ep * Math.cos(argpp);
+ temp = 1.0 / (am * (1.0 - ep * ep));
+ var aynl = ep * Math.sin(argpp) + temp * satrec.aycof;
+ var xl = mp + argpp + nodep + temp * satrec.xlcof * axnl; // --------------------- solve kepler's equation ---------------
+
+ var u = (xl - nodep) % twoPi;
+ eo1 = u;
+ tem5 = 9999.9;
+ var ktr = 1; // sgp4fix for kepler iteration
+ // the following iteration needs better limits on corrections
+
+ while (Math.abs(tem5) >= 1.0e-12 && ktr <= 10) {
+ sineo1 = Math.sin(eo1);
+ coseo1 = Math.cos(eo1);
+ tem5 = 1.0 - coseo1 * axnl - sineo1 * aynl;
+ tem5 = (u - aynl * coseo1 + axnl * sineo1 - eo1) / tem5;
+
+ if (Math.abs(tem5) >= 0.95) {
+ if (tem5 > 0.0) {
+ tem5 = 0.95;
+ } else {
+ tem5 = -0.95;
+ }
+ }
+
+ eo1 += tem5;
+ ktr += 1;
+ } // ------------- short period preliminary quantities -----------
+
+
+ var ecose = axnl * coseo1 + aynl * sineo1;
+ var esine = axnl * sineo1 - aynl * coseo1;
+ var el2 = axnl * axnl + aynl * aynl;
+ var pl = am * (1.0 - el2);
+
+ if (pl < 0.0) {
+ // printf("// error pl %f\n", pl);
+ satrec.error = 4; // sgp4fix add return
+
+ return [false, false];
+ }
+
+ var rl = am * (1.0 - ecose);
+ var rdotl = Math.sqrt(am) * esine / rl;
+ var rvdotl = Math.sqrt(pl) / rl;
+ var betal = Math.sqrt(1.0 - el2);
+ temp = esine / (1.0 + betal);
+ var sinu = am / rl * (sineo1 - aynl - axnl * temp);
+ var cosu = am / rl * (coseo1 - axnl + aynl * temp);
+ su = Math.atan2(sinu, cosu);
+ var sin2u = (cosu + cosu) * sinu;
+ var cos2u = 1.0 - 2.0 * sinu * sinu;
+ temp = 1.0 / pl;
+ var temp1 = 0.5 * j2 * temp;
+ var temp2 = temp1 * temp; // -------------- update for short period periodics ------------
+
+ if (satrec.method === 'd') {
+ cosisq = cosip * cosip;
+ satrec.con41 = 3.0 * cosisq - 1.0;
+ satrec.x1mth2 = 1.0 - cosisq;
+ satrec.x7thm1 = 7.0 * cosisq - 1.0;
+ }
+
+ var mrt = rl * (1.0 - 1.5 * temp2 * betal * satrec.con41) + 0.5 * temp1 * satrec.x1mth2 * cos2u;
+ su -= 0.25 * temp2 * satrec.x7thm1 * sin2u;
+ var xnode = nodep + 1.5 * temp2 * cosip * sin2u;
+ var xinc = xincp + 1.5 * temp2 * cosip * sinip * cos2u;
+ var mvt = rdotl - nm * temp1 * satrec.x1mth2 * sin2u / xke;
+ var rvdot = rvdotl + nm * temp1 * (satrec.x1mth2 * cos2u + 1.5 * satrec.con41) / xke; // --------------------- orientation vectors -------------------
+
+ var sinsu = Math.sin(su);
+ var cossu = Math.cos(su);
+ var snod = Math.sin(xnode);
+ var cnod = Math.cos(xnode);
+ var sini = Math.sin(xinc);
+ var cosi = Math.cos(xinc);
+ var xmx = -snod * cosi;
+ var xmy = cnod * cosi;
+ var ux = xmx * sinsu + cnod * cossu;
+ var uy = xmy * sinsu + snod * cossu;
+ var uz = sini * sinsu;
+ var vx = xmx * cossu - cnod * sinsu;
+ var vy = xmy * cossu - snod * sinsu;
+ var vz = sini * cossu; // --------- position and velocity (in km and km/sec) ----------
+
+ var r = {
+ x: mrt * ux * earthRadius,
+ y: mrt * uy * earthRadius,
+ z: mrt * uz * earthRadius
+ };
+ var v = {
+ x: (mvt * ux + rvdot * vx) * vkmpersec,
+ y: (mvt * uy + rvdot * vy) * vkmpersec,
+ z: (mvt * uz + rvdot * vz) * vkmpersec
+ }; // sgp4fix for decaying satellites
+
+ if (mrt < 1.0) {
+ // printf("// decay condition %11.6f \n",mrt);
+ satrec.error = 6;
+ return {
+ position: false,
+ velocity: false
+ };
+ }
+
+ return {
+ position: r,
+ velocity: v
+ };
+ /* eslint-enable no-param-reassign */
+ }
+
+ /*-----------------------------------------------------------------------------
+ *
+ * procedure sgp4init
+ *
+ * this procedure initializes variables for sgp4.
+ *
+ * author : david vallado 719-573-2600 28 jun 2005
+ * author : david vallado 719-573-2600 28 jun 2005
+ *
+ * inputs :
+ * opsmode - mode of operation afspc or improved 'a', 'i'
+ * satn - satellite number
+ * bstar - sgp4 type drag coefficient kg/m2er
+ * ecco - eccentricity
+ * epoch - epoch time in days from jan 0, 1950. 0 hr
+ * argpo - argument of perigee (output if ds)
+ * inclo - inclination
+ * mo - mean anomaly (output if ds)
+ * no - mean motion
+ * nodeo - right ascension of ascending node
+ *
+ * outputs :
+ * rec - common values for subsequent calls
+ * return code - non-zero on error.
+ * 1 - mean elements, ecc >= 1.0 or ecc < -0.001 or a < 0.95 er
+ * 2 - mean motion less than 0.0
+ * 3 - pert elements, ecc < 0.0 or ecc > 1.0
+ * 4 - semi-latus rectum < 0.0
+ * 5 - epoch elements are sub-orbital
+ * 6 - satellite has decayed
+ *
+ * locals :
+ * cnodm , snodm , cosim , sinim , cosomm , sinomm
+ * cc1sq , cc2 , cc3
+ * coef , coef1
+ * cosio4 -
+ * day -
+ * dndt -
+ * em - eccentricity
+ * emsq - eccentricity squared
+ * eeta -
+ * etasq -
+ * gam -
+ * argpm - argument of perigee
+ * nodem -
+ * inclm - inclination
+ * mm - mean anomaly
+ * nm - mean motion
+ * perige - perigee
+ * pinvsq -
+ * psisq -
+ * qzms24 -
+ * rtemsq -
+ * s1, s2, s3, s4, s5, s6, s7 -
+ * sfour -
+ * ss1, ss2, ss3, ss4, ss5, ss6, ss7 -
+ * sz1, sz2, sz3
+ * sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33 -
+ * tc -
+ * temp -
+ * temp1, temp2, temp3 -
+ * tsi -
+ * xpidot -
+ * xhdot1 -
+ * z1, z2, z3 -
+ * z11, z12, z13, z21, z22, z23, z31, z32, z33 -
+ *
+ * coupling :
+ * getgravconst-
+ * initl -
+ * dscom -
+ * dpper -
+ * dsinit -
+ * sgp4 -
+ *
+ * references :
+ * hoots, roehrich, norad spacetrack report #3 1980
+ * hoots, norad spacetrack report #6 1986
+ * hoots, schumacher and glover 2004
+ * vallado, crawford, hujsak, kelso 2006
+ ----------------------------------------------------------------------------*/
+
+ function sgp4init(satrec, options) {
+ /* eslint-disable no-param-reassign */
+ var opsmode = options.opsmode,
+ satn = options.satn,
+ epoch = options.epoch,
+ xbstar = options.xbstar,
+ xecco = options.xecco,
+ xargpo = options.xargpo,
+ xinclo = options.xinclo,
+ xmo = options.xmo,
+ xno = options.xno,
+ xnodeo = options.xnodeo;
+ var cosim;
+ var sinim;
+ var cc1sq;
+ var cc2;
+ var cc3;
+ var coef;
+ var coef1;
+ var cosio4;
+ var em;
+ var emsq;
+ var eeta;
+ var etasq;
+ var argpm;
+ var nodem;
+ var inclm;
+ var mm;
+ var nm;
+ var perige;
+ var pinvsq;
+ var psisq;
+ var qzms24;
+ var s1;
+ var s2;
+ var s3;
+ var s4;
+ var s5;
+ var sfour;
+ var ss1;
+ var ss2;
+ var ss3;
+ var ss4;
+ var ss5;
+ var sz1;
+ var sz3;
+ var sz11;
+ var sz13;
+ var sz21;
+ var sz23;
+ var sz31;
+ var sz33;
+ var tc;
+ var temp;
+ var temp1;
+ var temp2;
+ var temp3;
+ var tsi;
+ var xpidot;
+ var xhdot1;
+ var z1;
+ var z3;
+ var z11;
+ var z13;
+ var z21;
+ var z23;
+ var z31;
+ var z33;
+ /* ------------------------ initialization --------------------- */
+ // sgp4fix divisor for divide by zero check on inclination
+ // the old check used 1.0 + Math.cos(pi-1.0e-9), but then compared it to
+ // 1.5 e-12, so the threshold was changed to 1.5e-12 for consistency
+
+ var temp4 = 1.5e-12; // ----------- set all near earth variables to zero ------------
+
+ satrec.isimp = 0;
+ satrec.method = 'n';
+ satrec.aycof = 0.0;
+ satrec.con41 = 0.0;
+ satrec.cc1 = 0.0;
+ satrec.cc4 = 0.0;
+ satrec.cc5 = 0.0;
+ satrec.d2 = 0.0;
+ satrec.d3 = 0.0;
+ satrec.d4 = 0.0;
+ satrec.delmo = 0.0;
+ satrec.eta = 0.0;
+ satrec.argpdot = 0.0;
+ satrec.omgcof = 0.0;
+ satrec.sinmao = 0.0;
+ satrec.t = 0.0;
+ satrec.t2cof = 0.0;
+ satrec.t3cof = 0.0;
+ satrec.t4cof = 0.0;
+ satrec.t5cof = 0.0;
+ satrec.x1mth2 = 0.0;
+ satrec.x7thm1 = 0.0;
+ satrec.mdot = 0.0;
+ satrec.nodedot = 0.0;
+ satrec.xlcof = 0.0;
+ satrec.xmcof = 0.0;
+ satrec.nodecf = 0.0; // ----------- set all deep space variables to zero ------------
+
+ satrec.irez = 0;
+ satrec.d2201 = 0.0;
+ satrec.d2211 = 0.0;
+ satrec.d3210 = 0.0;
+ satrec.d3222 = 0.0;
+ satrec.d4410 = 0.0;
+ satrec.d4422 = 0.0;
+ satrec.d5220 = 0.0;
+ satrec.d5232 = 0.0;
+ satrec.d5421 = 0.0;
+ satrec.d5433 = 0.0;
+ satrec.dedt = 0.0;
+ satrec.del1 = 0.0;
+ satrec.del2 = 0.0;
+ satrec.del3 = 0.0;
+ satrec.didt = 0.0;
+ satrec.dmdt = 0.0;
+ satrec.dnodt = 0.0;
+ satrec.domdt = 0.0;
+ satrec.e3 = 0.0;
+ satrec.ee2 = 0.0;
+ satrec.peo = 0.0;
+ satrec.pgho = 0.0;
+ satrec.pho = 0.0;
+ satrec.pinco = 0.0;
+ satrec.plo = 0.0;
+ satrec.se2 = 0.0;
+ satrec.se3 = 0.0;
+ satrec.sgh2 = 0.0;
+ satrec.sgh3 = 0.0;
+ satrec.sgh4 = 0.0;
+ satrec.sh2 = 0.0;
+ satrec.sh3 = 0.0;
+ satrec.si2 = 0.0;
+ satrec.si3 = 0.0;
+ satrec.sl2 = 0.0;
+ satrec.sl3 = 0.0;
+ satrec.sl4 = 0.0;
+ satrec.gsto = 0.0;
+ satrec.xfact = 0.0;
+ satrec.xgh2 = 0.0;
+ satrec.xgh3 = 0.0;
+ satrec.xgh4 = 0.0;
+ satrec.xh2 = 0.0;
+ satrec.xh3 = 0.0;
+ satrec.xi2 = 0.0;
+ satrec.xi3 = 0.0;
+ satrec.xl2 = 0.0;
+ satrec.xl3 = 0.0;
+ satrec.xl4 = 0.0;
+ satrec.xlamo = 0.0;
+ satrec.zmol = 0.0;
+ satrec.zmos = 0.0;
+ satrec.atime = 0.0;
+ satrec.xli = 0.0;
+ satrec.xni = 0.0; // sgp4fix - note the following variables are also passed directly via satrec.
+ // it is possible to streamline the sgp4init call by deleting the "x"
+ // variables, but the user would need to set the satrec.* values first. we
+ // include the additional assignments in case twoline2rv is not used.
+
+ satrec.bstar = xbstar;
+ satrec.ecco = xecco;
+ satrec.argpo = xargpo;
+ satrec.inclo = xinclo;
+ satrec.mo = xmo;
+ satrec.no = xno;
+ satrec.nodeo = xnodeo; // sgp4fix add opsmode
+
+ satrec.operationmode = opsmode; // ------------------------ earth constants -----------------------
+ // sgp4fix identify constants and allow alternate values
+
+ var ss = 78.0 / earthRadius + 1.0; // sgp4fix use multiply for speed instead of pow
+
+ var qzms2ttemp = (120.0 - 78.0) / earthRadius;
+ var qzms2t = qzms2ttemp * qzms2ttemp * qzms2ttemp * qzms2ttemp;
+ satrec.init = 'y';
+ satrec.t = 0.0;
+ var initlOptions = {
+ satn: satn,
+ ecco: satrec.ecco,
+ epoch: epoch,
+ inclo: satrec.inclo,
+ no: satrec.no,
+ method: satrec.method,
+ opsmode: satrec.operationmode
+ };
+ var initlResult = initl(initlOptions);
+ var ao = initlResult.ao,
+ con42 = initlResult.con42,
+ cosio = initlResult.cosio,
+ cosio2 = initlResult.cosio2,
+ eccsq = initlResult.eccsq,
+ omeosq = initlResult.omeosq,
+ posq = initlResult.posq,
+ rp = initlResult.rp,
+ rteosq = initlResult.rteosq,
+ sinio = initlResult.sinio;
+ satrec.no = initlResult.no;
+ satrec.con41 = initlResult.con41;
+ satrec.gsto = initlResult.gsto;
+ satrec.error = 0; // sgp4fix remove this check as it is unnecessary
+ // the mrt check in sgp4 handles decaying satellite cases even if the starting
+ // condition is below the surface of te earth
+ // if (rp < 1.0)
+ // {
+ // printf("// *** satn%d epoch elts sub-orbital ***\n", satn);
+ // satrec.error = 5;
+ // }
+
+ if (omeosq >= 0.0 || satrec.no >= 0.0) {
+ satrec.isimp = 0;
+
+ if ((rp < 220.0 / earthRadius) + 1.0) {
+ satrec.isimp = 1;
+ }
+
+ sfour = ss;
+ qzms24 = qzms2t;
+ perige = (rp - 1.0) * earthRadius; // - for perigees below 156 km, s and qoms2t are altered -
+
+ if (perige < 156.0) {
+ sfour = perige - 78.0;
+
+ if (perige < 98.0) {
+ sfour = 20.0;
+ } // sgp4fix use multiply for speed instead of pow
+
+
+ var qzms24temp = (120.0 - sfour) / earthRadius;
+ qzms24 = qzms24temp * qzms24temp * qzms24temp * qzms24temp;
+ sfour = sfour / earthRadius + 1.0;
+ }
+
+ pinvsq = 1.0 / posq;
+ tsi = 1.0 / (ao - sfour);
+ satrec.eta = ao * satrec.ecco * tsi;
+ etasq = satrec.eta * satrec.eta;
+ eeta = satrec.ecco * satrec.eta;
+ psisq = Math.abs(1.0 - etasq);
+ coef = qzms24 * Math.pow(tsi, 4.0);
+ coef1 = coef / Math.pow(psisq, 3.5);
+ cc2 = coef1 * satrec.no * (ao * (1.0 + 1.5 * etasq + eeta * (4.0 + etasq)) + 0.375 * j2 * tsi / psisq * satrec.con41 * (8.0 + 3.0 * etasq * (8.0 + etasq)));
+ satrec.cc1 = satrec.bstar * cc2;
+ cc3 = 0.0;
+
+ if (satrec.ecco > 1.0e-4) {
+ cc3 = -2.0 * coef * tsi * j3oj2 * satrec.no * sinio / satrec.ecco;
+ }
+
+ satrec.x1mth2 = 1.0 - cosio2;
+ satrec.cc4 = 2.0 * satrec.no * coef1 * ao * omeosq * (satrec.eta * (2.0 + 0.5 * etasq) + satrec.ecco * (0.5 + 2.0 * etasq) - j2 * tsi / (ao * psisq) * (-3.0 * satrec.con41 * (1.0 - 2.0 * eeta + etasq * (1.5 - 0.5 * eeta)) + 0.75 * satrec.x1mth2 * (2.0 * etasq - eeta * (1.0 + etasq)) * Math.cos(2.0 * satrec.argpo)));
+ satrec.cc5 = 2.0 * coef1 * ao * omeosq * (1.0 + 2.75 * (etasq + eeta) + eeta * etasq);
+ cosio4 = cosio2 * cosio2;
+ temp1 = 1.5 * j2 * pinvsq * satrec.no;
+ temp2 = 0.5 * temp1 * j2 * pinvsq;
+ temp3 = -0.46875 * j4 * pinvsq * pinvsq * satrec.no;
+ satrec.mdot = satrec.no + 0.5 * temp1 * rteosq * satrec.con41 + 0.0625 * temp2 * rteosq * (13.0 - 78.0 * cosio2 + 137.0 * cosio4);
+ satrec.argpdot = -0.5 * temp1 * con42 + 0.0625 * temp2 * (7.0 - 114.0 * cosio2 + 395.0 * cosio4) + temp3 * (3.0 - 36.0 * cosio2 + 49.0 * cosio4);
+ xhdot1 = -temp1 * cosio;
+ satrec.nodedot = xhdot1 + (0.5 * temp2 * (4.0 - 19.0 * cosio2) + 2.0 * temp3 * (3.0 - 7.0 * cosio2)) * cosio;
+ xpidot = satrec.argpdot + satrec.nodedot;
+ satrec.omgcof = satrec.bstar * cc3 * Math.cos(satrec.argpo);
+ satrec.xmcof = 0.0;
+
+ if (satrec.ecco > 1.0e-4) {
+ satrec.xmcof = -x2o3 * coef * satrec.bstar / eeta;
+ }
+
+ satrec.nodecf = 3.5 * omeosq * xhdot1 * satrec.cc1;
+ satrec.t2cof = 1.5 * satrec.cc1; // sgp4fix for divide by zero with xinco = 180 deg
+
+ if (Math.abs(cosio + 1.0) > 1.5e-12) {
+ satrec.xlcof = -0.25 * j3oj2 * sinio * (3.0 + 5.0 * cosio) / (1.0 + cosio);
+ } else {
+ satrec.xlcof = -0.25 * j3oj2 * sinio * (3.0 + 5.0 * cosio) / temp4;
+ }
+
+ satrec.aycof = -0.5 * j3oj2 * sinio; // sgp4fix use multiply for speed instead of pow
+
+ var delmotemp = 1.0 + satrec.eta * Math.cos(satrec.mo);
+ satrec.delmo = delmotemp * delmotemp * delmotemp;
+ satrec.sinmao = Math.sin(satrec.mo);
+ satrec.x7thm1 = 7.0 * cosio2 - 1.0; // --------------- deep space initialization -------------
+
+ if (2 * pi / satrec.no >= 225.0) {
+ satrec.method = 'd';
+ satrec.isimp = 1;
+ tc = 0.0;
+ inclm = satrec.inclo;
+ var dscomOptions = {
+ epoch: epoch,
+ ep: satrec.ecco,
+ argpp: satrec.argpo,
+ tc: tc,
+ inclp: satrec.inclo,
+ nodep: satrec.nodeo,
+ np: satrec.no,
+ e3: satrec.e3,
+ ee2: satrec.ee2,
+ peo: satrec.peo,
+ pgho: satrec.pgho,
+ pho: satrec.pho,
+ pinco: satrec.pinco,
+ plo: satrec.plo,
+ se2: satrec.se2,
+ se3: satrec.se3,
+ sgh2: satrec.sgh2,
+ sgh3: satrec.sgh3,
+ sgh4: satrec.sgh4,
+ sh2: satrec.sh2,
+ sh3: satrec.sh3,
+ si2: satrec.si2,
+ si3: satrec.si3,
+ sl2: satrec.sl2,
+ sl3: satrec.sl3,
+ sl4: satrec.sl4,
+ xgh2: satrec.xgh2,
+ xgh3: satrec.xgh3,
+ xgh4: satrec.xgh4,
+ xh2: satrec.xh2,
+ xh3: satrec.xh3,
+ xi2: satrec.xi2,
+ xi3: satrec.xi3,
+ xl2: satrec.xl2,
+ xl3: satrec.xl3,
+ xl4: satrec.xl4,
+ zmol: satrec.zmol,
+ zmos: satrec.zmos
+ };
+ var dscomResult = dscom(dscomOptions);
+ satrec.e3 = dscomResult.e3;
+ satrec.ee2 = dscomResult.ee2;
+ satrec.peo = dscomResult.peo;
+ satrec.pgho = dscomResult.pgho;
+ satrec.pho = dscomResult.pho;
+ satrec.pinco = dscomResult.pinco;
+ satrec.plo = dscomResult.plo;
+ satrec.se2 = dscomResult.se2;
+ satrec.se3 = dscomResult.se3;
+ satrec.sgh2 = dscomResult.sgh2;
+ satrec.sgh3 = dscomResult.sgh3;
+ satrec.sgh4 = dscomResult.sgh4;
+ satrec.sh2 = dscomResult.sh2;
+ satrec.sh3 = dscomResult.sh3;
+ satrec.si2 = dscomResult.si2;
+ satrec.si3 = dscomResult.si3;
+ satrec.sl2 = dscomResult.sl2;
+ satrec.sl3 = dscomResult.sl3;
+ satrec.sl4 = dscomResult.sl4;
+ sinim = dscomResult.sinim;
+ cosim = dscomResult.cosim;
+ em = dscomResult.em;
+ emsq = dscomResult.emsq;
+ s1 = dscomResult.s1;
+ s2 = dscomResult.s2;
+ s3 = dscomResult.s3;
+ s4 = dscomResult.s4;
+ s5 = dscomResult.s5;
+ ss1 = dscomResult.ss1;
+ ss2 = dscomResult.ss2;
+ ss3 = dscomResult.ss3;
+ ss4 = dscomResult.ss4;
+ ss5 = dscomResult.ss5;
+ sz1 = dscomResult.sz1;
+ sz3 = dscomResult.sz3;
+ sz11 = dscomResult.sz11;
+ sz13 = dscomResult.sz13;
+ sz21 = dscomResult.sz21;
+ sz23 = dscomResult.sz23;
+ sz31 = dscomResult.sz31;
+ sz33 = dscomResult.sz33;
+ satrec.xgh2 = dscomResult.xgh2;
+ satrec.xgh3 = dscomResult.xgh3;
+ satrec.xgh4 = dscomResult.xgh4;
+ satrec.xh2 = dscomResult.xh2;
+ satrec.xh3 = dscomResult.xh3;
+ satrec.xi2 = dscomResult.xi2;
+ satrec.xi3 = dscomResult.xi3;
+ satrec.xl2 = dscomResult.xl2;
+ satrec.xl3 = dscomResult.xl3;
+ satrec.xl4 = dscomResult.xl4;
+ satrec.zmol = dscomResult.zmol;
+ satrec.zmos = dscomResult.zmos;
+ nm = dscomResult.nm;
+ z1 = dscomResult.z1;
+ z3 = dscomResult.z3;
+ z11 = dscomResult.z11;
+ z13 = dscomResult.z13;
+ z21 = dscomResult.z21;
+ z23 = dscomResult.z23;
+ z31 = dscomResult.z31;
+ z33 = dscomResult.z33;
+ var dpperOptions = {
+ inclo: inclm,
+ init: satrec.init,
+ ep: satrec.ecco,
+ inclp: satrec.inclo,
+ nodep: satrec.nodeo,
+ argpp: satrec.argpo,
+ mp: satrec.mo,
+ opsmode: satrec.operationmode
+ };
+ var dpperResult = dpper(satrec, dpperOptions);
+ satrec.ecco = dpperResult.ep;
+ satrec.inclo = dpperResult.inclp;
+ satrec.nodeo = dpperResult.nodep;
+ satrec.argpo = dpperResult.argpp;
+ satrec.mo = dpperResult.mp;
+ argpm = 0.0;
+ nodem = 0.0;
+ mm = 0.0;
+ var dsinitOptions = {
+ cosim: cosim,
+ emsq: emsq,
+ argpo: satrec.argpo,
+ s1: s1,
+ s2: s2,
+ s3: s3,
+ s4: s4,
+ s5: s5,
+ sinim: sinim,
+ ss1: ss1,
+ ss2: ss2,
+ ss3: ss3,
+ ss4: ss4,
+ ss5: ss5,
+ sz1: sz1,
+ sz3: sz3,
+ sz11: sz11,
+ sz13: sz13,
+ sz21: sz21,
+ sz23: sz23,
+ sz31: sz31,
+ sz33: sz33,
+ t: satrec.t,
+ tc: tc,
+ gsto: satrec.gsto,
+ mo: satrec.mo,
+ mdot: satrec.mdot,
+ no: satrec.no,
+ nodeo: satrec.nodeo,
+ nodedot: satrec.nodedot,
+ xpidot: xpidot,
+ z1: z1,
+ z3: z3,
+ z11: z11,
+ z13: z13,
+ z21: z21,
+ z23: z23,
+ z31: z31,
+ z33: z33,
+ ecco: satrec.ecco,
+ eccsq: eccsq,
+ em: em,
+ argpm: argpm,
+ inclm: inclm,
+ mm: mm,
+ nm: nm,
+ nodem: nodem,
+ irez: satrec.irez,
+ atime: satrec.atime,
+ d2201: satrec.d2201,
+ d2211: satrec.d2211,
+ d3210: satrec.d3210,
+ d3222: satrec.d3222,
+ d4410: satrec.d4410,
+ d4422: satrec.d4422,
+ d5220: satrec.d5220,
+ d5232: satrec.d5232,
+ d5421: satrec.d5421,
+ d5433: satrec.d5433,
+ dedt: satrec.dedt,
+ didt: satrec.didt,
+ dmdt: satrec.dmdt,
+ dnodt: satrec.dnodt,
+ domdt: satrec.domdt,
+ del1: satrec.del1,
+ del2: satrec.del2,
+ del3: satrec.del3,
+ xfact: satrec.xfact,
+ xlamo: satrec.xlamo,
+ xli: satrec.xli,
+ xni: satrec.xni
+ };
+ var dsinitResult = dsinit(dsinitOptions);
+ satrec.irez = dsinitResult.irez;
+ satrec.atime = dsinitResult.atime;
+ satrec.d2201 = dsinitResult.d2201;
+ satrec.d2211 = dsinitResult.d2211;
+ satrec.d3210 = dsinitResult.d3210;
+ satrec.d3222 = dsinitResult.d3222;
+ satrec.d4410 = dsinitResult.d4410;
+ satrec.d4422 = dsinitResult.d4422;
+ satrec.d5220 = dsinitResult.d5220;
+ satrec.d5232 = dsinitResult.d5232;
+ satrec.d5421 = dsinitResult.d5421;
+ satrec.d5433 = dsinitResult.d5433;
+ satrec.dedt = dsinitResult.dedt;
+ satrec.didt = dsinitResult.didt;
+ satrec.dmdt = dsinitResult.dmdt;
+ satrec.dnodt = dsinitResult.dnodt;
+ satrec.domdt = dsinitResult.domdt;
+ satrec.del1 = dsinitResult.del1;
+ satrec.del2 = dsinitResult.del2;
+ satrec.del3 = dsinitResult.del3;
+ satrec.xfact = dsinitResult.xfact;
+ satrec.xlamo = dsinitResult.xlamo;
+ satrec.xli = dsinitResult.xli;
+ satrec.xni = dsinitResult.xni;
+ } // ----------- set variables if not deep space -----------
+
+
+ if (satrec.isimp !== 1) {
+ cc1sq = satrec.cc1 * satrec.cc1;
+ satrec.d2 = 4.0 * ao * tsi * cc1sq;
+ temp = satrec.d2 * tsi * satrec.cc1 / 3.0;
+ satrec.d3 = (17.0 * ao + sfour) * temp;
+ satrec.d4 = 0.5 * temp * ao * tsi * (221.0 * ao + 31.0 * sfour) * satrec.cc1;
+ satrec.t3cof = satrec.d2 + 2.0 * cc1sq;
+ satrec.t4cof = 0.25 * (3.0 * satrec.d3 + satrec.cc1 * (12.0 * satrec.d2 + 10.0 * cc1sq));
+ satrec.t5cof = 0.2 * (3.0 * satrec.d4 + 12.0 * satrec.cc1 * satrec.d3 + 6.0 * satrec.d2 * satrec.d2 + 15.0 * cc1sq * (2.0 * satrec.d2 + cc1sq));
+ }
+ /* finally propogate to zero epoch to initialize all others. */
+ // sgp4fix take out check to let satellites process until they are actually below earth surface
+ // if(satrec.error == 0)
+
+ }
+
+ sgp4(satrec, 0, 0);
+ satrec.init = 'n';
+ /* eslint-enable no-param-reassign */
+ }
+
+ /* -----------------------------------------------------------------------------
+ *
+ * function twoline2rv
+ *
+ * this function converts the two line element set character string data to
+ * variables and initializes the sgp4 variables. several intermediate varaibles
+ * and quantities are determined. note that the result is a structure so multiple
+ * satellites can be processed simultaneously without having to reinitialize. the
+ * verification mode is an important option that permits quick checks of any
+ * changes to the underlying technical theory. this option works using a
+ * modified tle file in which the start, stop, and delta time values are
+ * included at the end of the second line of data. this only works with the
+ * verification mode. the catalog mode simply propagates from -1440 to 1440 min
+ * from epoch and is useful when performing entire catalog runs.
+ *
+ * author : david vallado 719-573-2600 1 mar 2001
+ *
+ * inputs :
+ * longstr1 - first line of the tle
+ * longstr2 - second line of the tle
+ * typerun - type of run verification 'v', catalog 'c',
+ * manual 'm'
+ * typeinput - type of manual input mfe 'm', epoch 'e', dayofyr 'd'
+ * opsmode - mode of operation afspc or improved 'a', 'i'
+ * whichconst - which set of constants to use 72, 84
+ *
+ * outputs :
+ * satrec - structure containing all the sgp4 satellite information
+ *
+ * coupling :
+ * getgravconst-
+ * days2mdhms - conversion of days to month, day, hour, minute, second
+ * jday - convert day month year hour minute second into julian date
+ * sgp4init - initialize the sgp4 variables
+ *
+ * references :
+ * norad spacetrack report #3
+ * vallado, crawford, hujsak, kelso 2006
+ --------------------------------------------------------------------------- */
+
+ /**
+ * Return a Satellite imported from two lines of TLE data.
+ *
+ * Provide the two TLE lines as strings `longstr1` and `longstr2`,
+ * and select which standard set of gravitational constants you want
+ * by providing `gravity_constants`:
+ *
+ * `sgp4.propagation.wgs72` - Standard WGS 72 model
+ * `sgp4.propagation.wgs84` - More recent WGS 84 model
+ * `sgp4.propagation.wgs72old` - Legacy support for old SGP4 behavior
+ *
+ * Normally, computations are made using letious recent improvements
+ * to the algorithm. If you want to turn some of these off and go
+ * back into "afspc" mode, then set `afspc_mode` to `True`.
+ */
+
+ function twoline2satrec(longstr1, longstr2) {
+ var opsmode = 'i';
+ var xpdotp = 1440.0 / (2.0 * pi); // 229.1831180523293;
+
+ var year = 0;
+ var satrec = {};
+ satrec.error = 0;
+ satrec.satnum = longstr1.substring(2, 7);
+ satrec.epochyr = parseInt(longstr1.substring(18, 20), 10);
+ satrec.epochdays = parseFloat(longstr1.substring(20, 32));
+ satrec.ndot = parseFloat(longstr1.substring(33, 43));
+ satrec.nddot = parseFloat(".".concat(parseInt(longstr1.substring(44, 50), 10), "E").concat(longstr1.substring(50, 52)));
+ satrec.bstar = parseFloat("".concat(longstr1.substring(53, 54), ".").concat(parseInt(longstr1.substring(54, 59), 10), "E").concat(longstr1.substring(59, 61))); // satrec.satnum = longstr2.substring(2, 7);
+
+ satrec.inclo = parseFloat(longstr2.substring(8, 16));
+ satrec.nodeo = parseFloat(longstr2.substring(17, 25));
+ satrec.ecco = parseFloat(".".concat(longstr2.substring(26, 33)));
+ satrec.argpo = parseFloat(longstr2.substring(34, 42));
+ satrec.mo = parseFloat(longstr2.substring(43, 51));
+ satrec.no = parseFloat(longstr2.substring(52, 63)); // ---- find no, ndot, nddot ----
+
+ satrec.no /= xpdotp; // rad/min
+ // satrec.nddot= satrec.nddot * Math.pow(10.0, nexp);
+ // satrec.bstar= satrec.bstar * Math.pow(10.0, ibexp);
+ // ---- convert to sgp4 units ----
+
+ satrec.a = Math.pow(satrec.no * tumin, -2.0 / 3.0);
+ satrec.ndot /= xpdotp * 1440.0; // ? * minperday
+
+ satrec.nddot /= xpdotp * 1440.0 * 1440; // ---- find standard orbital elements ----
+
+ satrec.inclo *= deg2rad;
+ satrec.nodeo *= deg2rad;
+ satrec.argpo *= deg2rad;
+ satrec.mo *= deg2rad;
+ satrec.alta = satrec.a * (1.0 + satrec.ecco) - 1.0;
+ satrec.altp = satrec.a * (1.0 - satrec.ecco) - 1.0; // ----------------------------------------------------------------
+ // find sgp4epoch time of element set
+ // remember that sgp4 uses units of days from 0 jan 1950 (sgp4epoch)
+ // and minutes from the epoch (time)
+ // ----------------------------------------------------------------
+ // ---------------- temp fix for years from 1957-2056 -------------------
+ // --------- correct fix will occur when year is 4-digit in tle ---------
+
+ if (satrec.epochyr < 57) {
+ year = satrec.epochyr + 2000;
+ } else {
+ year = satrec.epochyr + 1900;
+ }
+
+ var mdhmsResult = days2mdhms(year, satrec.epochdays);
+ var mon = mdhmsResult.mon,
+ day = mdhmsResult.day,
+ hr = mdhmsResult.hr,
+ minute = mdhmsResult.minute,
+ sec = mdhmsResult.sec;
+ satrec.jdsatepoch = jday(year, mon, day, hr, minute, sec); // ---------------- initialize the orbit at sgp4epoch -------------------
+
+ sgp4init(satrec, {
+ opsmode: opsmode,
+ satn: satrec.satnum,
+ epoch: satrec.jdsatepoch - 2433281.5,
+ xbstar: satrec.bstar,
+ xecco: satrec.ecco,
+ xargpo: satrec.argpo,
+ xinclo: satrec.inclo,
+ xmo: satrec.mo,
+ xno: satrec.no,
+ xnodeo: satrec.nodeo
+ });
+ return satrec;
+ }
+
+ function _toConsumableArray(arr) {
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
+ }
+
+ function _arrayWithoutHoles(arr) {
+ if (Array.isArray(arr)) {
+ for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
+
+ return arr2;
+ }
+ }
+
+ function _iterableToArray(iter) {
+ if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
+ }
+
+ function _nonIterableSpread() {
+ throw new TypeError("Invalid attempt to spread non-iterable instance");
+ }
+
+ function propagate() {
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ // Return a position and velocity vector for a given date and time.
+ var satrec = args[0];
+ var date = Array.prototype.slice.call(args, 1);
+ var j = jday.apply(void 0, _toConsumableArray(date));
+ var m = (j - satrec.jdsatepoch) * minutesPerDay;
+ return sgp4(satrec, m);
+ }
+
+ function dopplerFactor(location, position, velocity) {
+ var currentRange = Math.sqrt(Math.pow(position.x - location.x, 2) + Math.pow(position.y - location.y, 2) + Math.pow(position.z - location.z, 2));
+ var nextPos = {
+ x: position.x + velocity.x,
+ y: position.y + velocity.y,
+ z: position.z + velocity.z
+ };
+ var nextRange = Math.sqrt(Math.pow(nextPos.x - location.x, 2) + Math.pow(nextPos.y - location.y, 2) + Math.pow(nextPos.z - location.z, 2));
+ var rangeRate = nextRange - currentRange;
+
+ function sign(value) {
+ return value >= 0 ? 1 : -1;
+ }
+
+ rangeRate *= sign(rangeRate);
+ var c = 299792.458; // Speed of light in km/s
+
+ return 1 + rangeRate / c;
+ }
+
+ function radiansToDegrees(radians) {
+ return radians * rad2deg;
+ }
+ function degreesToRadians(degrees) {
+ return degrees * deg2rad;
+ }
+ function degreesLat(radians) {
+ if (radians < -pi / 2 || radians > pi / 2) {
+ throw new RangeError('Latitude radians must be in range [-pi/2; pi/2].');
+ }
+
+ return radiansToDegrees(radians);
+ }
+ function degreesLong(radians) {
+ if (radians < -pi || radians > 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 < -pi) {
+ longitude += twoPi;
+ }
+
+ while (longitude > pi) {
+ longitude -= 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) + pi;
+ return {
+ azimuth: Az,
+ elevation: El,
+ rangeSat: rangeSat // Range in km
+
+ };
+ }
+
+ function ecfToLookAngles(observerGeodetic, satelliteEcf) {
+ var topocentricCoords = topocentric(observerGeodetic, satelliteEcf);
+ return topocentricToLookAngles(topocentricCoords);
+ }
+
+ var indexUmd = {
+ constants: constants,
+ // Propagation
+ propagate: propagate,
+ sgp4: sgp4,
+ twoline2satrec: twoline2satrec,
+ gstime: gstime,
+ jday: jday,
+ invjday: invjday,
+ dopplerFactor: dopplerFactor,
+ // Coordinate transforms
+ radiansToDegrees: radiansToDegrees,
+ degreesToRadians: degreesToRadians,
+ degreesLat: degreesLat,
+ degreesLong: degreesLong,
+ radiansLat: radiansLat,
+ radiansLong: radiansLong,
+ geodeticToEcf: geodeticToEcf,
+ eciToGeodetic: eciToGeodetic,
+ eciToEcf: eciToEcf,
+ ecfToEci: ecfToEci,
+ ecfToLookAngles: ecfToLookAngles
+ };
+
+ return indexUmd;
+
+})));
diff --git a/assets/js/sections/three-orbit-controls.js b/assets/js/sections/three-orbit-controls.js
new file mode 100644
index 000000000..41e6fdddb
--- /dev/null
+++ b/assets/js/sections/three-orbit-controls.js
@@ -0,0 +1,1022 @@
+(function() {
+window.OrbitControls = function( THREE ) {
+ /**
+ * @author qiao / https://github.com/qiao
+ * @author mrdoob / http://mrdoob.com
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author erich666 / http://erichaines.com
+ */
+
+// This set of controls performs orbiting, dollying (zooming), and panning.
+// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
+//
+// Orbit - left mouse / touch: one finger move
+// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
+// Pan - right mouse, or arrow keys / touch: three finter swipe
+
+ function OrbitControls( object, domElement ) {
+
+ this.object = object;
+
+ this.domElement = ( domElement !== undefined ) ? domElement : document;
+
+ // Set to false to disable this control
+ this.enabled = true;
+
+ // "target" sets the location of focus, where the object orbits around
+ this.target = new THREE.Vector3();
+
+ // How far you can dolly in and out ( PerspectiveCamera only )
+ this.minDistance = 0;
+ this.maxDistance = Infinity;
+
+ // How far you can zoom in and out ( OrthographicCamera only )
+ this.minZoom = 0;
+ this.maxZoom = Infinity;
+
+ // How far you can orbit vertically, upper and lower limits.
+ // Range is 0 to Math.PI radians.
+ this.minPolarAngle = 0; // radians
+ this.maxPolarAngle = Math.PI; // radians
+
+ // How far you can orbit horizontally, upper and lower limits.
+ // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
+ this.minAzimuthAngle = - Infinity; // radians
+ this.maxAzimuthAngle = Infinity; // radians
+
+ // Set to true to enable damping (inertia)
+ // If damping is enabled, you must call controls.update() in your animation loop
+ this.enableDamping = false;
+ this.dampingFactor = 0.25;
+
+ // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
+ // Set to false to disable zooming
+ this.enableZoom = true;
+ this.zoomSpeed = 1.0;
+
+ // Set to false to disable rotating
+ this.enableRotate = true;
+ this.rotateSpeed = 1.0;
+
+ // Set to false to disable panning
+ this.enablePan = true;
+ this.keyPanSpeed = 7.0; // pixels moved per arrow key push
+
+ // Set to true to automatically rotate around the target
+ // If auto-rotate is enabled, you must call controls.update() in your animation loop
+ this.autoRotate = false;
+ this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
+
+ // Set to false to disable use of the keys
+ this.enableKeys = true;
+
+ // The four arrow keys
+ this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
+
+ // Mouse buttons
+ this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
+
+ // for reset
+ this.target0 = this.target.clone();
+ this.position0 = this.object.position.clone();
+ this.zoom0 = this.object.zoom;
+
+ //
+ // public methods
+ //
+
+ this.getPolarAngle = function () {
+
+ return spherical.phi;
+
+ };
+
+ this.getAzimuthalAngle = function () {
+
+ return spherical.theta;
+
+ };
+
+ this.reset = function () {
+
+ scope.target.copy( scope.target0 );
+ scope.object.position.copy( scope.position0 );
+ scope.object.zoom = scope.zoom0;
+
+ scope.object.updateProjectionMatrix();
+ scope.dispatchEvent( changeEvent );
+
+ scope.update();
+
+ state = STATE.NONE;
+
+ };
+
+ // this method is exposed, but perhaps it would be better if we can make it private...
+ this.update = function() {
+
+ var offset = new THREE.Vector3();
+
+ // so camera.up is the orbit axis
+ var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
+ var quatInverse = quat.clone().inverse();
+
+ var lastPosition = new THREE.Vector3();
+ var lastQuaternion = new THREE.Quaternion();
+
+ return function update () {
+
+ var position = scope.object.position;
+
+ offset.copy( position ).sub( scope.target );
+
+ // rotate offset to "y-axis-is-up" space
+ offset.applyQuaternion( quat );
+
+ // angle from z-axis around y-axis
+ spherical.setFromVector3( offset );
+
+ if ( scope.autoRotate && state === STATE.NONE ) {
+
+ rotateLeft( getAutoRotationAngle() );
+
+ }
+
+ spherical.theta += sphericalDelta.theta;
+ spherical.phi += sphericalDelta.phi;
+
+ // restrict theta to be between desired limits
+ spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
+
+ // restrict phi to be between desired limits
+ spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
+
+ spherical.makeSafe();
+
+
+ spherical.radius *= scale;
+
+ // restrict radius to be between desired limits
+ spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
+
+ // move target to panned location
+ scope.target.add( panOffset );
+
+ offset.setFromSpherical( spherical );
+
+ // rotate offset back to "camera-up-vector-is-up" space
+ offset.applyQuaternion( quatInverse );
+
+ position.copy( scope.target ).add( offset );
+
+ scope.object.lookAt( scope.target );
+
+ if ( scope.enableDamping === true ) {
+
+ sphericalDelta.theta *= ( 1 - scope.dampingFactor );
+ sphericalDelta.phi *= ( 1 - scope.dampingFactor );
+
+ } else {
+
+ sphericalDelta.set( 0, 0, 0 );
+
+ }
+
+ scale = 1;
+ panOffset.set( 0, 0, 0 );
+
+ // update condition is:
+ // min(camera displacement, camera rotation in radians)^2 > EPS
+ // using small-angle approximation cos(x/2) = 1 - x^2 / 8
+
+ if ( zoomChanged ||
+ lastPosition.distanceToSquared( scope.object.position ) > EPS ||
+ 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
+
+ scope.dispatchEvent( changeEvent );
+
+ lastPosition.copy( scope.object.position );
+ lastQuaternion.copy( scope.object.quaternion );
+ zoomChanged = false;
+
+ return true;
+
+ }
+
+ return false;
+
+ };
+
+ }();
+
+ this.dispose = function() {
+
+ scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
+ scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
+ scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
+
+ scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
+ scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
+ scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
+
+ document.removeEventListener( 'mousemove', onMouseMove, false );
+ document.removeEventListener( 'mouseup', onMouseUp, false );
+
+ window.removeEventListener( 'keydown', onKeyDown, false );
+
+ //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
+
+ };
+
+ //
+ // internals
+ //
+
+ var scope = this;
+
+ var changeEvent = { type: 'change' };
+ var startEvent = { type: 'start' };
+ var endEvent = { type: 'end' };
+
+ var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
+
+ var state = STATE.NONE;
+
+ var EPS = 0.000001;
+
+ // current position in spherical coordinates
+ var spherical = new THREE.Spherical();
+ var sphericalDelta = new THREE.Spherical();
+
+ var scale = 1;
+ var panOffset = new THREE.Vector3();
+ var zoomChanged = false;
+
+ var rotateStart = new THREE.Vector2();
+ var rotateEnd = new THREE.Vector2();
+ var rotateDelta = new THREE.Vector2();
+
+ var panStart = new THREE.Vector2();
+ var panEnd = new THREE.Vector2();
+ var panDelta = new THREE.Vector2();
+
+ var dollyStart = new THREE.Vector2();
+ var dollyEnd = new THREE.Vector2();
+ var dollyDelta = new THREE.Vector2();
+
+ function getAutoRotationAngle() {
+
+ return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
+
+ }
+
+ function getZoomScale() {
+
+ return Math.pow( 0.95, scope.zoomSpeed );
+
+ }
+
+ function rotateLeft( angle ) {
+
+ sphericalDelta.theta -= angle;
+
+ }
+
+ function rotateUp( angle ) {
+
+ sphericalDelta.phi -= angle;
+
+ }
+
+ var panLeft = function() {
+
+ var v = new THREE.Vector3();
+
+ return function panLeft( distance, objectMatrix ) {
+
+ v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
+ v.multiplyScalar( - distance );
+
+ panOffset.add( v );
+
+ };
+
+ }();
+
+ var panUp = function() {
+
+ var v = new THREE.Vector3();
+
+ return function panUp( distance, objectMatrix ) {
+
+ v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix
+ v.multiplyScalar( distance );
+
+ panOffset.add( v );
+
+ };
+
+ }();
+
+ // deltaX and deltaY are in pixels; right and down are positive
+ var pan = function() {
+
+ var offset = new THREE.Vector3();
+
+ return function pan ( deltaX, deltaY ) {
+
+ var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
+
+ if ( scope.object instanceof THREE.PerspectiveCamera ) {
+
+ // perspective
+ var position = scope.object.position;
+ offset.copy( position ).sub( scope.target );
+ var targetDistance = offset.length();
+
+ // half of the fov is center to top of screen
+ targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
+
+ // we actually don't use screenWidth, since perspective camera is fixed to screen height
+ panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
+ panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
+
+ } else if ( scope.object instanceof THREE.OrthographicCamera ) {
+
+ // orthographic
+ panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
+ panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
+
+ } else {
+
+ // camera neither orthographic nor perspective
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
+ scope.enablePan = false;
+
+ }
+
+ };
+
+ }();
+
+ function dollyIn( dollyScale ) {
+
+ if ( scope.object instanceof THREE.PerspectiveCamera ) {
+
+ scale /= dollyScale;
+
+ } else if ( scope.object instanceof THREE.OrthographicCamera ) {
+
+ scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
+ scope.object.updateProjectionMatrix();
+ zoomChanged = true;
+
+ } else {
+
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
+ scope.enableZoom = false;
+
+ }
+
+ }
+
+ function dollyOut( dollyScale ) {
+
+ if ( scope.object instanceof THREE.PerspectiveCamera ) {
+
+ scale *= dollyScale;
+
+ } else if ( scope.object instanceof THREE.OrthographicCamera ) {
+
+ scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
+ scope.object.updateProjectionMatrix();
+ zoomChanged = true;
+
+ } else {
+
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
+ scope.enableZoom = false;
+
+ }
+
+ }
+
+ //
+ // event callbacks - update the object state
+ //
+
+ function handleMouseDownRotate( event ) {
+
+ //console.log( 'handleMouseDownRotate' );
+
+ rotateStart.set( event.clientX, event.clientY );
+
+ }
+
+ function handleMouseDownDolly( event ) {
+
+ //console.log( 'handleMouseDownDolly' );
+
+ dollyStart.set( event.clientX, event.clientY );
+
+ }
+
+ function handleMouseDownPan( event ) {
+
+ //console.log( 'handleMouseDownPan' );
+
+ panStart.set( event.clientX, event.clientY );
+
+ }
+
+ function handleMouseMoveRotate( event ) {
+
+ //console.log( 'handleMouseMoveRotate' );
+
+ rotateEnd.set( event.clientX, event.clientY );
+ rotateDelta.subVectors( rotateEnd, rotateStart );
+
+ var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
+
+ // rotating across whole screen goes 360 degrees around
+ rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
+
+ // rotating up and down along whole screen attempts to go 360, but limited to 180
+ rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
+
+ rotateStart.copy( rotateEnd );
+
+ scope.update();
+
+ }
+
+ function handleMouseMoveDolly( event ) {
+
+ //console.log( 'handleMouseMoveDolly' );
+
+ dollyEnd.set( event.clientX, event.clientY );
+
+ dollyDelta.subVectors( dollyEnd, dollyStart );
+
+ if ( dollyDelta.y > 0 ) {
+
+ dollyIn( getZoomScale() );
+
+ } else if ( dollyDelta.y < 0 ) {
+
+ dollyOut( getZoomScale() );
+
+ }
+
+ dollyStart.copy( dollyEnd );
+
+ scope.update();
+
+ }
+
+ function handleMouseMovePan( event ) {
+
+ //console.log( 'handleMouseMovePan' );
+
+ panEnd.set( event.clientX, event.clientY );
+
+ panDelta.subVectors( panEnd, panStart );
+
+ pan( panDelta.x, panDelta.y );
+
+ panStart.copy( panEnd );
+
+ scope.update();
+
+ }
+
+ function handleMouseUp( event ) {
+
+ //console.log( 'handleMouseUp' );
+
+ }
+
+ function handleMouseWheel( event ) {
+
+ //console.log( 'handleMouseWheel' );
+
+ if ( event.deltaY < 0 ) {
+
+ dollyOut( getZoomScale() );
+
+ } else if ( event.deltaY > 0 ) {
+
+ dollyIn( getZoomScale() );
+
+ }
+
+ scope.update();
+
+ }
+
+ function handleKeyDown( event ) {
+
+ //console.log( 'handleKeyDown' );
+
+ switch ( event.keyCode ) {
+
+ case scope.keys.UP:
+ pan( 0, scope.keyPanSpeed );
+ scope.update();
+ break;
+
+ case scope.keys.BOTTOM:
+ pan( 0, - scope.keyPanSpeed );
+ scope.update();
+ break;
+
+ case scope.keys.LEFT:
+ pan( scope.keyPanSpeed, 0 );
+ scope.update();
+ break;
+
+ case scope.keys.RIGHT:
+ pan( - scope.keyPanSpeed, 0 );
+ scope.update();
+ break;
+
+ }
+
+ }
+
+ function handleTouchStartRotate( event ) {
+
+ //console.log( 'handleTouchStartRotate' );
+
+ rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+
+ }
+
+ function handleTouchStartDolly( event ) {
+
+ //console.log( 'handleTouchStartDolly' );
+
+ var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+ var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+
+ var distance = Math.sqrt( dx * dx + dy * dy );
+
+ dollyStart.set( 0, distance );
+
+ }
+
+ function handleTouchStartPan( event ) {
+
+ //console.log( 'handleTouchStartPan' );
+
+ panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+
+ }
+
+ function handleTouchMoveRotate( event ) {
+
+ //console.log( 'handleTouchMoveRotate' );
+
+ rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+ rotateDelta.subVectors( rotateEnd, rotateStart );
+
+ var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
+
+ // rotating across whole screen goes 360 degrees around
+ rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
+
+ // rotating up and down along whole screen attempts to go 360, but limited to 180
+ rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
+
+ rotateStart.copy( rotateEnd );
+
+ scope.update();
+
+ }
+
+ function handleTouchMoveDolly( event ) {
+
+ //console.log( 'handleTouchMoveDolly' );
+
+ var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+ var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+
+ var distance = Math.sqrt( dx * dx + dy * dy );
+
+ dollyEnd.set( 0, distance );
+
+ dollyDelta.subVectors( dollyEnd, dollyStart );
+
+ if ( dollyDelta.y > 0 ) {
+
+ dollyOut( getZoomScale() );
+
+ } else if ( dollyDelta.y < 0 ) {
+
+ dollyIn( getZoomScale() );
+
+ }
+
+ dollyStart.copy( dollyEnd );
+
+ scope.update();
+
+ }
+
+ function handleTouchMovePan( event ) {
+
+ //console.log( 'handleTouchMovePan' );
+
+ panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+
+ panDelta.subVectors( panEnd, panStart );
+
+ pan( panDelta.x, panDelta.y );
+
+ panStart.copy( panEnd );
+
+ scope.update();
+
+ }
+
+ function handleTouchEnd( event ) {
+
+ //console.log( 'handleTouchEnd' );
+
+ }
+
+ //
+ // event handlers - FSM: listen for events and reset state
+ //
+
+ function onMouseDown( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ event.preventDefault();
+
+ if ( event.button === scope.mouseButtons.ORBIT ) {
+
+ if ( scope.enableRotate === false ) return;
+
+ handleMouseDownRotate( event );
+
+ state = STATE.ROTATE;
+
+ } else if ( event.button === scope.mouseButtons.ZOOM ) {
+
+ if ( scope.enableZoom === false ) return;
+
+ handleMouseDownDolly( event );
+
+ state = STATE.DOLLY;
+
+ } else if ( event.button === scope.mouseButtons.PAN ) {
+
+ if ( scope.enablePan === false ) return;
+
+ handleMouseDownPan( event );
+
+ state = STATE.PAN;
+
+ }
+
+ if ( state !== STATE.NONE ) {
+
+ document.addEventListener( 'mousemove', onMouseMove, false );
+ document.addEventListener( 'mouseup', onMouseUp, false );
+
+ scope.dispatchEvent( startEvent );
+
+ }
+
+ }
+
+ function onMouseMove( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ event.preventDefault();
+
+ if ( state === STATE.ROTATE ) {
+
+ if ( scope.enableRotate === false ) return;
+
+ handleMouseMoveRotate( event );
+
+ } else if ( state === STATE.DOLLY ) {
+
+ if ( scope.enableZoom === false ) return;
+
+ handleMouseMoveDolly( event );
+
+ } else if ( state === STATE.PAN ) {
+
+ if ( scope.enablePan === false ) return;
+
+ handleMouseMovePan( event );
+
+ }
+
+ }
+
+ function onMouseUp( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ handleMouseUp( event );
+
+ document.removeEventListener( 'mousemove', onMouseMove, false );
+ document.removeEventListener( 'mouseup', onMouseUp, false );
+
+ scope.dispatchEvent( endEvent );
+
+ state = STATE.NONE;
+
+ }
+
+ function onMouseWheel( event ) {
+
+ if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ handleMouseWheel( event );
+
+ scope.dispatchEvent( startEvent ); // not sure why these are here...
+ scope.dispatchEvent( endEvent );
+
+ }
+
+ function onKeyDown( event ) {
+
+ if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
+
+ handleKeyDown( event );
+
+ }
+
+ function onTouchStart( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ switch ( event.touches.length ) {
+
+ case 1: // one-fingered touch: rotate
+
+ if ( scope.enableRotate === false ) return;
+
+ handleTouchStartRotate( event );
+
+ state = STATE.TOUCH_ROTATE;
+
+ break;
+
+ case 2: // two-fingered touch: dolly
+
+ if ( scope.enableZoom === false ) return;
+
+ handleTouchStartDolly( event );
+
+ state = STATE.TOUCH_DOLLY;
+
+ break;
+
+ case 3: // three-fingered touch: pan
+
+ if ( scope.enablePan === false ) return;
+
+ handleTouchStartPan( event );
+
+ state = STATE.TOUCH_PAN;
+
+ break;
+
+ default:
+
+ state = STATE.NONE;
+
+ }
+
+ if ( state !== STATE.NONE ) {
+
+ scope.dispatchEvent( startEvent );
+
+ }
+
+ }
+
+ function onTouchMove( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ switch ( event.touches.length ) {
+
+ case 1: // one-fingered touch: rotate
+
+ if ( scope.enableRotate === false ) return;
+ if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
+
+ handleTouchMoveRotate( event );
+
+ break;
+
+ case 2: // two-fingered touch: dolly
+
+ if ( scope.enableZoom === false ) return;
+ if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
+
+ handleTouchMoveDolly( event );
+
+ break;
+
+ case 3: // three-fingered touch: pan
+
+ if ( scope.enablePan === false ) return;
+ if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
+
+ handleTouchMovePan( event );
+
+ break;
+
+ default:
+
+ state = STATE.NONE;
+
+ }
+
+ }
+
+ function onTouchEnd( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ handleTouchEnd( event );
+
+ scope.dispatchEvent( endEvent );
+
+ state = STATE.NONE;
+
+ }
+
+ function onContextMenu( event ) {
+
+ event.preventDefault();
+
+ }
+
+ //
+
+ scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
+
+ scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
+ scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
+
+ scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
+ scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
+ scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
+
+ window.addEventListener( 'keydown', onKeyDown, false );
+
+ // force an update at start
+
+ this.update();
+
+ };
+
+ OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
+ OrbitControls.prototype.constructor = OrbitControls;
+
+ Object.defineProperties( OrbitControls.prototype, {
+
+ center: {
+
+ get: function () {
+
+ console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
+ return this.target;
+
+ }
+
+ },
+
+ // backward compatibility
+
+ noZoom: {
+
+ get: function () {
+
+ console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
+ return ! this.enableZoom;
+
+ },
+
+ set: function ( value ) {
+
+ console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
+ this.enableZoom = ! value;
+
+ }
+
+ },
+
+ noRotate: {
+
+ get: function () {
+
+ console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
+ return ! this.enableRotate;
+
+ },
+
+ set: function ( value ) {
+
+ console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
+ this.enableRotate = ! value;
+
+ }
+
+ },
+
+ noPan: {
+
+ get: function () {
+
+ console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
+ return ! this.enablePan;
+
+ },
+
+ set: function ( value ) {
+
+ console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
+ this.enablePan = ! value;
+
+ }
+
+ },
+
+ noKeys: {
+
+ get: function () {
+
+ console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
+ return ! this.enableKeys;
+
+ },
+
+ set: function ( value ) {
+
+ console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
+ this.enableKeys = ! value;
+
+ }
+
+ },
+
+ staticMoving : {
+
+ get: function () {
+
+ console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
+ return ! this.enableDamping;
+
+ },
+
+ set: function ( value ) {
+
+ console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
+ this.enableDamping = ! value;
+
+ }
+
+ },
+
+ dynamicDampingFactor : {
+
+ get: function () {
+
+ console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
+ return this.dampingFactor;
+
+ },
+
+ set: function ( value ) {
+
+ console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
+ this.dampingFactor = value;
+
+ }
+
+ }
+
+ } );
+
+ return OrbitControls;
+};
+}());