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 01/36] 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; +}; +}()); From b63479c793baf7b289bdc0e8ee8030d04a9666d4 Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Wed, 1 May 2024 10:19:11 +0200 Subject: [PATCH 02/36] Removed hardcoded date. Added name on popup. --- assets/js/sections/flightpath.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/assets/js/sections/flightpath.js b/assets/js/sections/flightpath.js index 35f95c434..e249edfa7 100644 --- a/assets/js/sections/flightpath.js +++ b/assets/js/sections/flightpath.js @@ -299,7 +299,7 @@ var sats = (function (L, d3, satelliteJs) { } }; - function init() { + function init(satellite) { svgLayer = L.svg(); leafletMap = L.map('sat_map', { zoom: 3, @@ -313,17 +313,10 @@ var sats = (function (L, d3, satelliteJs) { ] }); - // L.terminator().addTo(leafletMap); - - // L.marker(["50","7"],{ - // title: "blabla", - // zIndex: 1000} - // ).addTo(leafletMap); - satmarker = L.marker( [0, 0], { icon: saticon, - title: 'sat', + title: satellite, zIndex: 1000, } ).addTo(leafletMap); @@ -357,7 +350,7 @@ var sats = (function (L, d3, satelliteJs) { .rate(1) .date(TLE_DATA_DATE); sats = parsedTles.map(function (tle) { - var sat = new Satellite(tle, new Date(2024, 4, 18)); + var sat = new Satellite(tle, new Date()); sat.halfAngle(30); // sat.halfAngle(sat.getOrbitType() === 'LEO' ? Math.random() * (30 - 15) + 15 : Math.random() * 4 + 1); return sat; @@ -429,7 +422,7 @@ var sats = (function (L, d3, satelliteJs) { } function start(data) { - init(); + init(data.satellite); initSats(parseTle(data.tle)); } From 1f9f31abad0b69c9a28bedaf2b2627f8925940eb Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Wed, 1 May 2024 10:22:31 +0200 Subject: [PATCH 03/36] Added legend to map --- assets/js/sections/flightpath.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/assets/js/sections/flightpath.js b/assets/js/sections/flightpath.js index e249edfa7..d61de008e 100644 --- a/assets/js/sections/flightpath.js +++ b/assets/js/sections/flightpath.js @@ -321,6 +321,20 @@ var sats = (function (L, d3, satelliteJs) { } ).addTo(leafletMap); + /*Legend specific*/ + var legend = L.control({ position: "topright" }); + + legend.onAdd = function(map) { + var div = L.DomUtil.create("div", "legend"); + div.innerHTML += "

Satellite Orbit

"; + div.innerHTML += "LEO
"; + div.innerHTML += "MEO
"; + div.innerHTML += "GEO
"; + return div; + }; + + legend.addTo(leafletMap); + attributionControl = L.control.attribution({ prefix: '' }).addTo(leafletMap); From e20a740797f85c922d91efc328380025c00ecb64 Mon Sep 17 00:00:00 2001 From: Andreas Kristiansen <6977712+AndreasK79@users.noreply.github.com> Date: Wed, 1 May 2024 11:42:20 +0200 Subject: [PATCH 04/36] Added home icon --- application/controllers/Satellite.php | 6 ++++++ application/views/satellite/flightpath.php | 5 +++++ assets/js/sections/flightpath.js | 10 ++++++++++ 3 files changed, 21 insertions(+) diff --git a/application/controllers/Satellite.php b/application/controllers/Satellite.php index ffd958b1a..6eaf4652b 100644 --- a/application/controllers/Satellite.php +++ b/application/controllers/Satellite.php @@ -140,6 +140,7 @@ class Satellite extends CI_Controller { public function flightpath() { $this->load->model('satellite_model'); + $this->load->model('stations'); $pageData['satellites'] = $this->satellite_model->get_all_satellites_with_tle(); @@ -152,6 +153,11 @@ class Satellite extends CI_Controller { 'assets/js/sections/flightpath.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/flightpath.js")), ]; + $homegrid = explode(',', $this->stations->find_gridsquare()); + + $this->load->library('Qra'); + $pageData['latlng'] = $this->qra->qra2latlong($homegrid[0]); + // Render Page $pageData['page_title'] = "Satellite Flightpath"; $this->load->view('interface_assets/header', $pageData); diff --git a/application/views/satellite/flightpath.php b/application/views/satellite/flightpath.php index 43e175065..7a57c3d6d 100644 --- a/application/views/satellite/flightpath.php +++ b/application/views/satellite/flightpath.php @@ -2,6 +2,11 @@ +