├── LICENSE ├── README.md ├── aws-s3 ├── package.json ├── remove-wx-images.js ├── upload-upcoming-passes.js └── upload-wx-images.js ├── configure.sh ├── receive_and_process_satellite.sh ├── schedule_all.sh ├── schedule_satellite.sh └── website ├── index.html ├── logo.png ├── tle.js └── wx-ground-station.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 nootropic design 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automated Weather Satellite Ground Station 2 | 3 | This project allows you to create a fully automated ground station that will receive and decode NOAA weather satellite images and upload them to your own website served from an Amazon AWS S3 bucket. [See my example S3 site](http://nootropicdesign.wx.s3-website-us-west-2.amazonaws.com/). 4 | For full details of the project, see the [full project writeup on the Project Lab blog](https://nootropicdesign.com/projectlab/2019/11/08/weather-satellite-ground-station/). 5 | 6 | The scripting in this project is largely based on the excellent work by [Jim Haslett](https://www.youtube.com/user/JimHaslett) as described in his well-written Instructable, [Raspberry Pi NOAA Weather Satellite Receiver](https://www.instructables.com/id/Raspberry-Pi-NOAA-Weather-Satellite-Receiver/). 7 | 8 | 9 | -------------------------------------------------------------------------------- /aws-s3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-s3", 3 | "version": "1.0.0", 4 | "description": "scripts for manipulating and uploading wx images to S3 bucket", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "nootropic design", 10 | "license": "ISC", 11 | "dependencies": { 12 | "aws-sdk": "^2.539.0", 13 | "dateformat": "^3.0.3", 14 | "glob": "^7.1.4", 15 | "jimp": "^0.8.4", 16 | "uuid": "^3.3.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /aws-s3/remove-wx-images.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var AWS = require('aws-sdk'); 3 | var uuid = require('uuid'); 4 | 5 | var REGION = ""; 6 | var BUCKET = ""; 7 | var IMAGE_DIR = "images/"; 8 | 9 | AWS.config.update({region: REGION}); 10 | var s3 = new AWS.S3(); 11 | 12 | var filebase = process.argv[2]; 13 | 14 | console.log("Removing files " + filebase + "* from S3..."); 15 | 16 | var files = [ 17 | filebase + ".json", 18 | filebase + "-ZA.png", 19 | filebase + "-NO.png", 20 | filebase + "-MSA.png", 21 | filebase + "-MSAPRECIP.png", 22 | filebase + "-MCIR.png", 23 | filebase + "-THERM.png", 24 | "thumbs/" + filebase + "-ZA.png", 25 | "thumbs/" + filebase + "-NO.png", 26 | "thumbs/" + filebase + "-MSA.png", 27 | "thumbs/" + filebase + "-MSAPRECIP.png", 28 | "thumbs/" + filebase + "-MCIR.png", 29 | "thumbs/" + filebase + "-THERM.png" 30 | ]; 31 | 32 | files.forEach(removeFile); 33 | 34 | 35 | function removeFile(filename) { 36 | var params = { 37 | Bucket: BUCKET, 38 | Key: IMAGE_DIR + filename, 39 | }; 40 | s3.deleteObject(params, (err, data) => { 41 | if (err) { 42 | console.log(err) 43 | } else { 44 | console.log(" successfully removed " + filename); 45 | } 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /aws-s3/upload-upcoming-passes.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var AWS = require('aws-sdk'); 4 | var uuid = require('uuid'); 5 | 6 | var REGION = ""; 7 | var BUCKET = ""; 8 | var IMAGE_DIR = "images/"; 9 | 10 | AWS.config.update({region: REGION}); 11 | var s3 = new AWS.S3(); 12 | 13 | uploadUpcomingPasses(process.argv[2]); 14 | 15 | function uploadUpcomingPasses(filename) { 16 | var upcomingPassesFilename = "upcoming_passes.json"; 17 | var lines = fs.readFileSync(filename).toString().split("\n"); 18 | var count = 0; 19 | var all_passes = []; 20 | lines.forEach((line) => { 21 | if (line.trim().length > 0) { 22 | var fields = line.split(','); 23 | var pass_info = { 24 | start: new Date(0).setUTCSeconds(fields[0]), 25 | end: new Date(0).setUTCSeconds(fields[1]), 26 | elevation: fields[2], 27 | direction: fields[3], 28 | satellite: fields[4], 29 | tle1: fields[5], 30 | tle2: fields[6] 31 | }; 32 | all_passes.push(pass_info); 33 | } 34 | if (++count == lines.length) { 35 | all_passes = all_passes.sort((a, b) => { return a.start-b.start }); 36 | console.log("uploading upcoming pass info"); 37 | var params = { 38 | ACL: "public-read", 39 | ContentType: "application/json", 40 | Bucket: BUCKET, 41 | Key: IMAGE_DIR + upcomingPassesFilename, 42 | Body: JSON.stringify(all_passes, null, 2) 43 | }; 44 | 45 | s3.putObject(params, function(err, data) { 46 | if (err) { 47 | console.log(err) 48 | } else { 49 | console.log(" successfully uploaded " + upcomingPassesFilename); 50 | } 51 | }); 52 | } 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /aws-s3/upload-wx-images.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var glob = require("glob"); 4 | var AWS = require('aws-sdk'); 5 | var uuid = require('uuid'); 6 | var Jimp = require('jimp'); 7 | var dateFormat = require('dateformat'); 8 | 9 | var REGION = ""; 10 | var BUCKET = ""; 11 | var LOCATION = ""; 12 | var IMAGE_DIR = "images/"; 13 | 14 | AWS.config.update({region: REGION}); 15 | var s3 = new AWS.S3(); 16 | 17 | var satellite = process.argv[2]; 18 | var frequency = process.argv[3]; 19 | var filebase = process.argv[4]; 20 | var elevation = process.argv[5]; 21 | var direction = process.argv[6]; 22 | var duration = process.argv[7]; 23 | var tle1 = process.argv[8]; 24 | var tle2 = process.argv[9]; 25 | var gain = process.argv[10]; 26 | var chan_a = process.argv[11]; 27 | var chan_b = process.argv[12]; 28 | 29 | var basename = filebase.slice(filebase.lastIndexOf('/')+1); 30 | var dirname = filebase.slice(0, filebase.lastIndexOf('/')+1); 31 | var components = basename.split("-"); 32 | var date = components[1]; 33 | date = date.slice(0, 4) + '-' + date.slice(4, 6) + '-' + date.slice(6); 34 | var time = components[2]; 35 | time = time.slice(0, 2) + ':' + time.slice(2, 4) + ':' + time.slice(4) + ' ' + dateFormat(new Date, "o"); 36 | 37 | 38 | // example "Gain: 15.2" 39 | if (gain) { 40 | gain = gain.substring(gain.indexOf(": ") + 2) 41 | } 42 | 43 | // example "Channel A: 1 (visible)" 44 | if (chan_a) { 45 | chan_a = chan_a.substring(chan_a.indexOf(": ")+2); 46 | } 47 | // example "Channel B: 4 (thermal infrared)" 48 | if (chan_b) { 49 | chan_b = chan_b.substring(chan_b.indexOf(": ")+2); 50 | } 51 | 52 | console.log("Uploading files " + path.basename(filebase) + "* to S3..."); 53 | 54 | var metadata = { 55 | satellite: satellite, 56 | date: date, 57 | time: time, 58 | elevation: elevation, 59 | direction: direction, 60 | duration: duration, 61 | imageKey: filebase.slice(filebase.lastIndexOf('/')+1), 62 | tle1: tle1, 63 | tle2: tle2, 64 | frequency: frequency, 65 | gain: gain, 66 | chan_a: chan_a, 67 | chan_b: chan_b, 68 | images: [] 69 | }; 70 | 71 | async function uploadImage(image, filename) { 72 | var w = image.bitmap.width; 73 | var h = image.bitmap.height; 74 | var enhancement; 75 | if (filename.endsWith("-ZA.png")) enhancement = "normal infrared"; 76 | if (filename.endsWith("-NO.png")) enhancement = "color infrared"; 77 | if (filename.endsWith("-MSA.png")) enhancement = "multispectral analysis"; 78 | if (filename.endsWith("-MSAPRECIP.png")) enhancement = "multispectral precip"; 79 | if (filename.endsWith("-MCIR.png")) enhancement = "map color infrared"; 80 | if (filename.endsWith("-THERM.png")) enhancement = "thermal"; 81 | var imageInfo = { 82 | filename: filename, 83 | width: w, 84 | height: h, 85 | thumbfilename: 'thumbs/' + filename, 86 | enhancement: enhancement 87 | }; 88 | 89 | 90 | var font = await Jimp.loadFont(Jimp.FONT_SANS_16_WHITE); 91 | var newImage = await new Jimp(image.bitmap.width, image.bitmap.height+64, '#000000'); 92 | newImage.composite(image, 0, 48); 93 | image = newImage; 94 | image.print(font, 5, 5, metadata.date + " " + metadata.time + " satellite: " + metadata.satellite + 95 | " elevation: " + metadata.elevation + '\xB0' + " enhancement: " + enhancement); 96 | image.print(font, 5, 25, LOCATION); 97 | 98 | image.getBuffer(Jimp.MIME_PNG, (err, buffer) => { 99 | var params = { 100 | ACL: "public-read", 101 | ContentType: "image/png", 102 | Bucket: BUCKET, 103 | Key: IMAGE_DIR + filename, 104 | Body: buffer 105 | }; 106 | s3.putObject(params, (err, data) => { 107 | if (err) { 108 | console.log(err) 109 | } else { 110 | console.log(" successfully uploaded " + filename); 111 | } 112 | }); 113 | }); 114 | 115 | var thumb = image.clone(); 116 | thumb.cover(260, 200); 117 | var thumbFilename = "thumbs/" + filename; 118 | thumb.getBuffer(Jimp.MIME_PNG, (err, buffer) => { 119 | var params = { 120 | ACL: "public-read", 121 | ContentType: "image/png", 122 | Bucket: BUCKET, 123 | Key: IMAGE_DIR + thumbFilename, 124 | Body: buffer 125 | }; 126 | s3.putObject(params, (err, data) => { 127 | if (err) { 128 | console.log(err) 129 | } else { 130 | console.log(" successfully uploaded thumb " + filename); 131 | } 132 | }); 133 | }); 134 | 135 | return imageInfo; 136 | } 137 | 138 | function uploadMetadata(filebase) { 139 | var metadataFilename = filebase + ".json"; 140 | console.log("uploading metadata " + JSON.stringify(metadata, null, 2)); 141 | var params = { 142 | ACL: "public-read", 143 | Bucket: BUCKET, 144 | Key: IMAGE_DIR + metadataFilename, 145 | Body: JSON.stringify(metadata, null, 2) 146 | }; 147 | 148 | s3.putObject(params, function(err, data) { 149 | if (err) { 150 | console.log(err) 151 | } else { 152 | console.log(" successfully uploaded metadata " + metadataFilename); 153 | } 154 | }); 155 | } 156 | 157 | 158 | glob(filebase + "-[A-Z]*.png", {}, function (err, files) { 159 | var uploadPromises = []; 160 | files.forEach(function(filename) { 161 | var basename = path.basename(filename); 162 | Jimp.read(filename) 163 | .then(image => { 164 | uploadPromises.push(uploadImage(image, basename)); 165 | if (uploadPromises.length == files.length) { 166 | Promise.all(uploadPromises).then((values) => { 167 | metadata.images = values; 168 | console.log("values: " + JSON.stringify(values, null, 2)); 169 | uploadMetadata(path.basename(filebase)); 170 | }); 171 | } 172 | }); 173 | }); 174 | }); 175 | -------------------------------------------------------------------------------- /configure.sh: -------------------------------------------------------------------------------- 1 | 2 | # create directories 3 | if [ ! -d "audio" ] 4 | then 5 | mkdir audio 6 | fi 7 | 8 | if [ ! -d "images" ] 9 | then 10 | mkdir images 11 | fi 12 | 13 | if [ ! -d "logs" ] 14 | then 15 | mkdir logs 16 | fi 17 | 18 | currentDir=`echo $PWD` 19 | echo "configuring for" $currentDir 20 | 21 | sed -i "s|INSTALL_DIR|$currentDir|g" schedule_all.sh 22 | sed -i "s|INSTALL_DIR|$currentDir|g" schedule_satellite.sh 23 | sed -i "s|INSTALL_DIR|$currentDir|g" receive_and_process_satellite.sh 24 | 25 | chmod +x schedule_all.sh 26 | chmod +x schedule_satellite.sh 27 | chmod +x receive_and_process_satellite.sh 28 | 29 | cronjobcmd="$currentDir/schedule_all.sh" 30 | cronjob="0 0 * * * $cronjobcmd" 31 | ( crontab -l | grep -v -F "$cronjobcmd" ; echo "$cronjob" ) | crontab - 32 | -------------------------------------------------------------------------------- /receive_and_process_satellite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SAT=$1 4 | FREQ=$2 5 | FILEKEY=$3 6 | TLE_FILE=$4 7 | START_TIME=$5 8 | DURATION=$6 9 | ELEVATION=$7 10 | DIRECTION=$8 11 | 12 | AUDIO_DIR=INSTALL_DIR/audio 13 | IMAGE_DIR=INSTALL_DIR/images 14 | LOG_DIR=INSTALL_DIR/logs 15 | MAP_FILE=${IMAGE_DIR}/${FILEKEY}-map.png 16 | AUDIO_FILE=${AUDIO_DIR}/${FILEKEY}.wav 17 | LOGFILE=${LOG_DIR}/${FILEKEY}.log 18 | 19 | echo $@ >> $LOGFILE 20 | 21 | #/usr/local/bin/rtl_biast -b 1 2>> $LOGFILE 22 | sudo timeout $DURATION rtl_fm -f ${FREQ}M -s 60k -g 45 -p 0 -E wav -E deemp -F 9 - 2>> $LOGFILE | sox -t wav - $AUDIO_FILE rate 11025 23 | #/usr/local/bin/rtl_biast -b 0 2>> $LOGFILE 24 | 25 | PassStart=`expr $START_TIME + 90` 26 | 27 | if [ -e $AUDIO_FILE ] 28 | then 29 | /usr/local/bin/wxmap -T "${SAT}" -H $TLE_FILE -p 0 -l 0 -o $PassStart ${MAP_FILE} >> $LOGFILE 2>&1 30 | 31 | /usr/local/bin/wxtoimg -m ${MAP_FILE} -e ZA $AUDIO_FILE ${IMAGE_DIR}/${FILEKEY}-ZA.png >> $LOGFILE 2>&1 32 | 33 | /usr/local/bin/wxtoimg -m ${MAP_FILE} -e NO $AUDIO_FILE ${IMAGE_DIR}/${FILEKEY}-NO.png >> $LOGFILE 2>&1 34 | 35 | /usr/local/bin/wxtoimg -m ${MAP_FILE} -e MSA $AUDIO_FILE ${IMAGE_DIR}/${FILEKEY}-MSA.png >> $LOGFILE 2>&1 36 | 37 | /usr/local/bin/wxtoimg -m ${MAP_FILE} -e MCIR $AUDIO_FILE ${IMAGE_DIR}/${FILEKEY}-MCIR.png >> $LOGFILE 2>&1 38 | 39 | /usr/local/bin/wxtoimg -m ${MAP_FILE} -e therm $AUDIO_FILE ${IMAGE_DIR}/${FILEKEY}-THERM.png >> $LOGFILE 2>&1 40 | 41 | TLE1=`grep "$SAT" $TLE_FILE -A 2 | tail -2 | head -1 | tr -d '\r'` 42 | TLE2=`grep "$SAT" $TLE_FILE -A 2 | tail -2 | tail -1 | tr -d '\r'` 43 | GAIN=`grep Gain $LOGFILE | head -1` 44 | CHAN_A=`grep "Channel A" $LOGFILE | head -1` 45 | CHAN_B=`grep "Channel B" $LOGFILE | head -1` 46 | 47 | echo "node INSTALL_DIR/aws-s3/upload-wx-images.js \"$SAT\" $FREQ ${IMAGE_DIR}/${FILEKEY} $ELEVATION $DIRECTION $DURATION \"${TLE1}\" \"${TLE2}\" \"$GAIN\" \"${CHAN_A}\" \"${CHAN_B}\"" >> $LOGFILE 2>&1 48 | node INSTALL_DIR/aws-s3/upload-wx-images.js "$SAT" $FREQ ${IMAGE_DIR}/${FILEKEY} $ELEVATION $DIRECTION $DURATION "${TLE1}" "${TLE2}" "$GAIN" "${CHAN_A}" "${CHAN_B}" >> $LOGFILE 2>&1 49 | fi 50 | -------------------------------------------------------------------------------- /schedule_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Update Satellite Information 4 | 5 | wget -qr https://www.celestrak.com/NORAD/elements/weather.txt -O INSTALL_DIR/weather.txt 6 | grep "NOAA 15" INSTALL_DIR/weather.txt -A 2 > INSTALL_DIR/weather.tle 7 | grep "NOAA 18" INSTALL_DIR/weather.txt -A 2 >> INSTALL_DIR/weather.tle 8 | grep "NOAA 19" INSTALL_DIR/weather.txt -A 2 >> INSTALL_DIR/weather.tle 9 | 10 | 11 | 12 | #Remove all AT jobs 13 | 14 | for i in `atq | awk '{print $1}'`;do atrm $i;done 15 | 16 | rm -f INSTALL_DIR/upcoming_passes.txt 17 | 18 | #Schedule Satellite Passes: 19 | 20 | INSTALL_DIR/schedule_satellite.sh "NOAA 19" 137.1000 21 | INSTALL_DIR/schedule_satellite.sh "NOAA 18" 137.9125 22 | INSTALL_DIR/schedule_satellite.sh "NOAA 15" 137.6200 23 | 24 | node INSTALL_DIR/aws-s3/upload-upcoming-passes.js INSTALL_DIR/upcoming_passes.txt 25 | -------------------------------------------------------------------------------- /schedule_satellite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SAT=$1 4 | FREQ=$2 5 | TLE_FILE=INSTALL_DIR/weather.tle 6 | PREDICTION_START=`/usr/bin/predict -t $TLE_FILE -p "$SAT" | head -1` 7 | PREDICTION_END=`/usr/bin/predict -t $TLE_FILE -p "$SAT" | tail -1` 8 | 9 | END_EPOCH=`echo $PREDICTION_END | cut -d " " -f 1` 10 | END_EPOCH_DATE=`date --date="TZ=\"UTC\" @${END_EPOCH}" +%D` 11 | 12 | MAXELEV=`/usr/bin/predict -t $TLE_FILE -p "${SAT}" | awk -v max=0 '{if($5>max){max=$5}}END{print max}'` 13 | START_LAT=`echo $PREDICTION_START | awk '{print $8}'` 14 | END_LAT=`echo $PREDICTION_END | awk '{print $8}'` 15 | if [ $START_LAT -gt $END_LAT ] 16 | then 17 | DIR="southbound" 18 | else 19 | DIR="northbound" 20 | fi 21 | 22 | 23 | while [ $END_EPOCH_DATE == `date +%D` ] || [ $END_EPOCH_DATE == `date --date="tomorrow" +%D` ]; do 24 | 25 | START_TIME=`echo $PREDICTION_START | cut -d " " -f 3-4` 26 | START_EPOCH=`echo $PREDICTION_START | cut -d " " -f 1` 27 | 28 | SECONDS_REMAINDER=`echo $START_TIME | cut -d " " -f 2 | cut -d ":" -f 3` 29 | 30 | JOB_START=`date --date="TZ=\"UTC\" $START_TIME" +"%H:%M %D"` 31 | 32 | # at jobs can only be started on minute boundaries, so add the 33 | # seconds remainder to the duration of the pass because the 34 | # recording job will start early 35 | PASS_DURATION=`expr $END_EPOCH - $START_EPOCH` 36 | JOB_TIMER=`expr $PASS_DURATION + $SECONDS_REMAINDER` 37 | OUTDATE=`date --date="TZ=\"UTC\" $START_TIME" +%Y%m%d-%H%M%S` 38 | 39 | if [ $MAXELEV -ge 20 ] 40 | then 41 | FILEKEY="${SAT//" "}-${OUTDATE}" 42 | COMMAND="INSTALL_DIR/receive_and_process_satellite.sh \"${SAT}\" $FREQ $FILEKEY $TLE_FILE $START_EPOCH $JOB_TIMER $MAXELEV $DIR" 43 | echo $COMMAND 44 | echo $COMMAND | at $JOB_START 45 | 46 | TLE1=`grep "$SAT" $TLE_FILE -A 2 | tail -2 | head -1 | tr -d '\r'` 47 | TLE2=`grep "$SAT" $TLE_FILE -A 2 | tail -2 | tail -1 | tr -d '\r'` 48 | 49 | echo ${START_EPOCH},${END_EPOCH},${MAXELEV},${DIR},${SAT},"${TLE1}","${TLE2}" >> INSTALL_DIR/upcoming_passes.txt 50 | fi 51 | 52 | nextpredict=`expr $END_EPOCH + 60` 53 | 54 | PREDICTION_START=`/usr/bin/predict -t $TLE_FILE -p "${SAT}" $nextpredict | head -1` 55 | PREDICTION_END=`/usr/bin/predict -t $TLE_FILE -p "${SAT}" $nextpredict | tail -1` 56 | 57 | MAXELEV=`/usr/bin/predict -t $TLE_FILE -p "${SAT}" $nextpredict | awk -v max=0 '{if($5>max){max=$5}}END{print max}'` 58 | START_LAT=`echo $PREDICTION_START | awk '{print $8}'` 59 | END_LAT=`echo $PREDICTION_END | awk '{print $8}'` 60 | if [ $START_LAT -gt $END_LAT ] 61 | then 62 | DIR="southbound" 63 | else 64 | DIR="northbound" 65 | fi 66 | 67 | END_EPOCH=`echo $PREDICTION_END | cut -d " " -f 1` 68 | END_EPOCH_DATE=`date --date="TZ=\"UTC\" @${END_EPOCH}" +%D` 69 | 70 | done 71 | -------------------------------------------------------------------------------- /website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | weather satellite ground station 5 | 6 | 7 | 10 | 13 | 14 | 15 | 16 | 17 | 38 | 39 | 40 | 41 |
42 |
43 | 44 |

weather satellite ground station

45 |

46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |

Recent captures:

54 |
55 |
56 |
57 | 58 | 59 | 60 | 61 | 62 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /website/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nootropicdesign/wx-ground-station/c5336f719024d9f43797a1cf4d0a8e2e2d8f5ec0/website/logo.png -------------------------------------------------------------------------------- /website/tle.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.TLEJS = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i= 0 ? 1 : -1; 77 | } 78 | 79 | rangeRate *= sign(rangeRate); 80 | var c = 299792.458; // Speed of light in km/s 81 | 82 | return 1 + rangeRate / c; 83 | } 84 | },{}],4:[function(require,module,exports){ 85 | /*! 86 | * satellite-js v3.0.1 87 | * (c) 2013 Shashwat Kandadai and UCSC 88 | * https://github.com/shashwatak/satellite-js 89 | * License: MIT 90 | */ 91 | 92 | "use strict"; 93 | 94 | Object.defineProperty(exports, "__esModule", { 95 | value: true 96 | }); 97 | exports.days2mdhms = days2mdhms; 98 | exports.jday = jday; 99 | exports.invjday = invjday; 100 | 101 | /* ----------------------------------------------------------------------------- 102 | * 103 | * procedure days2mdhms 104 | * 105 | * this procedure converts the day of the year, days, to the equivalent month 106 | * day, hour, minute and second. 107 | * 108 | * algorithm : set up array for the number of days per month 109 | * find leap year - use 1900 because 2000 is a leap year 110 | * loop through a temp value while the value is < the days 111 | * perform int conversions to the correct day and month 112 | * convert remainder into h m s using type conversions 113 | * 114 | * author : david vallado 719-573-2600 1 mar 2001 115 | * 116 | * inputs description range / units 117 | * year - year 1900 .. 2100 118 | * days - julian day of the year 0.0 .. 366.0 119 | * 120 | * outputs : 121 | * mon - month 1 .. 12 122 | * day - day 1 .. 28,29,30,31 123 | * hr - hour 0 .. 23 124 | * min - minute 0 .. 59 125 | * sec - second 0.0 .. 59.999 126 | * 127 | * locals : 128 | * dayofyr - day of year 129 | * temp - temporary extended values 130 | * inttemp - temporary int value 131 | * i - index 132 | * lmonth[12] - int array containing the number of days per month 133 | * 134 | * coupling : 135 | * none. 136 | * --------------------------------------------------------------------------- */ 137 | function days2mdhms(year, days) { 138 | var lmonth = [31, year % 4 === 0 ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 139 | var dayofyr = Math.floor(days); // ----------------- find month and day of month ---------------- 140 | 141 | var i = 1; 142 | var inttemp = 0; 143 | 144 | while (dayofyr > inttemp + lmonth[i - 1] && i < 12) { 145 | inttemp += lmonth[i - 1]; 146 | i += 1; 147 | } 148 | 149 | var mon = i; 150 | var day = dayofyr - inttemp; // ----------------- find hours minutes and seconds ------------- 151 | 152 | var temp = (days - dayofyr) * 24.0; 153 | var hr = Math.floor(temp); 154 | temp = (temp - hr) * 60.0; 155 | var minute = Math.floor(temp); 156 | var sec = (temp - minute) * 60.0; 157 | return { 158 | mon: mon, 159 | day: day, 160 | hr: hr, 161 | minute: minute, 162 | sec: sec 163 | }; 164 | } 165 | /* ----------------------------------------------------------------------------- 166 | * 167 | * procedure jday 168 | * 169 | * this procedure finds the julian date given the year, month, day, and time. 170 | * the julian date is defined by each elapsed day since noon, jan 1, 4713 bc. 171 | * 172 | * algorithm : calculate the answer in one step for efficiency 173 | * 174 | * author : david vallado 719-573-2600 1 mar 2001 175 | * 176 | * inputs description range / units 177 | * year - year 1900 .. 2100 178 | * mon - month 1 .. 12 179 | * day - day 1 .. 28,29,30,31 180 | * hr - universal time hour 0 .. 23 181 | * min - universal time min 0 .. 59 182 | * sec - universal time sec 0.0 .. 59.999 183 | * 184 | * outputs : 185 | * jd - julian date days from 4713 bc 186 | * 187 | * locals : 188 | * none. 189 | * 190 | * coupling : 191 | * none. 192 | * 193 | * references : 194 | * vallado 2007, 189, alg 14, ex 3-14 195 | * 196 | * --------------------------------------------------------------------------- */ 197 | 198 | 199 | function jdayInternal(year, mon, day, hr, minute, sec) { 200 | var msec = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0; 201 | 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 202 | // # - 0.5*sgn(100.0*year + mon - 190002.5) + 0.5; 203 | ; 204 | } 205 | 206 | function jday(year, mon, day, hr, minute, sec, msec) { 207 | if (year instanceof Date) { 208 | var date = year; 209 | return jdayInternal(date.getUTCFullYear(), date.getUTCMonth() + 1, // Note, this function requires months in range 1-12. 210 | date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds()); 211 | } 212 | 213 | return jdayInternal(year, mon, day, hr, minute, sec, msec); 214 | } 215 | /* ----------------------------------------------------------------------------- 216 | * 217 | * procedure invjday 218 | * 219 | * this procedure finds the year, month, day, hour, minute and second 220 | * given the julian date. tu can be ut1, tdt, tdb, etc. 221 | * 222 | * algorithm : set up starting values 223 | * find leap year - use 1900 because 2000 is a leap year 224 | * find the elapsed days through the year in a loop 225 | * call routine to find each individual value 226 | * 227 | * author : david vallado 719-573-2600 1 mar 2001 228 | * 229 | * inputs description range / units 230 | * jd - julian date days from 4713 bc 231 | * 232 | * outputs : 233 | * year - year 1900 .. 2100 234 | * mon - month 1 .. 12 235 | * day - day 1 .. 28,29,30,31 236 | * hr - hour 0 .. 23 237 | * min - minute 0 .. 59 238 | * sec - second 0.0 .. 59.999 239 | * 240 | * locals : 241 | * days - day of year plus fractional 242 | * portion of a day days 243 | * tu - julian centuries from 0 h 244 | * jan 0, 1900 245 | * temp - temporary double values 246 | * leapyrs - number of leap years from 1900 247 | * 248 | * coupling : 249 | * days2mdhms - finds month, day, hour, minute and second given days and year 250 | * 251 | * references : 252 | * vallado 2007, 208, alg 22, ex 3-13 253 | * --------------------------------------------------------------------------- */ 254 | 255 | 256 | function invjday(jd, asArray) { 257 | // --------------- find year and days of the year - 258 | var temp = jd - 2415019.5; 259 | var tu = temp / 365.25; 260 | var year = 1900 + Math.floor(tu); 261 | var leapyrs = Math.floor((year - 1901) * 0.25); // optional nudge by 8.64x10-7 sec to get even outputs 262 | 263 | var days = temp - ((year - 1900) * 365.0 + leapyrs) + 0.00000000001; // ------------ check for case of beginning of a year ----------- 264 | 265 | if (days < 1.0) { 266 | year -= 1; 267 | leapyrs = Math.floor((year - 1901) * 0.25); 268 | days = temp - ((year - 1900) * 365.0 + leapyrs); 269 | } // ----------------- find remaing data ------------------------- 270 | 271 | 272 | var mdhms = days2mdhms(year, days); 273 | var mon = mdhms.mon, 274 | day = mdhms.day, 275 | hr = mdhms.hr, 276 | minute = mdhms.minute; 277 | var sec = mdhms.sec - 0.00000086400; 278 | 279 | if (asArray) { 280 | return [year, mon, day, hr, minute, Math.floor(sec)]; 281 | } 282 | 283 | return new Date(Date.UTC(year, mon - 1, day, hr, minute, Math.floor(sec))); 284 | } 285 | },{}],5:[function(require,module,exports){ 286 | /*! 287 | * satellite-js v3.0.1 288 | * (c) 2013 Shashwat Kandadai and UCSC 289 | * https://github.com/shashwatak/satellite-js 290 | * License: MIT 291 | */ 292 | 293 | "use strict"; 294 | 295 | Object.defineProperty(exports, "__esModule", { 296 | value: true 297 | }); 298 | Object.defineProperty(exports, "jday", { 299 | enumerable: true, 300 | get: function get() { 301 | return _ext.jday; 302 | } 303 | }); 304 | Object.defineProperty(exports, "invjday", { 305 | enumerable: true, 306 | get: function get() { 307 | return _ext.invjday; 308 | } 309 | }); 310 | Object.defineProperty(exports, "twoline2satrec", { 311 | enumerable: true, 312 | get: function get() { 313 | return _io.default; 314 | } 315 | }); 316 | Object.defineProperty(exports, "propagate", { 317 | enumerable: true, 318 | get: function get() { 319 | return _propagation.propagate; 320 | } 321 | }); 322 | Object.defineProperty(exports, "sgp4", { 323 | enumerable: true, 324 | get: function get() { 325 | return _propagation.sgp4; 326 | } 327 | }); 328 | Object.defineProperty(exports, "gstime", { 329 | enumerable: true, 330 | get: function get() { 331 | return _propagation.gstime; 332 | } 333 | }); 334 | Object.defineProperty(exports, "dopplerFactor", { 335 | enumerable: true, 336 | get: function get() { 337 | return _dopplerFactor.default; 338 | } 339 | }); 340 | Object.defineProperty(exports, "radiansToDegrees", { 341 | enumerable: true, 342 | get: function get() { 343 | return _transforms.radiansToDegrees; 344 | } 345 | }); 346 | Object.defineProperty(exports, "degreesToRadians", { 347 | enumerable: true, 348 | get: function get() { 349 | return _transforms.degreesToRadians; 350 | } 351 | }); 352 | Object.defineProperty(exports, "degreesLat", { 353 | enumerable: true, 354 | get: function get() { 355 | return _transforms.degreesLat; 356 | } 357 | }); 358 | Object.defineProperty(exports, "degreesLong", { 359 | enumerable: true, 360 | get: function get() { 361 | return _transforms.degreesLong; 362 | } 363 | }); 364 | Object.defineProperty(exports, "radiansLat", { 365 | enumerable: true, 366 | get: function get() { 367 | return _transforms.radiansLat; 368 | } 369 | }); 370 | Object.defineProperty(exports, "radiansLong", { 371 | enumerable: true, 372 | get: function get() { 373 | return _transforms.radiansLong; 374 | } 375 | }); 376 | Object.defineProperty(exports, "geodeticToEcf", { 377 | enumerable: true, 378 | get: function get() { 379 | return _transforms.geodeticToEcf; 380 | } 381 | }); 382 | Object.defineProperty(exports, "eciToGeodetic", { 383 | enumerable: true, 384 | get: function get() { 385 | return _transforms.eciToGeodetic; 386 | } 387 | }); 388 | Object.defineProperty(exports, "eciToEcf", { 389 | enumerable: true, 390 | get: function get() { 391 | return _transforms.eciToEcf; 392 | } 393 | }); 394 | Object.defineProperty(exports, "ecfToEci", { 395 | enumerable: true, 396 | get: function get() { 397 | return _transforms.ecfToEci; 398 | } 399 | }); 400 | Object.defineProperty(exports, "ecfToLookAngles", { 401 | enumerable: true, 402 | get: function get() { 403 | return _transforms.ecfToLookAngles; 404 | } 405 | }); 406 | exports.constants = void 0; 407 | 408 | var constants = _interopRequireWildcard(require("./constants")); 409 | 410 | exports.constants = constants; 411 | 412 | var _ext = require("./ext"); 413 | 414 | var _io = _interopRequireDefault(require("./io")); 415 | 416 | var _propagation = require("./propagation"); 417 | 418 | var _dopplerFactor = _interopRequireDefault(require("./dopplerFactor")); 419 | 420 | var _transforms = require("./transforms"); 421 | 422 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 423 | 424 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } 425 | },{"./constants":2,"./dopplerFactor":3,"./ext":4,"./io":6,"./propagation":7,"./transforms":17}],6:[function(require,module,exports){ 426 | /*! 427 | * satellite-js v3.0.1 428 | * (c) 2013 Shashwat Kandadai and UCSC 429 | * https://github.com/shashwatak/satellite-js 430 | * License: MIT 431 | */ 432 | 433 | "use strict"; 434 | 435 | Object.defineProperty(exports, "__esModule", { 436 | value: true 437 | }); 438 | exports.default = twoline2satrec; 439 | 440 | var _constants = require("./constants"); 441 | 442 | var _ext = require("./ext"); 443 | 444 | var _sgp4init = _interopRequireDefault(require("./propagation/sgp4init")); 445 | 446 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 447 | 448 | /* ----------------------------------------------------------------------------- 449 | * 450 | * function twoline2rv 451 | * 452 | * this function converts the two line element set character string data to 453 | * variables and initializes the sgp4 variables. several intermediate varaibles 454 | * and quantities are determined. note that the result is a structure so multiple 455 | * satellites can be processed simultaneously without having to reinitialize. the 456 | * verification mode is an important option that permits quick checks of any 457 | * changes to the underlying technical theory. this option works using a 458 | * modified tle file in which the start, stop, and delta time values are 459 | * included at the end of the second line of data. this only works with the 460 | * verification mode. the catalog mode simply propagates from -1440 to 1440 min 461 | * from epoch and is useful when performing entire catalog runs. 462 | * 463 | * author : david vallado 719-573-2600 1 mar 2001 464 | * 465 | * inputs : 466 | * longstr1 - first line of the tle 467 | * longstr2 - second line of the tle 468 | * typerun - type of run verification 'v', catalog 'c', 469 | * manual 'm' 470 | * typeinput - type of manual input mfe 'm', epoch 'e', dayofyr 'd' 471 | * opsmode - mode of operation afspc or improved 'a', 'i' 472 | * whichconst - which set of constants to use 72, 84 473 | * 474 | * outputs : 475 | * satrec - structure containing all the sgp4 satellite information 476 | * 477 | * coupling : 478 | * getgravconst- 479 | * days2mdhms - conversion of days to month, day, hour, minute, second 480 | * jday - convert day month year hour minute second into julian date 481 | * sgp4init - initialize the sgp4 variables 482 | * 483 | * references : 484 | * norad spacetrack report #3 485 | * vallado, crawford, hujsak, kelso 2006 486 | --------------------------------------------------------------------------- */ 487 | 488 | /** 489 | * Return a Satellite imported from two lines of TLE data. 490 | * 491 | * Provide the two TLE lines as strings `longstr1` and `longstr2`, 492 | * and select which standard set of gravitational constants you want 493 | * by providing `gravity_constants`: 494 | * 495 | * `sgp4.propagation.wgs72` - Standard WGS 72 model 496 | * `sgp4.propagation.wgs84` - More recent WGS 84 model 497 | * `sgp4.propagation.wgs72old` - Legacy support for old SGP4 behavior 498 | * 499 | * Normally, computations are made using letious recent improvements 500 | * to the algorithm. If you want to turn some of these off and go 501 | * back into "afspc" mode, then set `afspc_mode` to `True`. 502 | */ 503 | function twoline2satrec(longstr1, longstr2) { 504 | var opsmode = 'i'; 505 | var xpdotp = 1440.0 / (2.0 * _constants.pi); // 229.1831180523293; 506 | 507 | var year = 0; 508 | var satrec = {}; 509 | satrec.error = 0; 510 | satrec.satnum = longstr1.substring(2, 7); 511 | satrec.epochyr = parseInt(longstr1.substring(18, 20), 10); 512 | satrec.epochdays = parseFloat(longstr1.substring(20, 32)); 513 | satrec.ndot = parseFloat(longstr1.substring(33, 43)); 514 | satrec.nddot = parseFloat(".".concat(parseInt(longstr1.substring(44, 50), 10), "E").concat(longstr1.substring(50, 52))); 515 | 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); 516 | 517 | satrec.inclo = parseFloat(longstr2.substring(8, 16)); 518 | satrec.nodeo = parseFloat(longstr2.substring(17, 25)); 519 | satrec.ecco = parseFloat(".".concat(longstr2.substring(26, 33))); 520 | satrec.argpo = parseFloat(longstr2.substring(34, 42)); 521 | satrec.mo = parseFloat(longstr2.substring(43, 51)); 522 | satrec.no = parseFloat(longstr2.substring(52, 63)); // ---- find no, ndot, nddot ---- 523 | 524 | satrec.no /= xpdotp; // rad/min 525 | // satrec.nddot= satrec.nddot * Math.pow(10.0, nexp); 526 | // satrec.bstar= satrec.bstar * Math.pow(10.0, ibexp); 527 | // ---- convert to sgp4 units ---- 528 | 529 | satrec.a = Math.pow(satrec.no * _constants.tumin, -2.0 / 3.0); 530 | satrec.ndot /= xpdotp * 1440.0; // ? * minperday 531 | 532 | satrec.nddot /= xpdotp * 1440.0 * 1440; // ---- find standard orbital elements ---- 533 | 534 | satrec.inclo *= _constants.deg2rad; 535 | satrec.nodeo *= _constants.deg2rad; 536 | satrec.argpo *= _constants.deg2rad; 537 | satrec.mo *= _constants.deg2rad; 538 | satrec.alta = satrec.a * (1.0 + satrec.ecco) - 1.0; 539 | satrec.altp = satrec.a * (1.0 - satrec.ecco) - 1.0; // ---------------------------------------------------------------- 540 | // find sgp4epoch time of element set 541 | // remember that sgp4 uses units of days from 0 jan 1950 (sgp4epoch) 542 | // and minutes from the epoch (time) 543 | // ---------------------------------------------------------------- 544 | // ---------------- temp fix for years from 1957-2056 ------------------- 545 | // --------- correct fix will occur when year is 4-digit in tle --------- 546 | 547 | if (satrec.epochyr < 57) { 548 | year = satrec.epochyr + 2000; 549 | } else { 550 | year = satrec.epochyr + 1900; 551 | } 552 | 553 | var mdhmsResult = (0, _ext.days2mdhms)(year, satrec.epochdays); 554 | var mon = mdhmsResult.mon, 555 | day = mdhmsResult.day, 556 | hr = mdhmsResult.hr, 557 | minute = mdhmsResult.minute, 558 | sec = mdhmsResult.sec; 559 | satrec.jdsatepoch = (0, _ext.jday)(year, mon, day, hr, minute, sec); // ---------------- initialize the orbit at sgp4epoch ------------------- 560 | 561 | (0, _sgp4init.default)(satrec, { 562 | opsmode: opsmode, 563 | satn: satrec.satnum, 564 | epoch: satrec.jdsatepoch - 2433281.5, 565 | xbstar: satrec.bstar, 566 | xecco: satrec.ecco, 567 | xargpo: satrec.argpo, 568 | xinclo: satrec.inclo, 569 | xmo: satrec.mo, 570 | xno: satrec.no, 571 | xnodeo: satrec.nodeo 572 | }); 573 | return satrec; 574 | } 575 | },{"./constants":2,"./ext":4,"./propagation/sgp4init":16}],7:[function(require,module,exports){ 576 | /*! 577 | * satellite-js v3.0.1 578 | * (c) 2013 Shashwat Kandadai and UCSC 579 | * https://github.com/shashwatak/satellite-js 580 | * License: MIT 581 | */ 582 | 583 | "use strict"; 584 | 585 | Object.defineProperty(exports, "__esModule", { 586 | value: true 587 | }); 588 | Object.defineProperty(exports, "propagate", { 589 | enumerable: true, 590 | get: function get() { 591 | return _propagate.default; 592 | } 593 | }); 594 | Object.defineProperty(exports, "sgp4", { 595 | enumerable: true, 596 | get: function get() { 597 | return _sgp.default; 598 | } 599 | }); 600 | Object.defineProperty(exports, "gstime", { 601 | enumerable: true, 602 | get: function get() { 603 | return _gstime.default; 604 | } 605 | }); 606 | 607 | var _propagate = _interopRequireDefault(require("./propagation/propagate")); 608 | 609 | var _sgp = _interopRequireDefault(require("./propagation/sgp4")); 610 | 611 | var _gstime = _interopRequireDefault(require("./propagation/gstime")); 612 | 613 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 614 | },{"./propagation/gstime":12,"./propagation/propagate":14,"./propagation/sgp4":15}],8:[function(require,module,exports){ 615 | /*! 616 | * satellite-js v3.0.1 617 | * (c) 2013 Shashwat Kandadai and UCSC 618 | * https://github.com/shashwatak/satellite-js 619 | * License: MIT 620 | */ 621 | 622 | "use strict"; 623 | 624 | Object.defineProperty(exports, "__esModule", { 625 | value: true 626 | }); 627 | exports.default = dpper; 628 | 629 | var _constants = require("../constants"); 630 | 631 | /* ----------------------------------------------------------------------------- 632 | * 633 | * procedure dpper 634 | * 635 | * this procedure provides deep space long period periodic contributions 636 | * to the mean elements. by design, these periodics are zero at epoch. 637 | * this used to be dscom which included initialization, but it's really a 638 | * recurring function. 639 | * 640 | * author : david vallado 719-573-2600 28 jun 2005 641 | * 642 | * inputs : 643 | * e3 - 644 | * ee2 - 645 | * peo - 646 | * pgho - 647 | * pho - 648 | * pinco - 649 | * plo - 650 | * se2 , se3 , sgh2, sgh3, sgh4, sh2, sh3, si2, si3, sl2, sl3, sl4 - 651 | * t - 652 | * xh2, xh3, xi2, xi3, xl2, xl3, xl4 - 653 | * zmol - 654 | * zmos - 655 | * ep - eccentricity 0.0 - 1.0 656 | * inclo - inclination - needed for lyddane modification 657 | * nodep - right ascension of ascending node 658 | * argpp - argument of perigee 659 | * mp - mean anomaly 660 | * 661 | * outputs : 662 | * ep - eccentricity 0.0 - 1.0 663 | * inclp - inclination 664 | * nodep - right ascension of ascending node 665 | * argpp - argument of perigee 666 | * mp - mean anomaly 667 | * 668 | * locals : 669 | * alfdp - 670 | * betdp - 671 | * cosip , sinip , cosop , sinop , 672 | * dalf - 673 | * dbet - 674 | * dls - 675 | * f2, f3 - 676 | * pe - 677 | * pgh - 678 | * ph - 679 | * pinc - 680 | * pl - 681 | * sel , ses , sghl , sghs , shl , shs , sil , sinzf , sis , 682 | * sll , sls 683 | * xls - 684 | * xnoh - 685 | * zf - 686 | * zm - 687 | * 688 | * coupling : 689 | * none. 690 | * 691 | * references : 692 | * hoots, roehrich, norad spacetrack report #3 1980 693 | * hoots, norad spacetrack report #6 1986 694 | * hoots, schumacher and glover 2004 695 | * vallado, crawford, hujsak, kelso 2006 696 | ----------------------------------------------------------------------------*/ 697 | function dpper(satrec, options) { 698 | var e3 = satrec.e3, 699 | ee2 = satrec.ee2, 700 | peo = satrec.peo, 701 | pgho = satrec.pgho, 702 | pho = satrec.pho, 703 | pinco = satrec.pinco, 704 | plo = satrec.plo, 705 | se2 = satrec.se2, 706 | se3 = satrec.se3, 707 | sgh2 = satrec.sgh2, 708 | sgh3 = satrec.sgh3, 709 | sgh4 = satrec.sgh4, 710 | sh2 = satrec.sh2, 711 | sh3 = satrec.sh3, 712 | si2 = satrec.si2, 713 | si3 = satrec.si3, 714 | sl2 = satrec.sl2, 715 | sl3 = satrec.sl3, 716 | sl4 = satrec.sl4, 717 | t = satrec.t, 718 | xgh2 = satrec.xgh2, 719 | xgh3 = satrec.xgh3, 720 | xgh4 = satrec.xgh4, 721 | xh2 = satrec.xh2, 722 | xh3 = satrec.xh3, 723 | xi2 = satrec.xi2, 724 | xi3 = satrec.xi3, 725 | xl2 = satrec.xl2, 726 | xl3 = satrec.xl3, 727 | xl4 = satrec.xl4, 728 | zmol = satrec.zmol, 729 | zmos = satrec.zmos; 730 | var init = options.init, 731 | opsmode = options.opsmode; 732 | var ep = options.ep, 733 | inclp = options.inclp, 734 | nodep = options.nodep, 735 | argpp = options.argpp, 736 | mp = options.mp; // Copy satellite attributes into local variables for convenience 737 | // and symmetry in writing formulae. 738 | 739 | var alfdp; 740 | var betdp; 741 | var cosip; 742 | var sinip; 743 | var cosop; 744 | var sinop; 745 | var dalf; 746 | var dbet; 747 | var dls; 748 | var f2; 749 | var f3; 750 | var pe; 751 | var pgh; 752 | var ph; 753 | var pinc; 754 | var pl; 755 | var sinzf; 756 | var xls; 757 | var xnoh; 758 | var zf; 759 | var zm; // ---------------------- constants ----------------------------- 760 | 761 | var zns = 1.19459e-5; 762 | var zes = 0.01675; 763 | var znl = 1.5835218e-4; 764 | var zel = 0.05490; // --------------- calculate time varying periodics ----------- 765 | 766 | zm = zmos + zns * t; // be sure that the initial call has time set to zero 767 | 768 | if (init === 'y') { 769 | zm = zmos; 770 | } 771 | 772 | zf = zm + 2.0 * zes * Math.sin(zm); 773 | sinzf = Math.sin(zf); 774 | f2 = 0.5 * sinzf * sinzf - 0.25; 775 | f3 = -0.5 * sinzf * Math.cos(zf); 776 | var ses = se2 * f2 + se3 * f3; 777 | var sis = si2 * f2 + si3 * f3; 778 | var sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf; 779 | var sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf; 780 | var shs = sh2 * f2 + sh3 * f3; 781 | zm = zmol + znl * t; 782 | 783 | if (init === 'y') { 784 | zm = zmol; 785 | } 786 | 787 | zf = zm + 2.0 * zel * Math.sin(zm); 788 | sinzf = Math.sin(zf); 789 | f2 = 0.5 * sinzf * sinzf - 0.25; 790 | f3 = -0.5 * sinzf * Math.cos(zf); 791 | var sel = ee2 * f2 + e3 * f3; 792 | var sil = xi2 * f2 + xi3 * f3; 793 | var sll = xl2 * f2 + xl3 * f3 + xl4 * sinzf; 794 | var sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf; 795 | var shll = xh2 * f2 + xh3 * f3; 796 | pe = ses + sel; 797 | pinc = sis + sil; 798 | pl = sls + sll; 799 | pgh = sghs + sghl; 800 | ph = shs + shll; 801 | 802 | if (init === 'n') { 803 | pe -= peo; 804 | pinc -= pinco; 805 | pl -= plo; 806 | pgh -= pgho; 807 | ph -= pho; 808 | inclp += pinc; 809 | ep += pe; 810 | sinip = Math.sin(inclp); 811 | cosip = Math.cos(inclp); 812 | /* ----------------- apply periodics directly ------------ */ 813 | // sgp4fix for lyddane choice 814 | // strn3 used original inclination - this is technically feasible 815 | // gsfc used perturbed inclination - also technically feasible 816 | // probably best to readjust the 0.2 limit value and limit discontinuity 817 | // 0.2 rad = 11.45916 deg 818 | // use next line for original strn3 approach and original inclination 819 | // if (inclo >= 0.2) 820 | // use next line for gsfc version and perturbed inclination 821 | 822 | if (inclp >= 0.2) { 823 | ph /= sinip; 824 | pgh -= cosip * ph; 825 | argpp += pgh; 826 | nodep += ph; 827 | mp += pl; 828 | } else { 829 | // ---- apply periodics with lyddane modification ---- 830 | sinop = Math.sin(nodep); 831 | cosop = Math.cos(nodep); 832 | alfdp = sinip * sinop; 833 | betdp = sinip * cosop; 834 | dalf = ph * cosop + pinc * cosip * sinop; 835 | dbet = -ph * sinop + pinc * cosip * cosop; 836 | alfdp += dalf; 837 | betdp += dbet; 838 | nodep %= _constants.twoPi; // sgp4fix for afspc written intrinsic functions 839 | // nodep used without a trigonometric function ahead 840 | 841 | if (nodep < 0.0 && opsmode === 'a') { 842 | nodep += _constants.twoPi; 843 | } 844 | 845 | xls = mp + argpp + cosip * nodep; 846 | dls = pl + pgh - pinc * nodep * sinip; 847 | xls += dls; 848 | xnoh = nodep; 849 | nodep = Math.atan2(alfdp, betdp); // sgp4fix for afspc written intrinsic functions 850 | // nodep used without a trigonometric function ahead 851 | 852 | if (nodep < 0.0 && opsmode === 'a') { 853 | nodep += _constants.twoPi; 854 | } 855 | 856 | if (Math.abs(xnoh - nodep) > _constants.pi) { 857 | if (nodep < xnoh) { 858 | nodep += _constants.twoPi; 859 | } else { 860 | nodep -= _constants.twoPi; 861 | } 862 | } 863 | 864 | mp += pl; 865 | argpp = xls - mp - cosip * nodep; 866 | } 867 | } 868 | 869 | return { 870 | ep: ep, 871 | inclp: inclp, 872 | nodep: nodep, 873 | argpp: argpp, 874 | mp: mp 875 | }; 876 | } 877 | },{"../constants":2}],9:[function(require,module,exports){ 878 | /*! 879 | * satellite-js v3.0.1 880 | * (c) 2013 Shashwat Kandadai and UCSC 881 | * https://github.com/shashwatak/satellite-js 882 | * License: MIT 883 | */ 884 | 885 | "use strict"; 886 | 887 | Object.defineProperty(exports, "__esModule", { 888 | value: true 889 | }); 890 | exports.default = dscom; 891 | 892 | var _constants = require("../constants"); 893 | 894 | /*----------------------------------------------------------------------------- 895 | * 896 | * procedure dscom 897 | * 898 | * this procedure provides deep space common items used by both the secular 899 | * and periodics subroutines. input is provided as shown. this routine 900 | * used to be called dpper, but the functions inside weren't well organized. 901 | * 902 | * author : david vallado 719-573-2600 28 jun 2005 903 | * 904 | * inputs : 905 | * epoch - 906 | * ep - eccentricity 907 | * argpp - argument of perigee 908 | * tc - 909 | * inclp - inclination 910 | * nodep - right ascension of ascending node 911 | * np - mean motion 912 | * 913 | * outputs : 914 | * sinim , cosim , sinomm , cosomm , snodm , cnodm 915 | * day - 916 | * e3 - 917 | * ee2 - 918 | * em - eccentricity 919 | * emsq - eccentricity squared 920 | * gam - 921 | * peo - 922 | * pgho - 923 | * pho - 924 | * pinco - 925 | * plo - 926 | * rtemsq - 927 | * se2, se3 - 928 | * sgh2, sgh3, sgh4 - 929 | * sh2, sh3, si2, si3, sl2, sl3, sl4 - 930 | * s1, s2, s3, s4, s5, s6, s7 - 931 | * ss1, ss2, ss3, ss4, ss5, ss6, ss7, sz1, sz2, sz3 - 932 | * sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33 - 933 | * xgh2, xgh3, xgh4, xh2, xh3, xi2, xi3, xl2, xl3, xl4 - 934 | * nm - mean motion 935 | * z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33 - 936 | * zmol - 937 | * zmos - 938 | * 939 | * locals : 940 | * a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 - 941 | * betasq - 942 | * cc - 943 | * ctem, stem - 944 | * x1, x2, x3, x4, x5, x6, x7, x8 - 945 | * xnodce - 946 | * xnoi - 947 | * zcosg , zsing , zcosgl , zsingl , zcosh , zsinh , zcoshl , zsinhl , 948 | * zcosi , zsini , zcosil , zsinil , 949 | * zx - 950 | * zy - 951 | * 952 | * coupling : 953 | * none. 954 | * 955 | * references : 956 | * hoots, roehrich, norad spacetrack report #3 1980 957 | * hoots, norad spacetrack report #6 1986 958 | * hoots, schumacher and glover 2004 959 | * vallado, crawford, hujsak, kelso 2006 960 | ----------------------------------------------------------------------------*/ 961 | function dscom(options) { 962 | var epoch = options.epoch, 963 | ep = options.ep, 964 | argpp = options.argpp, 965 | tc = options.tc, 966 | inclp = options.inclp, 967 | nodep = options.nodep, 968 | np = options.np; 969 | var a1; 970 | var a2; 971 | var a3; 972 | var a4; 973 | var a5; 974 | var a6; 975 | var a7; 976 | var a8; 977 | var a9; 978 | var a10; 979 | var cc; 980 | var x1; 981 | var x2; 982 | var x3; 983 | var x4; 984 | var x5; 985 | var x6; 986 | var x7; 987 | var x8; 988 | var zcosg; 989 | var zsing; 990 | var zcosh; 991 | var zsinh; 992 | var zcosi; 993 | var zsini; 994 | var ss1; 995 | var ss2; 996 | var ss3; 997 | var ss4; 998 | var ss5; 999 | var ss6; 1000 | var ss7; 1001 | var sz1; 1002 | var sz2; 1003 | var sz3; 1004 | var sz11; 1005 | var sz12; 1006 | var sz13; 1007 | var sz21; 1008 | var sz22; 1009 | var sz23; 1010 | var sz31; 1011 | var sz32; 1012 | var sz33; 1013 | var s1; 1014 | var s2; 1015 | var s3; 1016 | var s4; 1017 | var s5; 1018 | var s6; 1019 | var s7; 1020 | var z1; 1021 | var z2; 1022 | var z3; 1023 | var z11; 1024 | var z12; 1025 | var z13; 1026 | var z21; 1027 | var z22; 1028 | var z23; 1029 | var z31; 1030 | var z32; 1031 | var z33; // -------------------------- constants ------------------------- 1032 | 1033 | var zes = 0.01675; 1034 | var zel = 0.05490; 1035 | var c1ss = 2.9864797e-6; 1036 | var c1l = 4.7968065e-7; 1037 | var zsinis = 0.39785416; 1038 | var zcosis = 0.91744867; 1039 | var zcosgs = 0.1945905; 1040 | var zsings = -0.98088458; // --------------------- local variables ------------------------ 1041 | 1042 | var nm = np; 1043 | var em = ep; 1044 | var snodm = Math.sin(nodep); 1045 | var cnodm = Math.cos(nodep); 1046 | var sinomm = Math.sin(argpp); 1047 | var cosomm = Math.cos(argpp); 1048 | var sinim = Math.sin(inclp); 1049 | var cosim = Math.cos(inclp); 1050 | var emsq = em * em; 1051 | var betasq = 1.0 - emsq; 1052 | var rtemsq = Math.sqrt(betasq); // ----------------- initialize lunar solar terms --------------- 1053 | 1054 | var peo = 0.0; 1055 | var pinco = 0.0; 1056 | var plo = 0.0; 1057 | var pgho = 0.0; 1058 | var pho = 0.0; 1059 | var day = epoch + 18261.5 + tc / 1440.0; 1060 | var xnodce = (4.5236020 - 9.2422029e-4 * day) % _constants.twoPi; 1061 | var stem = Math.sin(xnodce); 1062 | var ctem = Math.cos(xnodce); 1063 | var zcosil = 0.91375164 - 0.03568096 * ctem; 1064 | var zsinil = Math.sqrt(1.0 - zcosil * zcosil); 1065 | var zsinhl = 0.089683511 * stem / zsinil; 1066 | var zcoshl = Math.sqrt(1.0 - zsinhl * zsinhl); 1067 | var gam = 5.8351514 + 0.0019443680 * day; 1068 | var zx = 0.39785416 * stem / zsinil; 1069 | var zy = zcoshl * ctem + 0.91744867 * zsinhl * stem; 1070 | zx = Math.atan2(zx, zy); 1071 | zx += gam - xnodce; 1072 | var zcosgl = Math.cos(zx); 1073 | var zsingl = Math.sin(zx); // ------------------------- do solar terms --------------------- 1074 | 1075 | zcosg = zcosgs; 1076 | zsing = zsings; 1077 | zcosi = zcosis; 1078 | zsini = zsinis; 1079 | zcosh = cnodm; 1080 | zsinh = snodm; 1081 | cc = c1ss; 1082 | var xnoi = 1.0 / nm; 1083 | var lsflg = 0; 1084 | 1085 | while (lsflg < 2) { 1086 | lsflg += 1; 1087 | a1 = zcosg * zcosh + zsing * zcosi * zsinh; 1088 | a3 = -zsing * zcosh + zcosg * zcosi * zsinh; 1089 | a7 = -zcosg * zsinh + zsing * zcosi * zcosh; 1090 | a8 = zsing * zsini; 1091 | a9 = zsing * zsinh + zcosg * zcosi * zcosh; 1092 | a10 = zcosg * zsini; 1093 | a2 = cosim * a7 + sinim * a8; 1094 | a4 = cosim * a9 + sinim * a10; 1095 | a5 = -sinim * a7 + cosim * a8; 1096 | a6 = -sinim * a9 + cosim * a10; 1097 | x1 = a1 * cosomm + a2 * sinomm; 1098 | x2 = a3 * cosomm + a4 * sinomm; 1099 | x3 = -a1 * sinomm + a2 * cosomm; 1100 | x4 = -a3 * sinomm + a4 * cosomm; 1101 | x5 = a5 * sinomm; 1102 | x6 = a6 * sinomm; 1103 | x7 = a5 * cosomm; 1104 | x8 = a6 * cosomm; 1105 | z31 = 12.0 * x1 * x1 - 3.0 * x3 * x3; 1106 | z32 = 24.0 * x1 * x2 - 6.0 * x3 * x4; 1107 | z33 = 12.0 * x2 * x2 - 3.0 * x4 * x4; 1108 | z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * emsq; 1109 | z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * emsq; 1110 | z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * emsq; 1111 | z11 = -6.0 * a1 * a5 + emsq * (-24.0 * x1 * x7 - 6.0 * x3 * x5); 1112 | z12 = -6.0 * (a1 * a6 + a3 * a5) + emsq * (-24.0 * (x2 * x7 + x1 * x8) + -6.0 * (x3 * x6 + x4 * x5)); 1113 | z13 = -6.0 * a3 * a6 + emsq * (-24.0 * x2 * x8 - 6.0 * x4 * x6); 1114 | z21 = 6.0 * a2 * a5 + emsq * (24.0 * x1 * x5 - 6.0 * x3 * x7); 1115 | z22 = 6.0 * (a4 * a5 + a2 * a6) + emsq * (24.0 * (x2 * x5 + x1 * x6) - 6.0 * (x4 * x7 + x3 * x8)); 1116 | z23 = 6.0 * a4 * a6 + emsq * (24.0 * x2 * x6 - 6.0 * x4 * x8); 1117 | z1 = z1 + z1 + betasq * z31; 1118 | z2 = z2 + z2 + betasq * z32; 1119 | z3 = z3 + z3 + betasq * z33; 1120 | s3 = cc * xnoi; 1121 | s2 = -0.5 * s3 / rtemsq; 1122 | s4 = s3 * rtemsq; 1123 | s1 = -15.0 * em * s4; 1124 | s5 = x1 * x3 + x2 * x4; 1125 | s6 = x2 * x3 + x1 * x4; 1126 | s7 = x2 * x4 - x1 * x3; // ----------------------- do lunar terms ------------------- 1127 | 1128 | if (lsflg === 1) { 1129 | ss1 = s1; 1130 | ss2 = s2; 1131 | ss3 = s3; 1132 | ss4 = s4; 1133 | ss5 = s5; 1134 | ss6 = s6; 1135 | ss7 = s7; 1136 | sz1 = z1; 1137 | sz2 = z2; 1138 | sz3 = z3; 1139 | sz11 = z11; 1140 | sz12 = z12; 1141 | sz13 = z13; 1142 | sz21 = z21; 1143 | sz22 = z22; 1144 | sz23 = z23; 1145 | sz31 = z31; 1146 | sz32 = z32; 1147 | sz33 = z33; 1148 | zcosg = zcosgl; 1149 | zsing = zsingl; 1150 | zcosi = zcosil; 1151 | zsini = zsinil; 1152 | zcosh = zcoshl * cnodm + zsinhl * snodm; 1153 | zsinh = snodm * zcoshl - cnodm * zsinhl; 1154 | cc = c1l; 1155 | } 1156 | } 1157 | 1158 | var zmol = (4.7199672 + (0.22997150 * day - gam)) % _constants.twoPi; 1159 | var zmos = (6.2565837 + 0.017201977 * day) % _constants.twoPi; // ------------------------ do solar terms ---------------------- 1160 | 1161 | var se2 = 2.0 * ss1 * ss6; 1162 | var se3 = 2.0 * ss1 * ss7; 1163 | var si2 = 2.0 * ss2 * sz12; 1164 | var si3 = 2.0 * ss2 * (sz13 - sz11); 1165 | var sl2 = -2.0 * ss3 * sz2; 1166 | var sl3 = -2.0 * ss3 * (sz3 - sz1); 1167 | var sl4 = -2.0 * ss3 * (-21.0 - 9.0 * emsq) * zes; 1168 | var sgh2 = 2.0 * ss4 * sz32; 1169 | var sgh3 = 2.0 * ss4 * (sz33 - sz31); 1170 | var sgh4 = -18.0 * ss4 * zes; 1171 | var sh2 = -2.0 * ss2 * sz22; 1172 | var sh3 = -2.0 * ss2 * (sz23 - sz21); // ------------------------ do lunar terms ---------------------- 1173 | 1174 | var ee2 = 2.0 * s1 * s6; 1175 | var e3 = 2.0 * s1 * s7; 1176 | var xi2 = 2.0 * s2 * z12; 1177 | var xi3 = 2.0 * s2 * (z13 - z11); 1178 | var xl2 = -2.0 * s3 * z2; 1179 | var xl3 = -2.0 * s3 * (z3 - z1); 1180 | var xl4 = -2.0 * s3 * (-21.0 - 9.0 * emsq) * zel; 1181 | var xgh2 = 2.0 * s4 * z32; 1182 | var xgh3 = 2.0 * s4 * (z33 - z31); 1183 | var xgh4 = -18.0 * s4 * zel; 1184 | var xh2 = -2.0 * s2 * z22; 1185 | var xh3 = -2.0 * s2 * (z23 - z21); 1186 | return { 1187 | snodm: snodm, 1188 | cnodm: cnodm, 1189 | sinim: sinim, 1190 | cosim: cosim, 1191 | sinomm: sinomm, 1192 | cosomm: cosomm, 1193 | day: day, 1194 | e3: e3, 1195 | ee2: ee2, 1196 | em: em, 1197 | emsq: emsq, 1198 | gam: gam, 1199 | peo: peo, 1200 | pgho: pgho, 1201 | pho: pho, 1202 | pinco: pinco, 1203 | plo: plo, 1204 | rtemsq: rtemsq, 1205 | se2: se2, 1206 | se3: se3, 1207 | sgh2: sgh2, 1208 | sgh3: sgh3, 1209 | sgh4: sgh4, 1210 | sh2: sh2, 1211 | sh3: sh3, 1212 | si2: si2, 1213 | si3: si3, 1214 | sl2: sl2, 1215 | sl3: sl3, 1216 | sl4: sl4, 1217 | s1: s1, 1218 | s2: s2, 1219 | s3: s3, 1220 | s4: s4, 1221 | s5: s5, 1222 | s6: s6, 1223 | s7: s7, 1224 | ss1: ss1, 1225 | ss2: ss2, 1226 | ss3: ss3, 1227 | ss4: ss4, 1228 | ss5: ss5, 1229 | ss6: ss6, 1230 | ss7: ss7, 1231 | sz1: sz1, 1232 | sz2: sz2, 1233 | sz3: sz3, 1234 | sz11: sz11, 1235 | sz12: sz12, 1236 | sz13: sz13, 1237 | sz21: sz21, 1238 | sz22: sz22, 1239 | sz23: sz23, 1240 | sz31: sz31, 1241 | sz32: sz32, 1242 | sz33: sz33, 1243 | xgh2: xgh2, 1244 | xgh3: xgh3, 1245 | xgh4: xgh4, 1246 | xh2: xh2, 1247 | xh3: xh3, 1248 | xi2: xi2, 1249 | xi3: xi3, 1250 | xl2: xl2, 1251 | xl3: xl3, 1252 | xl4: xl4, 1253 | nm: nm, 1254 | z1: z1, 1255 | z2: z2, 1256 | z3: z3, 1257 | z11: z11, 1258 | z12: z12, 1259 | z13: z13, 1260 | z21: z21, 1261 | z22: z22, 1262 | z23: z23, 1263 | z31: z31, 1264 | z32: z32, 1265 | z33: z33, 1266 | zmol: zmol, 1267 | zmos: zmos 1268 | }; 1269 | } 1270 | },{"../constants":2}],10:[function(require,module,exports){ 1271 | /*! 1272 | * satellite-js v3.0.1 1273 | * (c) 2013 Shashwat Kandadai and UCSC 1274 | * https://github.com/shashwatak/satellite-js 1275 | * License: MIT 1276 | */ 1277 | 1278 | "use strict"; 1279 | 1280 | Object.defineProperty(exports, "__esModule", { 1281 | value: true 1282 | }); 1283 | exports.default = dsinit; 1284 | 1285 | var _constants = require("../constants"); 1286 | 1287 | /*----------------------------------------------------------------------------- 1288 | * 1289 | * procedure dsinit 1290 | * 1291 | * this procedure provides deep space contributions to mean motion dot due 1292 | * to geopotential resonance with half day and one day orbits. 1293 | * 1294 | * author : david vallado 719-573-2600 28 jun 2005 1295 | * 1296 | * inputs : 1297 | * cosim, sinim- 1298 | * emsq - eccentricity squared 1299 | * argpo - argument of perigee 1300 | * s1, s2, s3, s4, s5 - 1301 | * ss1, ss2, ss3, ss4, ss5 - 1302 | * sz1, sz3, sz11, sz13, sz21, sz23, sz31, sz33 - 1303 | * t - time 1304 | * tc - 1305 | * gsto - greenwich sidereal time rad 1306 | * mo - mean anomaly 1307 | * mdot - mean anomaly dot (rate) 1308 | * no - mean motion 1309 | * nodeo - right ascension of ascending node 1310 | * nodedot - right ascension of ascending node dot (rate) 1311 | * xpidot - 1312 | * z1, z3, z11, z13, z21, z23, z31, z33 - 1313 | * eccm - eccentricity 1314 | * argpm - argument of perigee 1315 | * inclm - inclination 1316 | * mm - mean anomaly 1317 | * xn - mean motion 1318 | * nodem - right ascension of ascending node 1319 | * 1320 | * outputs : 1321 | * em - eccentricity 1322 | * argpm - argument of perigee 1323 | * inclm - inclination 1324 | * mm - mean anomaly 1325 | * nm - mean motion 1326 | * nodem - right ascension of ascending node 1327 | * irez - flag for resonance 0-none, 1-one day, 2-half day 1328 | * atime - 1329 | * d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433 - 1330 | * dedt - 1331 | * didt - 1332 | * dmdt - 1333 | * dndt - 1334 | * dnodt - 1335 | * domdt - 1336 | * del1, del2, del3 - 1337 | * ses , sghl , sghs , sgs , shl , shs , sis , sls 1338 | * theta - 1339 | * xfact - 1340 | * xlamo - 1341 | * xli - 1342 | * xni 1343 | * 1344 | * locals : 1345 | * ainv2 - 1346 | * aonv - 1347 | * cosisq - 1348 | * eoc - 1349 | * f220, f221, f311, f321, f322, f330, f441, f442, f522, f523, f542, f543 - 1350 | * g200, g201, g211, g300, g310, g322, g410, g422, g520, g521, g532, g533 - 1351 | * sini2 - 1352 | * temp - 1353 | * temp1 - 1354 | * theta - 1355 | * xno2 - 1356 | * 1357 | * coupling : 1358 | * getgravconst 1359 | * 1360 | * references : 1361 | * hoots, roehrich, norad spacetrack report #3 1980 1362 | * hoots, norad spacetrack report #6 1986 1363 | * hoots, schumacher and glover 2004 1364 | * vallado, crawford, hujsak, kelso 2006 1365 | ----------------------------------------------------------------------------*/ 1366 | function dsinit(options) { 1367 | var cosim = options.cosim, 1368 | argpo = options.argpo, 1369 | s1 = options.s1, 1370 | s2 = options.s2, 1371 | s3 = options.s3, 1372 | s4 = options.s4, 1373 | s5 = options.s5, 1374 | sinim = options.sinim, 1375 | ss1 = options.ss1, 1376 | ss2 = options.ss2, 1377 | ss3 = options.ss3, 1378 | ss4 = options.ss4, 1379 | ss5 = options.ss5, 1380 | sz1 = options.sz1, 1381 | sz3 = options.sz3, 1382 | sz11 = options.sz11, 1383 | sz13 = options.sz13, 1384 | sz21 = options.sz21, 1385 | sz23 = options.sz23, 1386 | sz31 = options.sz31, 1387 | sz33 = options.sz33, 1388 | t = options.t, 1389 | tc = options.tc, 1390 | gsto = options.gsto, 1391 | mo = options.mo, 1392 | mdot = options.mdot, 1393 | no = options.no, 1394 | nodeo = options.nodeo, 1395 | nodedot = options.nodedot, 1396 | xpidot = options.xpidot, 1397 | z1 = options.z1, 1398 | z3 = options.z3, 1399 | z11 = options.z11, 1400 | z13 = options.z13, 1401 | z21 = options.z21, 1402 | z23 = options.z23, 1403 | z31 = options.z31, 1404 | z33 = options.z33, 1405 | ecco = options.ecco, 1406 | eccsq = options.eccsq; 1407 | var emsq = options.emsq, 1408 | em = options.em, 1409 | argpm = options.argpm, 1410 | inclm = options.inclm, 1411 | mm = options.mm, 1412 | nm = options.nm, 1413 | nodem = options.nodem, 1414 | irez = options.irez, 1415 | atime = options.atime, 1416 | d2201 = options.d2201, 1417 | d2211 = options.d2211, 1418 | d3210 = options.d3210, 1419 | d3222 = options.d3222, 1420 | d4410 = options.d4410, 1421 | d4422 = options.d4422, 1422 | d5220 = options.d5220, 1423 | d5232 = options.d5232, 1424 | d5421 = options.d5421, 1425 | d5433 = options.d5433, 1426 | dedt = options.dedt, 1427 | didt = options.didt, 1428 | dmdt = options.dmdt, 1429 | dnodt = options.dnodt, 1430 | domdt = options.domdt, 1431 | del1 = options.del1, 1432 | del2 = options.del2, 1433 | del3 = options.del3, 1434 | xfact = options.xfact, 1435 | xlamo = options.xlamo, 1436 | xli = options.xli, 1437 | xni = options.xni; 1438 | var f220; 1439 | var f221; 1440 | var f311; 1441 | var f321; 1442 | var f322; 1443 | var f330; 1444 | var f441; 1445 | var f442; 1446 | var f522; 1447 | var f523; 1448 | var f542; 1449 | var f543; 1450 | var g200; 1451 | var g201; 1452 | var g211; 1453 | var g300; 1454 | var g310; 1455 | var g322; 1456 | var g410; 1457 | var g422; 1458 | var g520; 1459 | var g521; 1460 | var g532; 1461 | var g533; 1462 | var sini2; 1463 | var temp; 1464 | var temp1; 1465 | var xno2; 1466 | var ainv2; 1467 | var aonv; 1468 | var cosisq; 1469 | var eoc; 1470 | var q22 = 1.7891679e-6; 1471 | var q31 = 2.1460748e-6; 1472 | var q33 = 2.2123015e-7; 1473 | var root22 = 1.7891679e-6; 1474 | var root44 = 7.3636953e-9; 1475 | var root54 = 2.1765803e-9; 1476 | var rptim = 4.37526908801129966e-3; // equates to 7.29211514668855e-5 rad/sec 1477 | 1478 | var root32 = 3.7393792e-7; 1479 | var root52 = 1.1428639e-7; 1480 | var znl = 1.5835218e-4; 1481 | var zns = 1.19459e-5; // -------------------- deep space initialization ------------ 1482 | 1483 | irez = 0; 1484 | 1485 | if (nm < 0.0052359877 && nm > 0.0034906585) { 1486 | irez = 1; 1487 | } 1488 | 1489 | if (nm >= 8.26e-3 && nm <= 9.24e-3 && em >= 0.5) { 1490 | irez = 2; 1491 | } // ------------------------ do solar terms ------------------- 1492 | 1493 | 1494 | var ses = ss1 * zns * ss5; 1495 | var sis = ss2 * zns * (sz11 + sz13); 1496 | var sls = -zns * ss3 * (sz1 + sz3 - 14.0 - 6.0 * emsq); 1497 | var sghs = ss4 * zns * (sz31 + sz33 - 6.0); 1498 | var shs = -zns * ss2 * (sz21 + sz23); // sgp4fix for 180 deg incl 1499 | 1500 | if (inclm < 5.2359877e-2 || inclm > _constants.pi - 5.2359877e-2) { 1501 | shs = 0.0; 1502 | } 1503 | 1504 | if (sinim !== 0.0) { 1505 | shs /= sinim; 1506 | } 1507 | 1508 | var sgs = sghs - cosim * shs; // ------------------------- do lunar terms ------------------ 1509 | 1510 | dedt = ses + s1 * znl * s5; 1511 | didt = sis + s2 * znl * (z11 + z13); 1512 | dmdt = sls - znl * s3 * (z1 + z3 - 14.0 - 6.0 * emsq); 1513 | var sghl = s4 * znl * (z31 + z33 - 6.0); 1514 | var shll = -znl * s2 * (z21 + z23); // sgp4fix for 180 deg incl 1515 | 1516 | if (inclm < 5.2359877e-2 || inclm > _constants.pi - 5.2359877e-2) { 1517 | shll = 0.0; 1518 | } 1519 | 1520 | domdt = sgs + sghl; 1521 | dnodt = shs; 1522 | 1523 | if (sinim !== 0.0) { 1524 | domdt -= cosim / sinim * shll; 1525 | dnodt += shll / sinim; 1526 | } // ----------- calculate deep space resonance effects -------- 1527 | 1528 | 1529 | var dndt = 0.0; 1530 | var theta = (gsto + tc * rptim) % _constants.twoPi; 1531 | em += dedt * t; 1532 | inclm += didt * t; 1533 | argpm += domdt * t; 1534 | nodem += dnodt * t; 1535 | mm += dmdt * t; // sgp4fix for negative inclinations 1536 | // the following if statement should be commented out 1537 | // if (inclm < 0.0) 1538 | // { 1539 | // inclm = -inclm; 1540 | // argpm = argpm - pi; 1541 | // nodem = nodem + pi; 1542 | // } 1543 | // -------------- initialize the resonance terms ------------- 1544 | 1545 | if (irez !== 0) { 1546 | aonv = Math.pow(nm / _constants.xke, _constants.x2o3); // ---------- geopotential resonance for 12 hour orbits ------ 1547 | 1548 | if (irez === 2) { 1549 | cosisq = cosim * cosim; 1550 | var emo = em; 1551 | em = ecco; 1552 | var emsqo = emsq; 1553 | emsq = eccsq; 1554 | eoc = em * emsq; 1555 | g201 = -0.306 - (em - 0.64) * 0.440; 1556 | 1557 | if (em <= 0.65) { 1558 | g211 = 3.616 - 13.2470 * em + 16.2900 * emsq; 1559 | g310 = -19.302 + 117.3900 * em - 228.4190 * emsq + 156.5910 * eoc; 1560 | g322 = -18.9068 + 109.7927 * em - 214.6334 * emsq + 146.5816 * eoc; 1561 | g410 = -41.122 + 242.6940 * em - 471.0940 * emsq + 313.9530 * eoc; 1562 | g422 = -146.407 + 841.8800 * em - 1629.014 * emsq + 1083.4350 * eoc; 1563 | g520 = -532.114 + 3017.977 * em - 5740.032 * emsq + 3708.2760 * eoc; 1564 | } else { 1565 | g211 = -72.099 + 331.819 * em - 508.738 * emsq + 266.724 * eoc; 1566 | g310 = -346.844 + 1582.851 * em - 2415.925 * emsq + 1246.113 * eoc; 1567 | g322 = -342.585 + 1554.908 * em - 2366.899 * emsq + 1215.972 * eoc; 1568 | g410 = -1052.797 + 4758.686 * em - 7193.992 * emsq + 3651.957 * eoc; 1569 | g422 = -3581.690 + 16178.110 * em - 24462.770 * emsq + 12422.520 * eoc; 1570 | 1571 | if (em > 0.715) { 1572 | g520 = -5149.66 + 29936.92 * em - 54087.36 * emsq + 31324.56 * eoc; 1573 | } else { 1574 | g520 = 1464.74 - 4664.75 * em + 3763.64 * emsq; 1575 | } 1576 | } 1577 | 1578 | if (em < 0.7) { 1579 | g533 = -919.22770 + 4988.6100 * em - 9064.7700 * emsq + 5542.21 * eoc; 1580 | g521 = -822.71072 + 4568.6173 * em - 8491.4146 * emsq + 5337.524 * eoc; 1581 | g532 = -853.66600 + 4690.2500 * em - 8624.7700 * emsq + 5341.4 * eoc; 1582 | } else { 1583 | g533 = -37995.780 + 161616.52 * em - 229838.20 * emsq + 109377.94 * eoc; 1584 | g521 = -51752.104 + 218913.95 * em - 309468.16 * emsq + 146349.42 * eoc; 1585 | g532 = -40023.880 + 170470.89 * em - 242699.48 * emsq + 115605.82 * eoc; 1586 | } 1587 | 1588 | sini2 = sinim * sinim; 1589 | f220 = 0.75 * (1.0 + 2.0 * cosim + cosisq); 1590 | f221 = 1.5 * sini2; 1591 | f321 = 1.875 * sinim * (1.0 - 2.0 * cosim - 3.0 * cosisq); 1592 | f322 = -1.875 * sinim * (1.0 + 2.0 * cosim - 3.0 * cosisq); 1593 | f441 = 35.0 * sini2 * f220; 1594 | f442 = 39.3750 * sini2 * sini2; 1595 | f522 = 9.84375 * sinim * (sini2 * (1.0 - 2.0 * cosim - 5.0 * cosisq) + 0.33333333 * (-2.0 + 4.0 * cosim + 6.0 * cosisq)); 1596 | f523 = sinim * (4.92187512 * sini2 * (-2.0 - 4.0 * cosim + 10.0 * cosisq) + 6.56250012 * (1.0 + 2.0 * cosim - 3.0 * cosisq)); 1597 | f542 = 29.53125 * sinim * (2.0 - 8.0 * cosim + cosisq * (-12.0 + 8.0 * cosim + 10.0 * cosisq)); 1598 | f543 = 29.53125 * sinim * (-2.0 - 8.0 * cosim + cosisq * (12.0 + 8.0 * cosim - 10.0 * cosisq)); 1599 | xno2 = nm * nm; 1600 | ainv2 = aonv * aonv; 1601 | temp1 = 3.0 * xno2 * ainv2; 1602 | temp = temp1 * root22; 1603 | d2201 = temp * f220 * g201; 1604 | d2211 = temp * f221 * g211; 1605 | temp1 *= aonv; 1606 | temp = temp1 * root32; 1607 | d3210 = temp * f321 * g310; 1608 | d3222 = temp * f322 * g322; 1609 | temp1 *= aonv; 1610 | temp = 2.0 * temp1 * root44; 1611 | d4410 = temp * f441 * g410; 1612 | d4422 = temp * f442 * g422; 1613 | temp1 *= aonv; 1614 | temp = temp1 * root52; 1615 | d5220 = temp * f522 * g520; 1616 | d5232 = temp * f523 * g532; 1617 | temp = 2.0 * temp1 * root54; 1618 | d5421 = temp * f542 * g521; 1619 | d5433 = temp * f543 * g533; 1620 | xlamo = (mo + nodeo + nodeo - (theta + theta)) % _constants.twoPi; 1621 | xfact = mdot + dmdt + 2.0 * (nodedot + dnodt - rptim) - no; 1622 | em = emo; 1623 | emsq = emsqo; 1624 | } // ---------------- synchronous resonance terms -------------- 1625 | 1626 | 1627 | if (irez === 1) { 1628 | g200 = 1.0 + emsq * (-2.5 + 0.8125 * emsq); 1629 | g310 = 1.0 + 2.0 * emsq; 1630 | g300 = 1.0 + emsq * (-6.0 + 6.60937 * emsq); 1631 | f220 = 0.75 * (1.0 + cosim) * (1.0 + cosim); 1632 | f311 = 0.9375 * sinim * sinim * (1.0 + 3.0 * cosim) - 0.75 * (1.0 + cosim); 1633 | f330 = 1.0 + cosim; 1634 | f330 *= 1.875 * f330 * f330; 1635 | del1 = 3.0 * nm * nm * aonv * aonv; 1636 | del2 = 2.0 * del1 * f220 * g200 * q22; 1637 | del3 = 3.0 * del1 * f330 * g300 * q33 * aonv; 1638 | del1 = del1 * f311 * g310 * q31 * aonv; 1639 | xlamo = (mo + nodeo + argpo - theta) % _constants.twoPi; 1640 | xfact = mdot + xpidot + dmdt + domdt + dnodt - (no + rptim); 1641 | } // ------------ for sgp4, initialize the integrator ---------- 1642 | 1643 | 1644 | xli = xlamo; 1645 | xni = no; 1646 | atime = 0.0; 1647 | nm = no + dndt; 1648 | } 1649 | 1650 | return { 1651 | em: em, 1652 | argpm: argpm, 1653 | inclm: inclm, 1654 | mm: mm, 1655 | nm: nm, 1656 | nodem: nodem, 1657 | irez: irez, 1658 | atime: atime, 1659 | d2201: d2201, 1660 | d2211: d2211, 1661 | d3210: d3210, 1662 | d3222: d3222, 1663 | d4410: d4410, 1664 | d4422: d4422, 1665 | d5220: d5220, 1666 | d5232: d5232, 1667 | d5421: d5421, 1668 | d5433: d5433, 1669 | dedt: dedt, 1670 | didt: didt, 1671 | dmdt: dmdt, 1672 | dndt: dndt, 1673 | dnodt: dnodt, 1674 | domdt: domdt, 1675 | del1: del1, 1676 | del2: del2, 1677 | del3: del3, 1678 | xfact: xfact, 1679 | xlamo: xlamo, 1680 | xli: xli, 1681 | xni: xni 1682 | }; 1683 | } 1684 | },{"../constants":2}],11:[function(require,module,exports){ 1685 | /*! 1686 | * satellite-js v3.0.1 1687 | * (c) 2013 Shashwat Kandadai and UCSC 1688 | * https://github.com/shashwatak/satellite-js 1689 | * License: MIT 1690 | */ 1691 | 1692 | "use strict"; 1693 | 1694 | Object.defineProperty(exports, "__esModule", { 1695 | value: true 1696 | }); 1697 | exports.default = dspace; 1698 | 1699 | var _constants = require("../constants"); 1700 | 1701 | /*----------------------------------------------------------------------------- 1702 | * 1703 | * procedure dspace 1704 | * 1705 | * this procedure provides deep space contributions to mean elements for 1706 | * perturbing third body. these effects have been averaged over one 1707 | * revolution of the sun and moon. for earth resonance effects, the 1708 | * effects have been averaged over no revolutions of the satellite. 1709 | * (mean motion) 1710 | * 1711 | * author : david vallado 719-573-2600 28 jun 2005 1712 | * 1713 | * inputs : 1714 | * d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433 - 1715 | * dedt - 1716 | * del1, del2, del3 - 1717 | * didt - 1718 | * dmdt - 1719 | * dnodt - 1720 | * domdt - 1721 | * irez - flag for resonance 0-none, 1-one day, 2-half day 1722 | * argpo - argument of perigee 1723 | * argpdot - argument of perigee dot (rate) 1724 | * t - time 1725 | * tc - 1726 | * gsto - gst 1727 | * xfact - 1728 | * xlamo - 1729 | * no - mean motion 1730 | * atime - 1731 | * em - eccentricity 1732 | * ft - 1733 | * argpm - argument of perigee 1734 | * inclm - inclination 1735 | * xli - 1736 | * mm - mean anomaly 1737 | * xni - mean motion 1738 | * nodem - right ascension of ascending node 1739 | * 1740 | * outputs : 1741 | * atime - 1742 | * em - eccentricity 1743 | * argpm - argument of perigee 1744 | * inclm - inclination 1745 | * xli - 1746 | * mm - mean anomaly 1747 | * xni - 1748 | * nodem - right ascension of ascending node 1749 | * dndt - 1750 | * nm - mean motion 1751 | * 1752 | * locals : 1753 | * delt - 1754 | * ft - 1755 | * theta - 1756 | * x2li - 1757 | * x2omi - 1758 | * xl - 1759 | * xldot - 1760 | * xnddt - 1761 | * xndt - 1762 | * xomi - 1763 | * 1764 | * coupling : 1765 | * none - 1766 | * 1767 | * references : 1768 | * hoots, roehrich, norad spacetrack report #3 1980 1769 | * hoots, norad spacetrack report #6 1986 1770 | * hoots, schumacher and glover 2004 1771 | * vallado, crawford, hujsak, kelso 2006 1772 | ----------------------------------------------------------------------------*/ 1773 | function dspace(options) { 1774 | var irez = options.irez, 1775 | d2201 = options.d2201, 1776 | d2211 = options.d2211, 1777 | d3210 = options.d3210, 1778 | d3222 = options.d3222, 1779 | d4410 = options.d4410, 1780 | d4422 = options.d4422, 1781 | d5220 = options.d5220, 1782 | d5232 = options.d5232, 1783 | d5421 = options.d5421, 1784 | d5433 = options.d5433, 1785 | dedt = options.dedt, 1786 | del1 = options.del1, 1787 | del2 = options.del2, 1788 | del3 = options.del3, 1789 | didt = options.didt, 1790 | dmdt = options.dmdt, 1791 | dnodt = options.dnodt, 1792 | domdt = options.domdt, 1793 | argpo = options.argpo, 1794 | argpdot = options.argpdot, 1795 | t = options.t, 1796 | tc = options.tc, 1797 | gsto = options.gsto, 1798 | xfact = options.xfact, 1799 | xlamo = options.xlamo, 1800 | no = options.no; 1801 | var atime = options.atime, 1802 | em = options.em, 1803 | argpm = options.argpm, 1804 | inclm = options.inclm, 1805 | xli = options.xli, 1806 | mm = options.mm, 1807 | xni = options.xni, 1808 | nodem = options.nodem, 1809 | nm = options.nm; 1810 | var fasx2 = 0.13130908; 1811 | var fasx4 = 2.8843198; 1812 | var fasx6 = 0.37448087; 1813 | var g22 = 5.7686396; 1814 | var g32 = 0.95240898; 1815 | var g44 = 1.8014998; 1816 | var g52 = 1.0508330; 1817 | var g54 = 4.4108898; 1818 | var rptim = 4.37526908801129966e-3; // equates to 7.29211514668855e-5 rad/sec 1819 | 1820 | var stepp = 720.0; 1821 | var stepn = -720.0; 1822 | var step2 = 259200.0; 1823 | var delt; 1824 | var x2li; 1825 | var x2omi; 1826 | var xl; 1827 | var xldot; 1828 | var xnddt; 1829 | var xndt; 1830 | var xomi; 1831 | var dndt = 0.0; 1832 | var ft = 0.0; // ----------- calculate deep space resonance effects ----------- 1833 | 1834 | var theta = (gsto + tc * rptim) % _constants.twoPi; 1835 | em += dedt * t; 1836 | inclm += didt * t; 1837 | argpm += domdt * t; 1838 | nodem += dnodt * t; 1839 | mm += dmdt * t; // sgp4fix for negative inclinations 1840 | // the following if statement should be commented out 1841 | // if (inclm < 0.0) 1842 | // { 1843 | // inclm = -inclm; 1844 | // argpm = argpm - pi; 1845 | // nodem = nodem + pi; 1846 | // } 1847 | 1848 | /* - update resonances : numerical (euler-maclaurin) integration - */ 1849 | 1850 | /* ------------------------- epoch restart ---------------------- */ 1851 | // sgp4fix for propagator problems 1852 | // the following integration works for negative time steps and periods 1853 | // the specific changes are unknown because the original code was so convoluted 1854 | // sgp4fix take out atime = 0.0 and fix for faster operation 1855 | 1856 | if (irez !== 0) { 1857 | // sgp4fix streamline check 1858 | if (atime === 0.0 || t * atime <= 0.0 || Math.abs(t) < Math.abs(atime)) { 1859 | atime = 0.0; 1860 | xni = no; 1861 | xli = xlamo; 1862 | } // sgp4fix move check outside loop 1863 | 1864 | 1865 | if (t > 0.0) { 1866 | delt = stepp; 1867 | } else { 1868 | delt = stepn; 1869 | } 1870 | 1871 | var iretn = 381; // added for do loop 1872 | 1873 | while (iretn === 381) { 1874 | // ------------------- dot terms calculated ------------- 1875 | // ----------- near - synchronous resonance terms ------- 1876 | if (irez !== 2) { 1877 | xndt = del1 * Math.sin(xli - fasx2) + del2 * Math.sin(2.0 * (xli - fasx4)) + del3 * Math.sin(3.0 * (xli - fasx6)); 1878 | xldot = xni + xfact; 1879 | xnddt = del1 * Math.cos(xli - fasx2) + 2.0 * del2 * Math.cos(2.0 * (xli - fasx4)) + 3.0 * del3 * Math.cos(3.0 * (xli - fasx6)); 1880 | xnddt *= xldot; 1881 | } else { 1882 | // --------- near - half-day resonance terms -------- 1883 | xomi = argpo + argpdot * atime; 1884 | x2omi = xomi + xomi; 1885 | x2li = xli + xli; 1886 | 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); 1887 | xldot = xni + xfact; 1888 | 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); 1889 | xnddt *= xldot; 1890 | } // ----------------------- integrator ------------------- 1891 | // sgp4fix move end checks to end of routine 1892 | 1893 | 1894 | if (Math.abs(t - atime) >= stepp) { 1895 | iretn = 381; 1896 | } else { 1897 | ft = t - atime; 1898 | iretn = 0; 1899 | } 1900 | 1901 | if (iretn === 381) { 1902 | xli += xldot * delt + xndt * step2; 1903 | xni += xndt * delt + xnddt * step2; 1904 | atime += delt; 1905 | } 1906 | } 1907 | 1908 | nm = xni + xndt * ft + xnddt * ft * ft * 0.5; 1909 | xl = xli + xldot * ft + xndt * ft * ft * 0.5; 1910 | 1911 | if (irez !== 1) { 1912 | mm = xl - 2.0 * nodem + 2.0 * theta; 1913 | dndt = nm - no; 1914 | } else { 1915 | mm = xl - nodem - argpm + theta; 1916 | dndt = nm - no; 1917 | } 1918 | 1919 | nm = no + dndt; 1920 | } 1921 | 1922 | return { 1923 | atime: atime, 1924 | em: em, 1925 | argpm: argpm, 1926 | inclm: inclm, 1927 | xli: xli, 1928 | mm: mm, 1929 | xni: xni, 1930 | nodem: nodem, 1931 | dndt: dndt, 1932 | nm: nm 1933 | }; 1934 | } 1935 | },{"../constants":2}],12:[function(require,module,exports){ 1936 | /*! 1937 | * satellite-js v3.0.1 1938 | * (c) 2013 Shashwat Kandadai and UCSC 1939 | * https://github.com/shashwatak/satellite-js 1940 | * License: MIT 1941 | */ 1942 | 1943 | "use strict"; 1944 | 1945 | Object.defineProperty(exports, "__esModule", { 1946 | value: true 1947 | }); 1948 | exports.default = gstime; 1949 | 1950 | var _constants = require("../constants"); 1951 | 1952 | var _ext = require("../ext"); 1953 | 1954 | /* ----------------------------------------------------------------------------- 1955 | * 1956 | * function gstime 1957 | * 1958 | * this function finds the greenwich sidereal time. 1959 | * 1960 | * author : david vallado 719-573-2600 1 mar 2001 1961 | * 1962 | * inputs description range / units 1963 | * jdut1 - julian date in ut1 days from 4713 bc 1964 | * 1965 | * outputs : 1966 | * gstime - greenwich sidereal time 0 to 2pi rad 1967 | * 1968 | * locals : 1969 | * temp - temporary variable for doubles rad 1970 | * tut1 - julian centuries from the 1971 | * jan 1, 2000 12 h epoch (ut1) 1972 | * 1973 | * coupling : 1974 | * none 1975 | * 1976 | * references : 1977 | * vallado 2004, 191, eq 3-45 1978 | * --------------------------------------------------------------------------- */ 1979 | function gstimeInternal(jdut1) { 1980 | var tut1 = (jdut1 - 2451545.0) / 36525.0; 1981 | var temp = -6.2e-6 * tut1 * tut1 * tut1 + 0.093104 * tut1 * tut1 + (876600.0 * 3600 + 8640184.812866) * tut1 + 67310.54841; // # sec 1982 | 1983 | temp = temp * _constants.deg2rad / 240.0 % _constants.twoPi; // 360/86400 = 1/240, to deg, to rad 1984 | // ------------------------ check quadrants --------------------- 1985 | 1986 | if (temp < 0.0) { 1987 | temp += _constants.twoPi; 1988 | } 1989 | 1990 | return temp; 1991 | } 1992 | 1993 | function gstime() { 1994 | if ((arguments.length <= 0 ? undefined : arguments[0]) instanceof Date || arguments.length > 1) { 1995 | return gstimeInternal(_ext.jday.apply(void 0, arguments)); 1996 | } 1997 | 1998 | return gstimeInternal.apply(void 0, arguments); 1999 | } 2000 | },{"../constants":2,"../ext":4}],13:[function(require,module,exports){ 2001 | /*! 2002 | * satellite-js v3.0.1 2003 | * (c) 2013 Shashwat Kandadai and UCSC 2004 | * https://github.com/shashwatak/satellite-js 2005 | * License: MIT 2006 | */ 2007 | 2008 | "use strict"; 2009 | 2010 | Object.defineProperty(exports, "__esModule", { 2011 | value: true 2012 | }); 2013 | exports.default = initl; 2014 | 2015 | var _constants = require("../constants"); 2016 | 2017 | var _gstime = _interopRequireDefault(require("./gstime")); 2018 | 2019 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2020 | 2021 | /*----------------------------------------------------------------------------- 2022 | * 2023 | * procedure initl 2024 | * 2025 | * this procedure initializes the sgp4 propagator. all the initialization is 2026 | * consolidated here instead of having multiple loops inside other routines. 2027 | * 2028 | * author : david vallado 719-573-2600 28 jun 2005 2029 | * 2030 | * inputs : 2031 | * ecco - eccentricity 0.0 - 1.0 2032 | * epoch - epoch time in days from jan 0, 1950. 0 hr 2033 | * inclo - inclination of satellite 2034 | * no - mean motion of satellite 2035 | * satn - satellite number 2036 | * 2037 | * outputs : 2038 | * ainv - 1.0 / a 2039 | * ao - semi major axis 2040 | * con41 - 2041 | * con42 - 1.0 - 5.0 cos(i) 2042 | * cosio - cosine of inclination 2043 | * cosio2 - cosio squared 2044 | * eccsq - eccentricity squared 2045 | * method - flag for deep space 'd', 'n' 2046 | * omeosq - 1.0 - ecco * ecco 2047 | * posq - semi-parameter squared 2048 | * rp - radius of perigee 2049 | * rteosq - square root of (1.0 - ecco*ecco) 2050 | * sinio - sine of inclination 2051 | * gsto - gst at time of observation rad 2052 | * no - mean motion of satellite 2053 | * 2054 | * locals : 2055 | * ak - 2056 | * d1 - 2057 | * del - 2058 | * adel - 2059 | * po - 2060 | * 2061 | * coupling : 2062 | * getgravconst 2063 | * gstime - find greenwich sidereal time from the julian date 2064 | * 2065 | * references : 2066 | * hoots, roehrich, norad spacetrack report #3 1980 2067 | * hoots, norad spacetrack report #6 1986 2068 | * hoots, schumacher and glover 2004 2069 | * vallado, crawford, hujsak, kelso 2006 2070 | ----------------------------------------------------------------------------*/ 2071 | function initl(options) { 2072 | var ecco = options.ecco, 2073 | epoch = options.epoch, 2074 | inclo = options.inclo, 2075 | opsmode = options.opsmode; 2076 | var no = options.no; // sgp4fix use old way of finding gst 2077 | // ----------------------- earth constants --------------------- 2078 | // sgp4fix identify constants and allow alternate values 2079 | // ------------- calculate auxillary epoch quantities ---------- 2080 | 2081 | var eccsq = ecco * ecco; 2082 | var omeosq = 1.0 - eccsq; 2083 | var rteosq = Math.sqrt(omeosq); 2084 | var cosio = Math.cos(inclo); 2085 | var cosio2 = cosio * cosio; // ------------------ un-kozai the mean motion ----------------- 2086 | 2087 | var ak = Math.pow(_constants.xke / no, _constants.x2o3); 2088 | var d1 = 0.75 * _constants.j2 * (3.0 * cosio2 - 1.0) / (rteosq * omeosq); 2089 | var delPrime = d1 / (ak * ak); 2090 | var adel = ak * (1.0 - delPrime * delPrime - delPrime * (1.0 / 3.0 + 134.0 * delPrime * delPrime / 81.0)); 2091 | delPrime = d1 / (adel * adel); 2092 | no /= 1.0 + delPrime; 2093 | var ao = Math.pow(_constants.xke / no, _constants.x2o3); 2094 | var sinio = Math.sin(inclo); 2095 | var po = ao * omeosq; 2096 | var con42 = 1.0 - 5.0 * cosio2; 2097 | var con41 = -con42 - cosio2 - cosio2; 2098 | var ainv = 1.0 / ao; 2099 | var posq = po * po; 2100 | var rp = ao * (1.0 - ecco); 2101 | var method = 'n'; // sgp4fix modern approach to finding sidereal time 2102 | 2103 | var gsto; 2104 | 2105 | if (opsmode === 'a') { 2106 | // sgp4fix use old way of finding gst 2107 | // count integer number of days from 0 jan 1970 2108 | var ts70 = epoch - 7305.0; 2109 | var ds70 = Math.floor(ts70 + 1.0e-8); 2110 | var tfrac = ts70 - ds70; // find greenwich location at epoch 2111 | 2112 | var c1 = 1.72027916940703639e-2; 2113 | var thgr70 = 1.7321343856509374; 2114 | var fk5r = 5.07551419432269442e-15; 2115 | var c1p2p = c1 + _constants.twoPi; 2116 | gsto = (thgr70 + c1 * ds70 + c1p2p * tfrac + ts70 * ts70 * fk5r) % _constants.twoPi; 2117 | 2118 | if (gsto < 0.0) { 2119 | gsto += _constants.twoPi; 2120 | } 2121 | } else { 2122 | gsto = (0, _gstime.default)(epoch + 2433281.5); 2123 | } 2124 | 2125 | return { 2126 | no: no, 2127 | method: method, 2128 | ainv: ainv, 2129 | ao: ao, 2130 | con41: con41, 2131 | con42: con42, 2132 | cosio: cosio, 2133 | cosio2: cosio2, 2134 | eccsq: eccsq, 2135 | omeosq: omeosq, 2136 | posq: posq, 2137 | rp: rp, 2138 | rteosq: rteosq, 2139 | sinio: sinio, 2140 | gsto: gsto 2141 | }; 2142 | } 2143 | },{"../constants":2,"./gstime":12}],14:[function(require,module,exports){ 2144 | /*! 2145 | * satellite-js v3.0.1 2146 | * (c) 2013 Shashwat Kandadai and UCSC 2147 | * https://github.com/shashwatak/satellite-js 2148 | * License: MIT 2149 | */ 2150 | 2151 | "use strict"; 2152 | 2153 | Object.defineProperty(exports, "__esModule", { 2154 | value: true 2155 | }); 2156 | exports.default = propagate; 2157 | 2158 | var _constants = require("../constants"); 2159 | 2160 | var _ext = require("../ext"); 2161 | 2162 | var _sgp = _interopRequireDefault(require("./sgp4")); 2163 | 2164 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2165 | 2166 | function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } 2167 | 2168 | function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } 2169 | 2170 | function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } 2171 | 2172 | 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; } } 2173 | 2174 | function propagate() { 2175 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 2176 | args[_key] = arguments[_key]; 2177 | } 2178 | 2179 | // Return a position and velocity vector for a given date and time. 2180 | var satrec = args[0]; 2181 | var date = Array.prototype.slice.call(args, 1); 2182 | 2183 | var j = _ext.jday.apply(void 0, _toConsumableArray(date)); 2184 | 2185 | var m = (j - satrec.jdsatepoch) * _constants.minutesPerDay; 2186 | return (0, _sgp.default)(satrec, m); 2187 | } 2188 | },{"../constants":2,"../ext":4,"./sgp4":15}],15:[function(require,module,exports){ 2189 | /*! 2190 | * satellite-js v3.0.1 2191 | * (c) 2013 Shashwat Kandadai and UCSC 2192 | * https://github.com/shashwatak/satellite-js 2193 | * License: MIT 2194 | */ 2195 | 2196 | "use strict"; 2197 | 2198 | Object.defineProperty(exports, "__esModule", { 2199 | value: true 2200 | }); 2201 | exports.default = sgp4; 2202 | 2203 | var _constants = require("../constants"); 2204 | 2205 | var _dpper = _interopRequireDefault(require("./dpper")); 2206 | 2207 | var _dspace = _interopRequireDefault(require("./dspace")); 2208 | 2209 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2210 | 2211 | /*---------------------------------------------------------------------------- 2212 | * 2213 | * procedure sgp4 2214 | * 2215 | * this procedure is the sgp4 prediction model from space command. this is an 2216 | * updated and combined version of sgp4 and sdp4, which were originally 2217 | * published separately in spacetrack report //3. this version follows the 2218 | * methodology from the aiaa paper (2006) describing the history and 2219 | * development of the code. 2220 | * 2221 | * author : david vallado 719-573-2600 28 jun 2005 2222 | * 2223 | * inputs : 2224 | * satrec - initialised structure from sgp4init() call. 2225 | * tsince - time since epoch (minutes) 2226 | * 2227 | * outputs : 2228 | * r - position vector km 2229 | * v - velocity km/sec 2230 | * return code - non-zero on error. 2231 | * 1 - mean elements, ecc >= 1.0 or ecc < -0.001 or a < 0.95 er 2232 | * 2 - mean motion less than 0.0 2233 | * 3 - pert elements, ecc < 0.0 or ecc > 1.0 2234 | * 4 - semi-latus rectum < 0.0 2235 | * 5 - epoch elements are sub-orbital 2236 | * 6 - satellite has decayed 2237 | * 2238 | * locals : 2239 | * am - 2240 | * axnl, aynl - 2241 | * betal - 2242 | * cosim , sinim , cosomm , sinomm , cnod , snod , cos2u , 2243 | * sin2u , coseo1 , sineo1 , cosi , sini , cosip , sinip , 2244 | * cosisq , cossu , sinsu , cosu , sinu 2245 | * delm - 2246 | * delomg - 2247 | * dndt - 2248 | * eccm - 2249 | * emsq - 2250 | * ecose - 2251 | * el2 - 2252 | * eo1 - 2253 | * eccp - 2254 | * esine - 2255 | * argpm - 2256 | * argpp - 2257 | * omgadf - 2258 | * pl - 2259 | * r - 2260 | * rtemsq - 2261 | * rdotl - 2262 | * rl - 2263 | * rvdot - 2264 | * rvdotl - 2265 | * su - 2266 | * t2 , t3 , t4 , tc 2267 | * tem5, temp , temp1 , temp2 , tempa , tempe , templ 2268 | * u , ux , uy , uz , vx , vy , vz 2269 | * inclm - inclination 2270 | * mm - mean anomaly 2271 | * nm - mean motion 2272 | * nodem - right asc of ascending node 2273 | * xinc - 2274 | * xincp - 2275 | * xl - 2276 | * xlm - 2277 | * mp - 2278 | * xmdf - 2279 | * xmx - 2280 | * xmy - 2281 | * nodedf - 2282 | * xnode - 2283 | * nodep - 2284 | * np - 2285 | * 2286 | * coupling : 2287 | * getgravconst- 2288 | * dpper 2289 | * dspace 2290 | * 2291 | * references : 2292 | * hoots, roehrich, norad spacetrack report //3 1980 2293 | * hoots, norad spacetrack report //6 1986 2294 | * hoots, schumacher and glover 2004 2295 | * vallado, crawford, hujsak, kelso 2006 2296 | ----------------------------------------------------------------------------*/ 2297 | function sgp4(satrec, tsince) { 2298 | /* eslint-disable no-param-reassign */ 2299 | var coseo1; 2300 | var sineo1; 2301 | var cosip; 2302 | var sinip; 2303 | var cosisq; 2304 | var delm; 2305 | var delomg; 2306 | var eo1; 2307 | var argpm; 2308 | var argpp; 2309 | var su; 2310 | var t3; 2311 | var t4; 2312 | var tc; 2313 | var tem5; 2314 | var temp; 2315 | var tempa; 2316 | var tempe; 2317 | var templ; 2318 | var inclm; 2319 | var mm; 2320 | var nm; 2321 | var nodem; 2322 | var xincp; 2323 | var xlm; 2324 | var mp; 2325 | var nodep; 2326 | /* ------------------ set mathematical constants --------------- */ 2327 | // sgp4fix divisor for divide by zero check on inclination 2328 | // the old check used 1.0 + cos(pi-1.0e-9), but then compared it to 2329 | // 1.5 e-12, so the threshold was changed to 1.5e-12 for consistency 2330 | 2331 | var temp4 = 1.5e-12; // --------------------- clear sgp4 error flag ----------------- 2332 | 2333 | satrec.t = tsince; 2334 | satrec.error = 0; // ------- update for secular gravity and atmospheric drag ----- 2335 | 2336 | var xmdf = satrec.mo + satrec.mdot * satrec.t; 2337 | var argpdf = satrec.argpo + satrec.argpdot * satrec.t; 2338 | var nodedf = satrec.nodeo + satrec.nodedot * satrec.t; 2339 | argpm = argpdf; 2340 | mm = xmdf; 2341 | var t2 = satrec.t * satrec.t; 2342 | nodem = nodedf + satrec.nodecf * t2; 2343 | tempa = 1.0 - satrec.cc1 * satrec.t; 2344 | tempe = satrec.bstar * satrec.cc4 * satrec.t; 2345 | templ = satrec.t2cof * t2; 2346 | 2347 | if (satrec.isimp !== 1) { 2348 | delomg = satrec.omgcof * satrec.t; // sgp4fix use mutliply for speed instead of pow 2349 | 2350 | var delmtemp = 1.0 + satrec.eta * Math.cos(xmdf); 2351 | delm = satrec.xmcof * (delmtemp * delmtemp * delmtemp - satrec.delmo); 2352 | temp = delomg + delm; 2353 | mm = xmdf + temp; 2354 | argpm = argpdf - temp; 2355 | t3 = t2 * satrec.t; 2356 | t4 = t3 * satrec.t; 2357 | tempa = tempa - satrec.d2 * t2 - satrec.d3 * t3 - satrec.d4 * t4; 2358 | tempe += satrec.bstar * satrec.cc5 * (Math.sin(mm) - satrec.sinmao); 2359 | templ = templ + satrec.t3cof * t3 + t4 * (satrec.t4cof + satrec.t * satrec.t5cof); 2360 | } 2361 | 2362 | nm = satrec.no; 2363 | var em = satrec.ecco; 2364 | inclm = satrec.inclo; 2365 | 2366 | if (satrec.method === 'd') { 2367 | tc = satrec.t; 2368 | var dspaceOptions = { 2369 | irez: satrec.irez, 2370 | d2201: satrec.d2201, 2371 | d2211: satrec.d2211, 2372 | d3210: satrec.d3210, 2373 | d3222: satrec.d3222, 2374 | d4410: satrec.d4410, 2375 | d4422: satrec.d4422, 2376 | d5220: satrec.d5220, 2377 | d5232: satrec.d5232, 2378 | d5421: satrec.d5421, 2379 | d5433: satrec.d5433, 2380 | dedt: satrec.dedt, 2381 | del1: satrec.del1, 2382 | del2: satrec.del2, 2383 | del3: satrec.del3, 2384 | didt: satrec.didt, 2385 | dmdt: satrec.dmdt, 2386 | dnodt: satrec.dnodt, 2387 | domdt: satrec.domdt, 2388 | argpo: satrec.argpo, 2389 | argpdot: satrec.argpdot, 2390 | t: satrec.t, 2391 | tc: tc, 2392 | gsto: satrec.gsto, 2393 | xfact: satrec.xfact, 2394 | xlamo: satrec.xlamo, 2395 | no: satrec.no, 2396 | atime: satrec.atime, 2397 | em: em, 2398 | argpm: argpm, 2399 | inclm: inclm, 2400 | xli: satrec.xli, 2401 | mm: mm, 2402 | xni: satrec.xni, 2403 | nodem: nodem, 2404 | nm: nm 2405 | }; 2406 | var dspaceResult = (0, _dspace.default)(dspaceOptions); 2407 | em = dspaceResult.em; 2408 | argpm = dspaceResult.argpm; 2409 | inclm = dspaceResult.inclm; 2410 | mm = dspaceResult.mm; 2411 | nodem = dspaceResult.nodem; 2412 | nm = dspaceResult.nm; 2413 | } 2414 | 2415 | if (nm <= 0.0) { 2416 | // printf("// error nm %f\n", nm); 2417 | satrec.error = 2; // sgp4fix add return 2418 | 2419 | return [false, false]; 2420 | } 2421 | 2422 | var am = Math.pow(_constants.xke / nm, _constants.x2o3) * tempa * tempa; 2423 | nm = _constants.xke / Math.pow(am, 1.5); 2424 | em -= tempe; // fix tolerance for error recognition 2425 | // sgp4fix am is fixed from the previous nm check 2426 | 2427 | if (em >= 1.0 || em < -0.001) { 2428 | // || (am < 0.95) 2429 | // printf("// error em %f\n", em); 2430 | satrec.error = 1; // sgp4fix to return if there is an error in eccentricity 2431 | 2432 | return [false, false]; 2433 | } // sgp4fix fix tolerance to avoid a divide by zero 2434 | 2435 | 2436 | if (em < 1.0e-6) { 2437 | em = 1.0e-6; 2438 | } 2439 | 2440 | mm += satrec.no * templ; 2441 | xlm = mm + argpm + nodem; 2442 | nodem %= _constants.twoPi; 2443 | argpm %= _constants.twoPi; 2444 | xlm %= _constants.twoPi; 2445 | mm = (xlm - argpm - nodem) % _constants.twoPi; // ----------------- compute extra mean quantities ------------- 2446 | 2447 | var sinim = Math.sin(inclm); 2448 | var cosim = Math.cos(inclm); // -------------------- add lunar-solar periodics -------------- 2449 | 2450 | var ep = em; 2451 | xincp = inclm; 2452 | argpp = argpm; 2453 | nodep = nodem; 2454 | mp = mm; 2455 | sinip = sinim; 2456 | cosip = cosim; 2457 | 2458 | if (satrec.method === 'd') { 2459 | var dpperParameters = { 2460 | inclo: satrec.inclo, 2461 | init: 'n', 2462 | ep: ep, 2463 | inclp: xincp, 2464 | nodep: nodep, 2465 | argpp: argpp, 2466 | mp: mp, 2467 | opsmode: satrec.operationmode 2468 | }; 2469 | var dpperResult = (0, _dpper.default)(satrec, dpperParameters); 2470 | ep = dpperResult.ep; 2471 | nodep = dpperResult.nodep; 2472 | argpp = dpperResult.argpp; 2473 | mp = dpperResult.mp; 2474 | xincp = dpperResult.inclp; 2475 | 2476 | if (xincp < 0.0) { 2477 | xincp = -xincp; 2478 | nodep += _constants.pi; 2479 | argpp -= _constants.pi; 2480 | } 2481 | 2482 | if (ep < 0.0 || ep > 1.0) { 2483 | // printf("// error ep %f\n", ep); 2484 | satrec.error = 3; // sgp4fix add return 2485 | 2486 | return [false, false]; 2487 | } 2488 | } // -------------------- long period periodics ------------------ 2489 | 2490 | 2491 | if (satrec.method === 'd') { 2492 | sinip = Math.sin(xincp); 2493 | cosip = Math.cos(xincp); 2494 | satrec.aycof = -0.5 * _constants.j3oj2 * sinip; // sgp4fix for divide by zero for xincp = 180 deg 2495 | 2496 | if (Math.abs(cosip + 1.0) > 1.5e-12) { 2497 | satrec.xlcof = -0.25 * _constants.j3oj2 * sinip * (3.0 + 5.0 * cosip) / (1.0 + cosip); 2498 | } else { 2499 | satrec.xlcof = -0.25 * _constants.j3oj2 * sinip * (3.0 + 5.0 * cosip) / temp4; 2500 | } 2501 | } 2502 | 2503 | var axnl = ep * Math.cos(argpp); 2504 | temp = 1.0 / (am * (1.0 - ep * ep)); 2505 | var aynl = ep * Math.sin(argpp) + temp * satrec.aycof; 2506 | var xl = mp + argpp + nodep + temp * satrec.xlcof * axnl; // --------------------- solve kepler's equation --------------- 2507 | 2508 | var u = (xl - nodep) % _constants.twoPi; 2509 | eo1 = u; 2510 | tem5 = 9999.9; 2511 | var ktr = 1; // sgp4fix for kepler iteration 2512 | // the following iteration needs better limits on corrections 2513 | 2514 | while (Math.abs(tem5) >= 1.0e-12 && ktr <= 10) { 2515 | sineo1 = Math.sin(eo1); 2516 | coseo1 = Math.cos(eo1); 2517 | tem5 = 1.0 - coseo1 * axnl - sineo1 * aynl; 2518 | tem5 = (u - aynl * coseo1 + axnl * sineo1 - eo1) / tem5; 2519 | 2520 | if (Math.abs(tem5) >= 0.95) { 2521 | if (tem5 > 0.0) { 2522 | tem5 = 0.95; 2523 | } else { 2524 | tem5 = -0.95; 2525 | } 2526 | } 2527 | 2528 | eo1 += tem5; 2529 | ktr += 1; 2530 | } // ------------- short period preliminary quantities ----------- 2531 | 2532 | 2533 | var ecose = axnl * coseo1 + aynl * sineo1; 2534 | var esine = axnl * sineo1 - aynl * coseo1; 2535 | var el2 = axnl * axnl + aynl * aynl; 2536 | var pl = am * (1.0 - el2); 2537 | 2538 | if (pl < 0.0) { 2539 | // printf("// error pl %f\n", pl); 2540 | satrec.error = 4; // sgp4fix add return 2541 | 2542 | return [false, false]; 2543 | } 2544 | 2545 | var rl = am * (1.0 - ecose); 2546 | var rdotl = Math.sqrt(am) * esine / rl; 2547 | var rvdotl = Math.sqrt(pl) / rl; 2548 | var betal = Math.sqrt(1.0 - el2); 2549 | temp = esine / (1.0 + betal); 2550 | var sinu = am / rl * (sineo1 - aynl - axnl * temp); 2551 | var cosu = am / rl * (coseo1 - axnl + aynl * temp); 2552 | su = Math.atan2(sinu, cosu); 2553 | var sin2u = (cosu + cosu) * sinu; 2554 | var cos2u = 1.0 - 2.0 * sinu * sinu; 2555 | temp = 1.0 / pl; 2556 | var temp1 = 0.5 * _constants.j2 * temp; 2557 | var temp2 = temp1 * temp; // -------------- update for short period periodics ------------ 2558 | 2559 | if (satrec.method === 'd') { 2560 | cosisq = cosip * cosip; 2561 | satrec.con41 = 3.0 * cosisq - 1.0; 2562 | satrec.x1mth2 = 1.0 - cosisq; 2563 | satrec.x7thm1 = 7.0 * cosisq - 1.0; 2564 | } 2565 | 2566 | var mrt = rl * (1.0 - 1.5 * temp2 * betal * satrec.con41) + 0.5 * temp1 * satrec.x1mth2 * cos2u; // sgp4fix for decaying satellites 2567 | 2568 | if (mrt < 1.0) { 2569 | // printf("// decay condition %11.6f \n",mrt); 2570 | satrec.error = 6; 2571 | return { 2572 | position: false, 2573 | velocity: false 2574 | }; 2575 | } 2576 | 2577 | su -= 0.25 * temp2 * satrec.x7thm1 * sin2u; 2578 | var xnode = nodep + 1.5 * temp2 * cosip * sin2u; 2579 | var xinc = xincp + 1.5 * temp2 * cosip * sinip * cos2u; 2580 | var mvt = rdotl - nm * temp1 * satrec.x1mth2 * sin2u / _constants.xke; 2581 | var rvdot = rvdotl + nm * temp1 * (satrec.x1mth2 * cos2u + 1.5 * satrec.con41) / _constants.xke; // --------------------- orientation vectors ------------------- 2582 | 2583 | var sinsu = Math.sin(su); 2584 | var cossu = Math.cos(su); 2585 | var snod = Math.sin(xnode); 2586 | var cnod = Math.cos(xnode); 2587 | var sini = Math.sin(xinc); 2588 | var cosi = Math.cos(xinc); 2589 | var xmx = -snod * cosi; 2590 | var xmy = cnod * cosi; 2591 | var ux = xmx * sinsu + cnod * cossu; 2592 | var uy = xmy * sinsu + snod * cossu; 2593 | var uz = sini * sinsu; 2594 | var vx = xmx * cossu - cnod * sinsu; 2595 | var vy = xmy * cossu - snod * sinsu; 2596 | var vz = sini * cossu; // --------- position and velocity (in km and km/sec) ---------- 2597 | 2598 | var r = { 2599 | x: mrt * ux * _constants.earthRadius, 2600 | y: mrt * uy * _constants.earthRadius, 2601 | z: mrt * uz * _constants.earthRadius 2602 | }; 2603 | var v = { 2604 | x: (mvt * ux + rvdot * vx) * _constants.vkmpersec, 2605 | y: (mvt * uy + rvdot * vy) * _constants.vkmpersec, 2606 | z: (mvt * uz + rvdot * vz) * _constants.vkmpersec 2607 | }; 2608 | return { 2609 | position: r, 2610 | velocity: v 2611 | }; 2612 | /* eslint-enable no-param-reassign */ 2613 | } 2614 | },{"../constants":2,"./dpper":8,"./dspace":11}],16:[function(require,module,exports){ 2615 | /*! 2616 | * satellite-js v3.0.1 2617 | * (c) 2013 Shashwat Kandadai and UCSC 2618 | * https://github.com/shashwatak/satellite-js 2619 | * License: MIT 2620 | */ 2621 | 2622 | "use strict"; 2623 | 2624 | Object.defineProperty(exports, "__esModule", { 2625 | value: true 2626 | }); 2627 | exports.default = sgp4init; 2628 | 2629 | var _constants = require("../constants"); 2630 | 2631 | var _dpper = _interopRequireDefault(require("./dpper")); 2632 | 2633 | var _dscom = _interopRequireDefault(require("./dscom")); 2634 | 2635 | var _dsinit = _interopRequireDefault(require("./dsinit")); 2636 | 2637 | var _initl = _interopRequireDefault(require("./initl")); 2638 | 2639 | var _sgp = _interopRequireDefault(require("./sgp4")); 2640 | 2641 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2642 | 2643 | /*----------------------------------------------------------------------------- 2644 | * 2645 | * procedure sgp4init 2646 | * 2647 | * this procedure initializes variables for sgp4. 2648 | * 2649 | * author : david vallado 719-573-2600 28 jun 2005 2650 | * author : david vallado 719-573-2600 28 jun 2005 2651 | * 2652 | * inputs : 2653 | * opsmode - mode of operation afspc or improved 'a', 'i' 2654 | * satn - satellite number 2655 | * bstar - sgp4 type drag coefficient kg/m2er 2656 | * ecco - eccentricity 2657 | * epoch - epoch time in days from jan 0, 1950. 0 hr 2658 | * argpo - argument of perigee (output if ds) 2659 | * inclo - inclination 2660 | * mo - mean anomaly (output if ds) 2661 | * no - mean motion 2662 | * nodeo - right ascension of ascending node 2663 | * 2664 | * outputs : 2665 | * rec - common values for subsequent calls 2666 | * return code - non-zero on error. 2667 | * 1 - mean elements, ecc >= 1.0 or ecc < -0.001 or a < 0.95 er 2668 | * 2 - mean motion less than 0.0 2669 | * 3 - pert elements, ecc < 0.0 or ecc > 1.0 2670 | * 4 - semi-latus rectum < 0.0 2671 | * 5 - epoch elements are sub-orbital 2672 | * 6 - satellite has decayed 2673 | * 2674 | * locals : 2675 | * cnodm , snodm , cosim , sinim , cosomm , sinomm 2676 | * cc1sq , cc2 , cc3 2677 | * coef , coef1 2678 | * cosio4 - 2679 | * day - 2680 | * dndt - 2681 | * em - eccentricity 2682 | * emsq - eccentricity squared 2683 | * eeta - 2684 | * etasq - 2685 | * gam - 2686 | * argpm - argument of perigee 2687 | * nodem - 2688 | * inclm - inclination 2689 | * mm - mean anomaly 2690 | * nm - mean motion 2691 | * perige - perigee 2692 | * pinvsq - 2693 | * psisq - 2694 | * qzms24 - 2695 | * rtemsq - 2696 | * s1, s2, s3, s4, s5, s6, s7 - 2697 | * sfour - 2698 | * ss1, ss2, ss3, ss4, ss5, ss6, ss7 - 2699 | * sz1, sz2, sz3 2700 | * sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33 - 2701 | * tc - 2702 | * temp - 2703 | * temp1, temp2, temp3 - 2704 | * tsi - 2705 | * xpidot - 2706 | * xhdot1 - 2707 | * z1, z2, z3 - 2708 | * z11, z12, z13, z21, z22, z23, z31, z32, z33 - 2709 | * 2710 | * coupling : 2711 | * getgravconst- 2712 | * initl - 2713 | * dscom - 2714 | * dpper - 2715 | * dsinit - 2716 | * sgp4 - 2717 | * 2718 | * references : 2719 | * hoots, roehrich, norad spacetrack report #3 1980 2720 | * hoots, norad spacetrack report #6 1986 2721 | * hoots, schumacher and glover 2004 2722 | * vallado, crawford, hujsak, kelso 2006 2723 | ----------------------------------------------------------------------------*/ 2724 | function sgp4init(satrec, options) { 2725 | /* eslint-disable no-param-reassign */ 2726 | var opsmode = options.opsmode, 2727 | satn = options.satn, 2728 | epoch = options.epoch, 2729 | xbstar = options.xbstar, 2730 | xecco = options.xecco, 2731 | xargpo = options.xargpo, 2732 | xinclo = options.xinclo, 2733 | xmo = options.xmo, 2734 | xno = options.xno, 2735 | xnodeo = options.xnodeo; 2736 | var cosim; 2737 | var sinim; 2738 | var cc1sq; 2739 | var cc2; 2740 | var cc3; 2741 | var coef; 2742 | var coef1; 2743 | var cosio4; 2744 | var em; 2745 | var emsq; 2746 | var eeta; 2747 | var etasq; 2748 | var argpm; 2749 | var nodem; 2750 | var inclm; 2751 | var mm; 2752 | var nm; 2753 | var perige; 2754 | var pinvsq; 2755 | var psisq; 2756 | var qzms24; 2757 | var s1; 2758 | var s2; 2759 | var s3; 2760 | var s4; 2761 | var s5; 2762 | var sfour; 2763 | var ss1; 2764 | var ss2; 2765 | var ss3; 2766 | var ss4; 2767 | var ss5; 2768 | var sz1; 2769 | var sz3; 2770 | var sz11; 2771 | var sz13; 2772 | var sz21; 2773 | var sz23; 2774 | var sz31; 2775 | var sz33; 2776 | var tc; 2777 | var temp; 2778 | var temp1; 2779 | var temp2; 2780 | var temp3; 2781 | var tsi; 2782 | var xpidot; 2783 | var xhdot1; 2784 | var z1; 2785 | var z3; 2786 | var z11; 2787 | var z13; 2788 | var z21; 2789 | var z23; 2790 | var z31; 2791 | var z33; 2792 | /* ------------------------ initialization --------------------- */ 2793 | // sgp4fix divisor for divide by zero check on inclination 2794 | // the old check used 1.0 + Math.cos(pi-1.0e-9), but then compared it to 2795 | // 1.5 e-12, so the threshold was changed to 1.5e-12 for consistency 2796 | 2797 | var temp4 = 1.5e-12; // ----------- set all near earth variables to zero ------------ 2798 | 2799 | satrec.isimp = 0; 2800 | satrec.method = 'n'; 2801 | satrec.aycof = 0.0; 2802 | satrec.con41 = 0.0; 2803 | satrec.cc1 = 0.0; 2804 | satrec.cc4 = 0.0; 2805 | satrec.cc5 = 0.0; 2806 | satrec.d2 = 0.0; 2807 | satrec.d3 = 0.0; 2808 | satrec.d4 = 0.0; 2809 | satrec.delmo = 0.0; 2810 | satrec.eta = 0.0; 2811 | satrec.argpdot = 0.0; 2812 | satrec.omgcof = 0.0; 2813 | satrec.sinmao = 0.0; 2814 | satrec.t = 0.0; 2815 | satrec.t2cof = 0.0; 2816 | satrec.t3cof = 0.0; 2817 | satrec.t4cof = 0.0; 2818 | satrec.t5cof = 0.0; 2819 | satrec.x1mth2 = 0.0; 2820 | satrec.x7thm1 = 0.0; 2821 | satrec.mdot = 0.0; 2822 | satrec.nodedot = 0.0; 2823 | satrec.xlcof = 0.0; 2824 | satrec.xmcof = 0.0; 2825 | satrec.nodecf = 0.0; // ----------- set all deep space variables to zero ------------ 2826 | 2827 | satrec.irez = 0; 2828 | satrec.d2201 = 0.0; 2829 | satrec.d2211 = 0.0; 2830 | satrec.d3210 = 0.0; 2831 | satrec.d3222 = 0.0; 2832 | satrec.d4410 = 0.0; 2833 | satrec.d4422 = 0.0; 2834 | satrec.d5220 = 0.0; 2835 | satrec.d5232 = 0.0; 2836 | satrec.d5421 = 0.0; 2837 | satrec.d5433 = 0.0; 2838 | satrec.dedt = 0.0; 2839 | satrec.del1 = 0.0; 2840 | satrec.del2 = 0.0; 2841 | satrec.del3 = 0.0; 2842 | satrec.didt = 0.0; 2843 | satrec.dmdt = 0.0; 2844 | satrec.dnodt = 0.0; 2845 | satrec.domdt = 0.0; 2846 | satrec.e3 = 0.0; 2847 | satrec.ee2 = 0.0; 2848 | satrec.peo = 0.0; 2849 | satrec.pgho = 0.0; 2850 | satrec.pho = 0.0; 2851 | satrec.pinco = 0.0; 2852 | satrec.plo = 0.0; 2853 | satrec.se2 = 0.0; 2854 | satrec.se3 = 0.0; 2855 | satrec.sgh2 = 0.0; 2856 | satrec.sgh3 = 0.0; 2857 | satrec.sgh4 = 0.0; 2858 | satrec.sh2 = 0.0; 2859 | satrec.sh3 = 0.0; 2860 | satrec.si2 = 0.0; 2861 | satrec.si3 = 0.0; 2862 | satrec.sl2 = 0.0; 2863 | satrec.sl3 = 0.0; 2864 | satrec.sl4 = 0.0; 2865 | satrec.gsto = 0.0; 2866 | satrec.xfact = 0.0; 2867 | satrec.xgh2 = 0.0; 2868 | satrec.xgh3 = 0.0; 2869 | satrec.xgh4 = 0.0; 2870 | satrec.xh2 = 0.0; 2871 | satrec.xh3 = 0.0; 2872 | satrec.xi2 = 0.0; 2873 | satrec.xi3 = 0.0; 2874 | satrec.xl2 = 0.0; 2875 | satrec.xl3 = 0.0; 2876 | satrec.xl4 = 0.0; 2877 | satrec.xlamo = 0.0; 2878 | satrec.zmol = 0.0; 2879 | satrec.zmos = 0.0; 2880 | satrec.atime = 0.0; 2881 | satrec.xli = 0.0; 2882 | satrec.xni = 0.0; // sgp4fix - note the following variables are also passed directly via satrec. 2883 | // it is possible to streamline the sgp4init call by deleting the "x" 2884 | // variables, but the user would need to set the satrec.* values first. we 2885 | // include the additional assignments in case twoline2rv is not used. 2886 | 2887 | satrec.bstar = xbstar; 2888 | satrec.ecco = xecco; 2889 | satrec.argpo = xargpo; 2890 | satrec.inclo = xinclo; 2891 | satrec.mo = xmo; 2892 | satrec.no = xno; 2893 | satrec.nodeo = xnodeo; // sgp4fix add opsmode 2894 | 2895 | satrec.operationmode = opsmode; // ------------------------ earth constants ----------------------- 2896 | // sgp4fix identify constants and allow alternate values 2897 | 2898 | var ss = 78.0 / _constants.earthRadius + 1.0; // sgp4fix use multiply for speed instead of pow 2899 | 2900 | var qzms2ttemp = (120.0 - 78.0) / _constants.earthRadius; 2901 | var qzms2t = qzms2ttemp * qzms2ttemp * qzms2ttemp * qzms2ttemp; 2902 | satrec.init = 'y'; 2903 | satrec.t = 0.0; 2904 | var initlOptions = { 2905 | satn: satn, 2906 | ecco: satrec.ecco, 2907 | epoch: epoch, 2908 | inclo: satrec.inclo, 2909 | no: satrec.no, 2910 | method: satrec.method, 2911 | opsmode: satrec.operationmode 2912 | }; 2913 | var initlResult = (0, _initl.default)(initlOptions); 2914 | var ao = initlResult.ao, 2915 | con42 = initlResult.con42, 2916 | cosio = initlResult.cosio, 2917 | cosio2 = initlResult.cosio2, 2918 | eccsq = initlResult.eccsq, 2919 | omeosq = initlResult.omeosq, 2920 | posq = initlResult.posq, 2921 | rp = initlResult.rp, 2922 | rteosq = initlResult.rteosq, 2923 | sinio = initlResult.sinio; 2924 | satrec.no = initlResult.no; 2925 | satrec.con41 = initlResult.con41; 2926 | satrec.gsto = initlResult.gsto; 2927 | satrec.error = 0; // sgp4fix remove this check as it is unnecessary 2928 | // the mrt check in sgp4 handles decaying satellite cases even if the starting 2929 | // condition is below the surface of te earth 2930 | // if (rp < 1.0) 2931 | // { 2932 | // printf("// *** satn%d epoch elts sub-orbital ***\n", satn); 2933 | // satrec.error = 5; 2934 | // } 2935 | 2936 | if (omeosq >= 0.0 || satrec.no >= 0.0) { 2937 | satrec.isimp = 0; 2938 | 2939 | if (rp < 220.0 / _constants.earthRadius + 1.0) { 2940 | satrec.isimp = 1; 2941 | } 2942 | 2943 | sfour = ss; 2944 | qzms24 = qzms2t; 2945 | perige = (rp - 1.0) * _constants.earthRadius; // - for perigees below 156 km, s and qoms2t are altered - 2946 | 2947 | if (perige < 156.0) { 2948 | sfour = perige - 78.0; 2949 | 2950 | if (perige < 98.0) { 2951 | sfour = 20.0; 2952 | } // sgp4fix use multiply for speed instead of pow 2953 | 2954 | 2955 | var qzms24temp = (120.0 - sfour) / _constants.earthRadius; 2956 | qzms24 = qzms24temp * qzms24temp * qzms24temp * qzms24temp; 2957 | sfour = sfour / _constants.earthRadius + 1.0; 2958 | } 2959 | 2960 | pinvsq = 1.0 / posq; 2961 | tsi = 1.0 / (ao - sfour); 2962 | satrec.eta = ao * satrec.ecco * tsi; 2963 | etasq = satrec.eta * satrec.eta; 2964 | eeta = satrec.ecco * satrec.eta; 2965 | psisq = Math.abs(1.0 - etasq); 2966 | coef = qzms24 * Math.pow(tsi, 4.0); 2967 | coef1 = coef / Math.pow(psisq, 3.5); 2968 | cc2 = coef1 * satrec.no * (ao * (1.0 + 1.5 * etasq + eeta * (4.0 + etasq)) + 0.375 * _constants.j2 * tsi / psisq * satrec.con41 * (8.0 + 3.0 * etasq * (8.0 + etasq))); 2969 | satrec.cc1 = satrec.bstar * cc2; 2970 | cc3 = 0.0; 2971 | 2972 | if (satrec.ecco > 1.0e-4) { 2973 | cc3 = -2.0 * coef * tsi * _constants.j3oj2 * satrec.no * sinio / satrec.ecco; 2974 | } 2975 | 2976 | satrec.x1mth2 = 1.0 - cosio2; 2977 | satrec.cc4 = 2.0 * satrec.no * coef1 * ao * omeosq * (satrec.eta * (2.0 + 0.5 * etasq) + satrec.ecco * (0.5 + 2.0 * etasq) - _constants.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))); 2978 | satrec.cc5 = 2.0 * coef1 * ao * omeosq * (1.0 + 2.75 * (etasq + eeta) + eeta * etasq); 2979 | cosio4 = cosio2 * cosio2; 2980 | temp1 = 1.5 * _constants.j2 * pinvsq * satrec.no; 2981 | temp2 = 0.5 * temp1 * _constants.j2 * pinvsq; 2982 | temp3 = -0.46875 * _constants.j4 * pinvsq * pinvsq * satrec.no; 2983 | satrec.mdot = satrec.no + 0.5 * temp1 * rteosq * satrec.con41 + 0.0625 * temp2 * rteosq * (13.0 - 78.0 * cosio2 + 137.0 * cosio4); 2984 | 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); 2985 | xhdot1 = -temp1 * cosio; 2986 | satrec.nodedot = xhdot1 + (0.5 * temp2 * (4.0 - 19.0 * cosio2) + 2.0 * temp3 * (3.0 - 7.0 * cosio2)) * cosio; 2987 | xpidot = satrec.argpdot + satrec.nodedot; 2988 | satrec.omgcof = satrec.bstar * cc3 * Math.cos(satrec.argpo); 2989 | satrec.xmcof = 0.0; 2990 | 2991 | if (satrec.ecco > 1.0e-4) { 2992 | satrec.xmcof = -_constants.x2o3 * coef * satrec.bstar / eeta; 2993 | } 2994 | 2995 | satrec.nodecf = 3.5 * omeosq * xhdot1 * satrec.cc1; 2996 | satrec.t2cof = 1.5 * satrec.cc1; // sgp4fix for divide by zero with xinco = 180 deg 2997 | 2998 | if (Math.abs(cosio + 1.0) > 1.5e-12) { 2999 | satrec.xlcof = -0.25 * _constants.j3oj2 * sinio * (3.0 + 5.0 * cosio) / (1.0 + cosio); 3000 | } else { 3001 | satrec.xlcof = -0.25 * _constants.j3oj2 * sinio * (3.0 + 5.0 * cosio) / temp4; 3002 | } 3003 | 3004 | satrec.aycof = -0.5 * _constants.j3oj2 * sinio; // sgp4fix use multiply for speed instead of pow 3005 | 3006 | var delmotemp = 1.0 + satrec.eta * Math.cos(satrec.mo); 3007 | satrec.delmo = delmotemp * delmotemp * delmotemp; 3008 | satrec.sinmao = Math.sin(satrec.mo); 3009 | satrec.x7thm1 = 7.0 * cosio2 - 1.0; // --------------- deep space initialization ------------- 3010 | 3011 | if (2 * _constants.pi / satrec.no >= 225.0) { 3012 | satrec.method = 'd'; 3013 | satrec.isimp = 1; 3014 | tc = 0.0; 3015 | inclm = satrec.inclo; 3016 | var dscomOptions = { 3017 | epoch: epoch, 3018 | ep: satrec.ecco, 3019 | argpp: satrec.argpo, 3020 | tc: tc, 3021 | inclp: satrec.inclo, 3022 | nodep: satrec.nodeo, 3023 | np: satrec.no, 3024 | e3: satrec.e3, 3025 | ee2: satrec.ee2, 3026 | peo: satrec.peo, 3027 | pgho: satrec.pgho, 3028 | pho: satrec.pho, 3029 | pinco: satrec.pinco, 3030 | plo: satrec.plo, 3031 | se2: satrec.se2, 3032 | se3: satrec.se3, 3033 | sgh2: satrec.sgh2, 3034 | sgh3: satrec.sgh3, 3035 | sgh4: satrec.sgh4, 3036 | sh2: satrec.sh2, 3037 | sh3: satrec.sh3, 3038 | si2: satrec.si2, 3039 | si3: satrec.si3, 3040 | sl2: satrec.sl2, 3041 | sl3: satrec.sl3, 3042 | sl4: satrec.sl4, 3043 | xgh2: satrec.xgh2, 3044 | xgh3: satrec.xgh3, 3045 | xgh4: satrec.xgh4, 3046 | xh2: satrec.xh2, 3047 | xh3: satrec.xh3, 3048 | xi2: satrec.xi2, 3049 | xi3: satrec.xi3, 3050 | xl2: satrec.xl2, 3051 | xl3: satrec.xl3, 3052 | xl4: satrec.xl4, 3053 | zmol: satrec.zmol, 3054 | zmos: satrec.zmos 3055 | }; 3056 | var dscomResult = (0, _dscom.default)(dscomOptions); 3057 | satrec.e3 = dscomResult.e3; 3058 | satrec.ee2 = dscomResult.ee2; 3059 | satrec.peo = dscomResult.peo; 3060 | satrec.pgho = dscomResult.pgho; 3061 | satrec.pho = dscomResult.pho; 3062 | satrec.pinco = dscomResult.pinco; 3063 | satrec.plo = dscomResult.plo; 3064 | satrec.se2 = dscomResult.se2; 3065 | satrec.se3 = dscomResult.se3; 3066 | satrec.sgh2 = dscomResult.sgh2; 3067 | satrec.sgh3 = dscomResult.sgh3; 3068 | satrec.sgh4 = dscomResult.sgh4; 3069 | satrec.sh2 = dscomResult.sh2; 3070 | satrec.sh3 = dscomResult.sh3; 3071 | satrec.si2 = dscomResult.si2; 3072 | satrec.si3 = dscomResult.si3; 3073 | satrec.sl2 = dscomResult.sl2; 3074 | satrec.sl3 = dscomResult.sl3; 3075 | satrec.sl4 = dscomResult.sl4; 3076 | sinim = dscomResult.sinim; 3077 | cosim = dscomResult.cosim; 3078 | em = dscomResult.em; 3079 | emsq = dscomResult.emsq; 3080 | s1 = dscomResult.s1; 3081 | s2 = dscomResult.s2; 3082 | s3 = dscomResult.s3; 3083 | s4 = dscomResult.s4; 3084 | s5 = dscomResult.s5; 3085 | ss1 = dscomResult.ss1; 3086 | ss2 = dscomResult.ss2; 3087 | ss3 = dscomResult.ss3; 3088 | ss4 = dscomResult.ss4; 3089 | ss5 = dscomResult.ss5; 3090 | sz1 = dscomResult.sz1; 3091 | sz3 = dscomResult.sz3; 3092 | sz11 = dscomResult.sz11; 3093 | sz13 = dscomResult.sz13; 3094 | sz21 = dscomResult.sz21; 3095 | sz23 = dscomResult.sz23; 3096 | sz31 = dscomResult.sz31; 3097 | sz33 = dscomResult.sz33; 3098 | satrec.xgh2 = dscomResult.xgh2; 3099 | satrec.xgh3 = dscomResult.xgh3; 3100 | satrec.xgh4 = dscomResult.xgh4; 3101 | satrec.xh2 = dscomResult.xh2; 3102 | satrec.xh3 = dscomResult.xh3; 3103 | satrec.xi2 = dscomResult.xi2; 3104 | satrec.xi3 = dscomResult.xi3; 3105 | satrec.xl2 = dscomResult.xl2; 3106 | satrec.xl3 = dscomResult.xl3; 3107 | satrec.xl4 = dscomResult.xl4; 3108 | satrec.zmol = dscomResult.zmol; 3109 | satrec.zmos = dscomResult.zmos; 3110 | nm = dscomResult.nm; 3111 | z1 = dscomResult.z1; 3112 | z3 = dscomResult.z3; 3113 | z11 = dscomResult.z11; 3114 | z13 = dscomResult.z13; 3115 | z21 = dscomResult.z21; 3116 | z23 = dscomResult.z23; 3117 | z31 = dscomResult.z31; 3118 | z33 = dscomResult.z33; 3119 | var dpperOptions = { 3120 | inclo: inclm, 3121 | init: satrec.init, 3122 | ep: satrec.ecco, 3123 | inclp: satrec.inclo, 3124 | nodep: satrec.nodeo, 3125 | argpp: satrec.argpo, 3126 | mp: satrec.mo, 3127 | opsmode: satrec.operationmode 3128 | }; 3129 | var dpperResult = (0, _dpper.default)(satrec, dpperOptions); 3130 | satrec.ecco = dpperResult.ep; 3131 | satrec.inclo = dpperResult.inclp; 3132 | satrec.nodeo = dpperResult.nodep; 3133 | satrec.argpo = dpperResult.argpp; 3134 | satrec.mo = dpperResult.mp; 3135 | argpm = 0.0; 3136 | nodem = 0.0; 3137 | mm = 0.0; 3138 | var dsinitOptions = { 3139 | cosim: cosim, 3140 | emsq: emsq, 3141 | argpo: satrec.argpo, 3142 | s1: s1, 3143 | s2: s2, 3144 | s3: s3, 3145 | s4: s4, 3146 | s5: s5, 3147 | sinim: sinim, 3148 | ss1: ss1, 3149 | ss2: ss2, 3150 | ss3: ss3, 3151 | ss4: ss4, 3152 | ss5: ss5, 3153 | sz1: sz1, 3154 | sz3: sz3, 3155 | sz11: sz11, 3156 | sz13: sz13, 3157 | sz21: sz21, 3158 | sz23: sz23, 3159 | sz31: sz31, 3160 | sz33: sz33, 3161 | t: satrec.t, 3162 | tc: tc, 3163 | gsto: satrec.gsto, 3164 | mo: satrec.mo, 3165 | mdot: satrec.mdot, 3166 | no: satrec.no, 3167 | nodeo: satrec.nodeo, 3168 | nodedot: satrec.nodedot, 3169 | xpidot: xpidot, 3170 | z1: z1, 3171 | z3: z3, 3172 | z11: z11, 3173 | z13: z13, 3174 | z21: z21, 3175 | z23: z23, 3176 | z31: z31, 3177 | z33: z33, 3178 | ecco: satrec.ecco, 3179 | eccsq: eccsq, 3180 | em: em, 3181 | argpm: argpm, 3182 | inclm: inclm, 3183 | mm: mm, 3184 | nm: nm, 3185 | nodem: nodem, 3186 | irez: satrec.irez, 3187 | atime: satrec.atime, 3188 | d2201: satrec.d2201, 3189 | d2211: satrec.d2211, 3190 | d3210: satrec.d3210, 3191 | d3222: satrec.d3222, 3192 | d4410: satrec.d4410, 3193 | d4422: satrec.d4422, 3194 | d5220: satrec.d5220, 3195 | d5232: satrec.d5232, 3196 | d5421: satrec.d5421, 3197 | d5433: satrec.d5433, 3198 | dedt: satrec.dedt, 3199 | didt: satrec.didt, 3200 | dmdt: satrec.dmdt, 3201 | dnodt: satrec.dnodt, 3202 | domdt: satrec.domdt, 3203 | del1: satrec.del1, 3204 | del2: satrec.del2, 3205 | del3: satrec.del3, 3206 | xfact: satrec.xfact, 3207 | xlamo: satrec.xlamo, 3208 | xli: satrec.xli, 3209 | xni: satrec.xni 3210 | }; 3211 | var dsinitResult = (0, _dsinit.default)(dsinitOptions); 3212 | satrec.irez = dsinitResult.irez; 3213 | satrec.atime = dsinitResult.atime; 3214 | satrec.d2201 = dsinitResult.d2201; 3215 | satrec.d2211 = dsinitResult.d2211; 3216 | satrec.d3210 = dsinitResult.d3210; 3217 | satrec.d3222 = dsinitResult.d3222; 3218 | satrec.d4410 = dsinitResult.d4410; 3219 | satrec.d4422 = dsinitResult.d4422; 3220 | satrec.d5220 = dsinitResult.d5220; 3221 | satrec.d5232 = dsinitResult.d5232; 3222 | satrec.d5421 = dsinitResult.d5421; 3223 | satrec.d5433 = dsinitResult.d5433; 3224 | satrec.dedt = dsinitResult.dedt; 3225 | satrec.didt = dsinitResult.didt; 3226 | satrec.dmdt = dsinitResult.dmdt; 3227 | satrec.dnodt = dsinitResult.dnodt; 3228 | satrec.domdt = dsinitResult.domdt; 3229 | satrec.del1 = dsinitResult.del1; 3230 | satrec.del2 = dsinitResult.del2; 3231 | satrec.del3 = dsinitResult.del3; 3232 | satrec.xfact = dsinitResult.xfact; 3233 | satrec.xlamo = dsinitResult.xlamo; 3234 | satrec.xli = dsinitResult.xli; 3235 | satrec.xni = dsinitResult.xni; 3236 | } // ----------- set variables if not deep space ----------- 3237 | 3238 | 3239 | if (satrec.isimp !== 1) { 3240 | cc1sq = satrec.cc1 * satrec.cc1; 3241 | satrec.d2 = 4.0 * ao * tsi * cc1sq; 3242 | temp = satrec.d2 * tsi * satrec.cc1 / 3.0; 3243 | satrec.d3 = (17.0 * ao + sfour) * temp; 3244 | satrec.d4 = 0.5 * temp * ao * tsi * (221.0 * ao + 31.0 * sfour) * satrec.cc1; 3245 | satrec.t3cof = satrec.d2 + 2.0 * cc1sq; 3246 | satrec.t4cof = 0.25 * (3.0 * satrec.d3 + satrec.cc1 * (12.0 * satrec.d2 + 10.0 * cc1sq)); 3247 | 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)); 3248 | } 3249 | /* finally propogate to zero epoch to initialize all others. */ 3250 | // sgp4fix take out check to let satellites process until they are actually below earth surface 3251 | // if(satrec.error == 0) 3252 | 3253 | } 3254 | 3255 | (0, _sgp.default)(satrec, 0, 0); 3256 | satrec.init = 'n'; 3257 | /* eslint-enable no-param-reassign */ 3258 | } 3259 | },{"../constants":2,"./dpper":8,"./dscom":9,"./dsinit":10,"./initl":13,"./sgp4":15}],17:[function(require,module,exports){ 3260 | /*! 3261 | * satellite-js v3.0.1 3262 | * (c) 2013 Shashwat Kandadai and UCSC 3263 | * https://github.com/shashwatak/satellite-js 3264 | * License: MIT 3265 | */ 3266 | 3267 | "use strict"; 3268 | 3269 | Object.defineProperty(exports, "__esModule", { 3270 | value: true 3271 | }); 3272 | exports.radiansToDegrees = radiansToDegrees; 3273 | exports.degreesToRadians = degreesToRadians; 3274 | exports.degreesLat = degreesLat; 3275 | exports.degreesLong = degreesLong; 3276 | exports.radiansLat = radiansLat; 3277 | exports.radiansLong = radiansLong; 3278 | exports.geodeticToEcf = geodeticToEcf; 3279 | exports.eciToGeodetic = eciToGeodetic; 3280 | exports.ecfToEci = ecfToEci; 3281 | exports.eciToEcf = eciToEcf; 3282 | exports.ecfToLookAngles = ecfToLookAngles; 3283 | 3284 | var _constants = require("./constants"); 3285 | 3286 | function radiansToDegrees(radians) { 3287 | return radians * _constants.rad2deg; 3288 | } 3289 | 3290 | function degreesToRadians(degrees) { 3291 | return degrees * _constants.deg2rad; 3292 | } 3293 | 3294 | function degreesLat(radians) { 3295 | if (radians < -_constants.pi / 2 || radians > _constants.pi / 2) { 3296 | throw new RangeError('Latitude radians must be in range [-pi/2; pi/2].'); 3297 | } 3298 | 3299 | return radiansToDegrees(radians); 3300 | } 3301 | 3302 | function degreesLong(radians) { 3303 | if (radians < -_constants.pi || radians > _constants.pi) { 3304 | throw new RangeError('Longitude radians must be in range [-pi; pi].'); 3305 | } 3306 | 3307 | return radiansToDegrees(radians); 3308 | } 3309 | 3310 | function radiansLat(degrees) { 3311 | if (degrees < -90 || degrees > 90) { 3312 | throw new RangeError('Latitude degrees must be in range [-90; 90].'); 3313 | } 3314 | 3315 | return degreesToRadians(degrees); 3316 | } 3317 | 3318 | function radiansLong(degrees) { 3319 | if (degrees < -180 || degrees > 180) { 3320 | throw new RangeError('Longitude degrees must be in range [-180; 180].'); 3321 | } 3322 | 3323 | return degreesToRadians(degrees); 3324 | } 3325 | 3326 | function geodeticToEcf(geodetic) { 3327 | var longitude = geodetic.longitude, 3328 | latitude = geodetic.latitude, 3329 | height = geodetic.height; 3330 | var a = 6378.137; 3331 | var b = 6356.7523142; 3332 | var f = (a - b) / a; 3333 | var e2 = 2 * f - f * f; 3334 | var normal = a / Math.sqrt(1 - e2 * (Math.sin(latitude) * Math.sin(latitude))); 3335 | var x = (normal + height) * Math.cos(latitude) * Math.cos(longitude); 3336 | var y = (normal + height) * Math.cos(latitude) * Math.sin(longitude); 3337 | var z = (normal * (1 - e2) + height) * Math.sin(latitude); 3338 | return { 3339 | x: x, 3340 | y: y, 3341 | z: z 3342 | }; 3343 | } 3344 | 3345 | function eciToGeodetic(eci, gmst) { 3346 | // http://www.celestrak.com/columns/v02n03/ 3347 | var a = 6378.137; 3348 | var b = 6356.7523142; 3349 | var R = Math.sqrt(eci.x * eci.x + eci.y * eci.y); 3350 | var f = (a - b) / a; 3351 | var e2 = 2 * f - f * f; 3352 | var longitude = Math.atan2(eci.y, eci.x) - gmst; 3353 | 3354 | while (longitude < -_constants.pi) { 3355 | longitude += _constants.twoPi; 3356 | } 3357 | 3358 | while (longitude > _constants.pi) { 3359 | longitude -= _constants.twoPi; 3360 | } 3361 | 3362 | var kmax = 20; 3363 | var k = 0; 3364 | var latitude = Math.atan2(eci.z, Math.sqrt(eci.x * eci.x + eci.y * eci.y)); 3365 | var C; 3366 | 3367 | while (k < kmax) { 3368 | C = 1 / Math.sqrt(1 - e2 * (Math.sin(latitude) * Math.sin(latitude))); 3369 | latitude = Math.atan2(eci.z + a * C * e2 * Math.sin(latitude), R); 3370 | k += 1; 3371 | } 3372 | 3373 | var height = R / Math.cos(latitude) - a * C; 3374 | return { 3375 | longitude: longitude, 3376 | latitude: latitude, 3377 | height: height 3378 | }; 3379 | } 3380 | 3381 | function ecfToEci(ecf, gmst) { 3382 | // ccar.colorado.edu/ASEN5070/handouts/coordsys.doc 3383 | // 3384 | // [X] [C -S 0][X] 3385 | // [Y] = [S C 0][Y] 3386 | // [Z]eci [0 0 1][Z]ecf 3387 | // 3388 | var X = ecf.x * Math.cos(gmst) - ecf.y * Math.sin(gmst); 3389 | var Y = ecf.x * Math.sin(gmst) + ecf.y * Math.cos(gmst); 3390 | var Z = ecf.z; 3391 | return { 3392 | x: X, 3393 | y: Y, 3394 | z: Z 3395 | }; 3396 | } 3397 | 3398 | function eciToEcf(eci, gmst) { 3399 | // ccar.colorado.edu/ASEN5070/handouts/coordsys.doc 3400 | // 3401 | // [X] [C -S 0][X] 3402 | // [Y] = [S C 0][Y] 3403 | // [Z]eci [0 0 1][Z]ecf 3404 | // 3405 | // 3406 | // Inverse: 3407 | // [X] [C S 0][X] 3408 | // [Y] = [-S C 0][Y] 3409 | // [Z]ecf [0 0 1][Z]eci 3410 | var x = eci.x * Math.cos(gmst) + eci.y * Math.sin(gmst); 3411 | var y = eci.x * -Math.sin(gmst) + eci.y * Math.cos(gmst); 3412 | var z = eci.z; 3413 | return { 3414 | x: x, 3415 | y: y, 3416 | z: z 3417 | }; 3418 | } 3419 | 3420 | function topocentric(observerGeodetic, satelliteEcf) { 3421 | // http://www.celestrak.com/columns/v02n02/ 3422 | // TS Kelso's method, except I'm using ECF frame 3423 | // and he uses ECI. 3424 | var longitude = observerGeodetic.longitude, 3425 | latitude = observerGeodetic.latitude; 3426 | var observerEcf = geodeticToEcf(observerGeodetic); 3427 | var rx = satelliteEcf.x - observerEcf.x; 3428 | var ry = satelliteEcf.y - observerEcf.y; 3429 | var rz = satelliteEcf.z - observerEcf.z; 3430 | var topS = Math.sin(latitude) * Math.cos(longitude) * rx + Math.sin(latitude) * Math.sin(longitude) * ry - Math.cos(latitude) * rz; 3431 | var topE = -Math.sin(longitude) * rx + Math.cos(longitude) * ry; 3432 | var topZ = Math.cos(latitude) * Math.cos(longitude) * rx + Math.cos(latitude) * Math.sin(longitude) * ry + Math.sin(latitude) * rz; 3433 | return { 3434 | topS: topS, 3435 | topE: topE, 3436 | topZ: topZ 3437 | }; 3438 | } 3439 | /** 3440 | * @param {Object} tc 3441 | * @param {Number} tc.topS Positive horizontal vector S due south. 3442 | * @param {Number} tc.topE Positive horizontal vector E due east. 3443 | * @param {Number} tc.topZ Vector Z normal to the surface of the earth (up). 3444 | * @returns {Object} 3445 | */ 3446 | 3447 | 3448 | function topocentricToLookAngles(tc) { 3449 | var topS = tc.topS, 3450 | topE = tc.topE, 3451 | topZ = tc.topZ; 3452 | var rangeSat = Math.sqrt(topS * topS + topE * topE + topZ * topZ); 3453 | var El = Math.asin(topZ / rangeSat); 3454 | 3455 | var Az = Math.atan2(-topE, topS) + _constants.pi; 3456 | 3457 | return { 3458 | azimuth: Az, 3459 | elevation: El, 3460 | rangeSat: rangeSat // Range in km 3461 | 3462 | }; 3463 | } 3464 | 3465 | function ecfToLookAngles(observerGeodetic, satelliteEcf) { 3466 | var topocentricCoords = topocentric(observerGeodetic, satelliteEcf); 3467 | return topocentricToLookAngles(topocentricCoords); 3468 | } 3469 | },{"./constants":2}],18:[function(require,module,exports){ 3470 | const _MS_IN_A_DAY = 1000 * 60 * 60 * 24; 3471 | 3472 | // Data formats for TLE orbital elements. 3473 | const _DATA_TYPES = { 3474 | _INT: Symbol(), 3475 | _FLOAT: Symbol(), 3476 | _CHAR: Symbol(), 3477 | _DECIMAL_ASSUMED: Symbol(), // 12345 -> 0.12345 3478 | _DECIMAL_ASSUMED_E: Symbol() // 12345-2 -> 0.0012345 3479 | }; 3480 | 3481 | const _ACCEPTABLE_TLE_INPUT_TYPES = { 3482 | _STRING: 'string', 3483 | _ARRAY: 'array', 3484 | _OBJECT: 'object' 3485 | }; 3486 | 3487 | const _LEADING_ZERO_ASSUMED_PREFIX = '0.'; 3488 | 3489 | module.exports = { 3490 | _MS_IN_A_DAY, 3491 | _DATA_TYPES, 3492 | _ACCEPTABLE_TLE_INPUT_TYPES, 3493 | _LEADING_ZERO_ASSUMED_PREFIX 3494 | }; 3495 | 3496 | },{}],19:[function(require,module,exports){ 3497 | const { _DATA_TYPES } = require('./constants'); 3498 | 3499 | const line1 = { 3500 | /* TLE line number. Will always return 1 for valid TLEs. */ 3501 | lineNumber1: { 3502 | start: 0, 3503 | length: 1, 3504 | type: _DATA_TYPES._INT 3505 | }, 3506 | 3507 | /** 3508 | * NORAD satellite catalog number (Sputnik's rocket was 00001). 3509 | * 3510 | * Range: 0 to 99999 3511 | * Example: 25544 3512 | */ 3513 | satelliteNumber: { 3514 | start: 2, 3515 | length: 5, 3516 | type: _DATA_TYPES._INT 3517 | }, 3518 | 3519 | /** 3520 | * Satellite classification. 3521 | * 'U' = unclassified 3522 | * 'C' = classified 3523 | * 'S' = secret) 3524 | * 3525 | * Example: 'U' 3526 | */ 3527 | classification: { 3528 | start: 7, 3529 | length: 1, 3530 | type: _DATA_TYPES._CHAR 3531 | }, 3532 | 3533 | /** 3534 | * International Designator: Last 2 digits of launch year. 57 to 99 = 1900s, 00-56 = 2000s. 3535 | * See https://en.wikipedia.org/wiki/International_Designator 3536 | * 3537 | * Range: 00 to 99 3538 | * Example: 98 3539 | */ 3540 | intDesignatorYear: { 3541 | start: 9, 3542 | length: 2, 3543 | type: _DATA_TYPES._INT 3544 | }, 3545 | 3546 | /** 3547 | * International Designator: Launch number of the year. 3548 | * See https://en.wikipedia.org/wiki/International_Designator 3549 | * 3550 | * Range: 1 to 999 3551 | * Example: 67 3552 | */ 3553 | intDesignatorLaunchNumber: { 3554 | start: 11, 3555 | length: 3, 3556 | type: _DATA_TYPES._INT 3557 | }, 3558 | 3559 | /** 3560 | * International Designator: Piece of the launch. 3561 | * See https://en.wikipedia.org/wiki/International_Designator 3562 | * 3563 | * Range: A to ZZZ 3564 | * Example: 'A' 3565 | */ 3566 | intDesignatorPieceOfLaunch: { 3567 | start: 14, 3568 | length: 3, 3569 | type: _DATA_TYPES._CHAR 3570 | }, 3571 | 3572 | /** 3573 | * Year when the TLE was generated (TLE epoch), last two digits. 3574 | * 3575 | * Range: 00 to 99 3576 | * Example: 17 3577 | */ 3578 | epochYear: { 3579 | start: 18, 3580 | length: 2, 3581 | type: _DATA_TYPES._INT 3582 | }, 3583 | 3584 | /** 3585 | * Fractional day of the year when the TLE was generated (TLE epoch). 3586 | * 3587 | * Range: 1 to 365.99999999 3588 | * Example: 206.18396726 3589 | */ 3590 | epochDay: { 3591 | start: 20, 3592 | length: 12, 3593 | type: _DATA_TYPES._FLOAT 3594 | }, 3595 | 3596 | /** 3597 | * First Time Derivative of the Mean Motion divided by two. Defines how mean motion changes 3598 | * from day to day, so TLE propagators can still be used to make reasonable guesses when 3599 | * times are distant from the original TLE epoch. 3600 | * 3601 | * Units: Orbits / day ^ 2 3602 | * Example: 0.00001961 3603 | */ 3604 | firstTimeDerivative: { 3605 | start: 33, 3606 | length: 11, 3607 | type: _DATA_TYPES._FLOAT 3608 | }, 3609 | 3610 | /** 3611 | * Second Time Derivative of Mean Motion divided by six (decimal point assumed). Measures rate 3612 | * of change in the Mean Motion Dot so software can make reasonable guesses when times are 3613 | * distant from the original TLE epoch. 3614 | * 3615 | * Usually zero, unless the satellite is manuevering or in a decaying orbit. 3616 | * 3617 | * Units: Orbits / day ^ 3. 3618 | * Example: 0 ('00000-0' in the original TLE [= 0.00000 * 10 ^ 0]) 3619 | */ 3620 | secondTimeDerivative: { 3621 | start: 44, 3622 | length: 8, 3623 | type: _DATA_TYPES._DECIMAL_ASSUMED_E 3624 | }, 3625 | 3626 | /** 3627 | * BSTAR drag term (decimal point assumed). Estimates the effects of 3628 | * atmospheric drag on the satellite's motion. 3629 | * 3630 | * Units: EarthRadii ^ -1 3631 | * Example: 0.000036771 ('36771-4' in the original TLE [= 0.36771 * 10 ^ -4]) 3632 | */ 3633 | bstarDrag: { 3634 | start: 53, 3635 | length: 8, 3636 | type: _DATA_TYPES._DECIMAL_ASSUMED_E 3637 | }, 3638 | 3639 | /** 3640 | * Private value - used by Air Force Space Command to reference the orbit model used to 3641 | * generate the TLE. Will always be seen as zero externally (e.g. by "us", unless you are 3642 | * "them" - in which case, hello!). 3643 | * 3644 | * Example: 0 3645 | */ 3646 | orbitModel: { 3647 | start: 62, 3648 | length: 1, 3649 | type: _DATA_TYPES._INT 3650 | }, 3651 | 3652 | /** 3653 | * TLE element set number, incremented for each new TLE generated. 999 seems to mean the TLE 3654 | * has maxed out. 3655 | * 3656 | * Range: Technically 1 to 9999, though in practice the maximum number seems to be 999. 3657 | * Example: 999 3658 | */ 3659 | tleSetNumber: { 3660 | start: 64, 3661 | length: 4, 3662 | type: _DATA_TYPES._INT 3663 | }, 3664 | 3665 | /* 3666 | * TLE line 1 checksum (modulo 10), for verifying the integrity of this line of the TLE. 3667 | * 3668 | * Range: 0 to 9 3669 | * Example: 3 3670 | */ 3671 | checksum1: { 3672 | start: 68, 3673 | length: 1, 3674 | type: _DATA_TYPES._INT 3675 | } 3676 | }; 3677 | 3678 | const line2 = { 3679 | /* TLE line number. Will always return 2 for valid TLEs. */ 3680 | lineNumber2: { 3681 | start: 0, 3682 | length: 1, 3683 | type: _DATA_TYPES._INT 3684 | }, 3685 | 3686 | /** 3687 | * NORAD satellite catalog number (Sputnik's rocket was 00001). Should match the satellite 3688 | * number on line 1. 3689 | * 3690 | * Range: 0 to 99999 3691 | * Example: 25544 3692 | */ 3693 | satelliteNumber2: { 3694 | start: 2, 3695 | length: 5, 3696 | type: _DATA_TYPES._INT 3697 | }, 3698 | 3699 | /** 3700 | * Inclination relative to the Earth's equatorial plane in degrees. 0 to 90 degrees is a 3701 | * prograde orbit and 90 to 180 degrees is a retrograde orbit. 3702 | * 3703 | * Units: degrees 3704 | * Range: 0 to 180 3705 | * Example: 51.6400 3706 | */ 3707 | inclination: { 3708 | start: 8, 3709 | length: 8, 3710 | type: _DATA_TYPES._FLOAT 3711 | }, 3712 | 3713 | /** 3714 | * Right ascension of the ascending node in degrees. Essentially, this is the angle of the 3715 | * satellite as it crosses northward (ascending) across the Earth's equator (equatorial 3716 | * plane). 3717 | * 3718 | * Units: degrees 3719 | * Range: 0 to 359.9999 3720 | * Example: 208.9163 3721 | */ 3722 | rightAscension: { 3723 | start: 17, 3724 | length: 8, 3725 | type: _DATA_TYPES._FLOAT 3726 | }, 3727 | 3728 | /** 3729 | * Orbital eccentricity, decimal point assumed. All artifical Earth satellites have an 3730 | * eccentricity between 0 (perfect circle) and 1 (parabolic orbit). 3731 | * 3732 | * Range: 0 to 1 3733 | * Example: 0.0006317 (`0006317` in the original TLE) 3734 | */ 3735 | eccentricity: { 3736 | start: 26, 3737 | length: 7, 3738 | type: _DATA_TYPES._DECIMAL_ASSUMED 3739 | }, 3740 | 3741 | /** 3742 | * Argument of perigee. See https://en.wikipedia.org/wiki/Argument_of_perigee 3743 | * Units: degrees 3744 | * Range: 0 to 359.9999 3745 | * Example: 69.9862 3746 | */ 3747 | perigee: { 3748 | start: 34, 3749 | length: 8, 3750 | type: _DATA_TYPES._FLOAT 3751 | }, 3752 | 3753 | /** 3754 | * Mean anomaly. Indicates where the satellite was located within its orbit at the time of the 3755 | * TLE epoch. 3756 | * See https://en.wikipedia.org/wiki/Mean_Anomaly 3757 | * 3758 | * Units: degrees 3759 | * Range: 0 to 359.9999 3760 | * Example: 25.2906 3761 | */ 3762 | meanAnomaly: { 3763 | start: 43, 3764 | length: 8, 3765 | type: _DATA_TYPES._FLOAT 3766 | }, 3767 | 3768 | /** 3769 | * Revolutions around the Earth per day (mean motion). 3770 | * See https://en.wikipedia.org/wiki/Mean_Motion 3771 | * 3772 | * Range: 0 to 17 (theoretically) 3773 | * Example: 15.54225995 3774 | */ 3775 | meanMotion: { 3776 | start: 52, 3777 | length: 11, 3778 | type: _DATA_TYPES._FLOAT 3779 | }, 3780 | 3781 | /** 3782 | * Total satellite revolutions when this TLE was generated. This number seems to roll over 3783 | * (e.g. 99999 -> 0). 3784 | * 3785 | * Range: 0 to 99999 3786 | * Example: 6766 3787 | */ 3788 | revNumberAtEpoch: { 3789 | start: 63, 3790 | length: 5, 3791 | type: _DATA_TYPES._INT 3792 | }, 3793 | 3794 | /* 3795 | * TLE line 1 checksum (modulo 10), for verifying the integrity of this line of the TLE. 3796 | * 3797 | * Range: 0 to 9 3798 | * Example: 0 3799 | */ 3800 | checksum2: { 3801 | start: 68, 3802 | length: 1, 3803 | type: _DATA_TYPES._INT 3804 | } 3805 | }; 3806 | 3807 | /** 3808 | * Fixed locations of orbital element value strings as they have appeared going back to the 3809 | * punchcard days. 3810 | * See https://en.wikipedia.org/wiki/Two-line_element_set. 3811 | */ 3812 | module.exports = { 3813 | line1, 3814 | line2 3815 | }; 3816 | 3817 | },{"./constants":18}],20:[function(require,module,exports){ 3818 | const SatelliteJS = require('satellite.js'); 3819 | const { 3820 | _crossesAntemeridian, 3821 | _dayOfYearToTimeStamp, 3822 | _decimalAssumedEToFloat, 3823 | _degreesToRadians, 3824 | _radiansToDegrees, 3825 | _toCamelCase 3826 | } = require('./utils'); 3827 | const tleLines = require('./line-defs'); 3828 | const { 3829 | _ACCEPTABLE_TLE_INPUT_TYPES, 3830 | _DATA_TYPES, 3831 | _LEADING_ZERO_ASSUMED_PREFIX, 3832 | _MS_IN_A_DAY 3833 | } = require('./constants'); 3834 | 3835 | // TODO: fix this ugliness 3836 | const satellitejs = (SatelliteJS.twoline2satrec) ? SatelliteJS : SatelliteJS.satellite; 3837 | 3838 | class TLEJS { 3839 | constructor() { 3840 | this.createAllTLEGetters(tleLines); 3841 | 3842 | // TODO: use Set to store cache vals. 3843 | this.cache = { 3844 | antemeridianCrossings: {} 3845 | }; 3846 | } 3847 | 3848 | /** 3849 | * Parses a TLE from a string or array input. Both two and three-line variants are acceptable. 3850 | */ 3851 | parseTLE(inputTLE) { 3852 | const fnName = 'parseTLE'; 3853 | 3854 | // Check if already an instance of a TLE object. 3855 | if (typeof inputTLE === _ACCEPTABLE_TLE_INPUT_TYPES._OBJECT && inputTLE.arr) return inputTLE; 3856 | const tleStrLong = (Array.isArray(inputTLE)) ? inputTLE.join('') : inputTLE; 3857 | const tleStr = tleStrLong.substr && tleStrLong.substr(0, 30); 3858 | const cacheKey = `${fnName}-${tleStr}`; 3859 | if (this.cache[cacheKey]) return this.cache[cacheKey]; 3860 | 3861 | const outputObj = {}; 3862 | const tleType = (Array.isArray(inputTLE)) 3863 | ? _ACCEPTABLE_TLE_INPUT_TYPES._ARRAY 3864 | : typeof inputTLE; 3865 | let tleArr = []; 3866 | 3867 | switch (tleType) { 3868 | case _ACCEPTABLE_TLE_INPUT_TYPES._ARRAY: 3869 | // Make a copy. 3870 | tleArr = inputTLE.concat(); 3871 | break; 3872 | 3873 | case _ACCEPTABLE_TLE_INPUT_TYPES._STRING: 3874 | // Convert string to array. 3875 | tleArr = inputTLE.split('\n'); 3876 | break; 3877 | 3878 | default: 3879 | throw new Error('TLE input is invalid'); 3880 | } 3881 | 3882 | // Handle 2 and 3 line variants. 3883 | if (tleArr.length > 2) { 3884 | // 3-line TLE with satellite name as the first line. 3885 | 3886 | // Keep track of satellite name. 3887 | outputObj.name = tleArr[0]; 3888 | 3889 | // Remove name from array. 3890 | tleArr.splice(0, 1); 3891 | } else { 3892 | // 2-line TLE with no satellite name. 3893 | outputObj.name = 'Unknown'; 3894 | } 3895 | 3896 | // Trim spaces 3897 | tleArr = tleArr.map(line => line.trim()); 3898 | 3899 | outputObj.arr = tleArr; 3900 | 3901 | this.cache[cacheKey] = outputObj; 3902 | 3903 | return outputObj; 3904 | } 3905 | 3906 | /** 3907 | * Determines if a TLE is valid, checking for the presence of line numbers and making sure 3908 | * the calculated checksum matches the expected checksum. 3909 | */ 3910 | isValidTLE(tle) { 3911 | const fnName = 'isValidTLE'; 3912 | 3913 | const parsedTLE = this.parseTLE(tle); 3914 | const tleStr = parsedTLE.arr.join('').substr(0, 30); 3915 | const cacheKey = `${fnName}-${tleStr}`; 3916 | if (this.cache[cacheKey]) return this.cache[cacheKey]; 3917 | 3918 | let isValid = true; 3919 | 3920 | if (parsedTLE.arr.length !== 2) return false; 3921 | 3922 | // Check line numbers and checksums at the same time. 3923 | parsedTLE.arr.forEach((line, index) => { 3924 | // Noop if already invalid. 3925 | if (!isValid) return; 3926 | 3927 | const lineNumber = index + 1; 3928 | 3929 | // Check line number. 3930 | const parsedLineNumber = this[`getLineNumber${lineNumber}`](parsedTLE); 3931 | const lineNumberIsValid = parsedLineNumber === lineNumber; 3932 | 3933 | // Checksum. 3934 | const calculatedLineChecksum = this.tleLineChecksum(parsedTLE.arr[index]); 3935 | const parsedChecksum = this[`getChecksum${lineNumber}`](parsedTLE); 3936 | const checksumIsValid = parsedChecksum === calculatedLineChecksum; 3937 | 3938 | if (!lineNumberIsValid || !checksumIsValid) { 3939 | isValid = false; 3940 | } 3941 | }); 3942 | 3943 | this.cache[cacheKey] = isValid; 3944 | 3945 | return isValid; 3946 | } 3947 | 3948 | /** 3949 | * Determines the checksum for a single line of a TLE. 3950 | * 3951 | * Checksum = modulo 10 of sum of all numbers (including line number) + 1 for each negative 3952 | * sign (-). Everything else is ignored. 3953 | */ 3954 | tleLineChecksum(tleLineStr) { 3955 | const charArr = tleLineStr.split(''); 3956 | 3957 | // Remove trailing checksum. 3958 | charArr.splice(charArr.length - 1, 1); 3959 | 3960 | if (charArr.length === 0) { 3961 | throw new Error('Character array empty!', tleLineStr); 3962 | } 3963 | 3964 | const checksum = charArr.reduce((sum, val) => { 3965 | const parsedVal = parseInt(val, 10); 3966 | const parsedSum = parseInt(sum, 10); 3967 | 3968 | if (Number.isInteger(parsedVal)) { 3969 | return parsedSum + parsedVal; 3970 | } else if (val === '-') { 3971 | return parsedSum + 1; 3972 | } 3973 | 3974 | return parsedSum; 3975 | }); 3976 | 3977 | return checksum % 10; 3978 | } 3979 | 3980 | /** 3981 | * Creates simple getters for each line of a TLE. 3982 | */ 3983 | createAllTLEGetters(lines) { 3984 | const boundCreateTLELineGetters = this.createTLELineGetters.bind(this, lines); 3985 | Object.keys(lines).forEach(boundCreateTLELineGetters); 3986 | } 3987 | 3988 | /** 3989 | * Creates simple getters for all values on a single line of a TLE. 3990 | */ 3991 | createTLELineGetters(lines, line) { 3992 | const boundCreateTLEValGetter = this.createTLEValGetter.bind(this, line); 3993 | Object.keys(lines[line]).forEach(boundCreateTLEValGetter); 3994 | } 3995 | 3996 | /** 3997 | * Creates a simple getter for a single TLE value. 3998 | * 3999 | * TODO: proper ES6 getters? 4000 | */ 4001 | createTLEValGetter(tleLine, prop) { 4002 | this[_toCamelCase(`get-${prop}`)] = (tle) => { 4003 | const parsedTLE = this.parseTLE(tle); 4004 | 4005 | const tleArr = parsedTLE.arr; 4006 | const line = (tleLine === 'line1') ? tleArr[0] : tleArr[1]; 4007 | const start = tleLines[tleLine][prop].start; 4008 | const length = tleLines[tleLine][prop].length; 4009 | 4010 | const substr = line.substr(start, length); 4011 | 4012 | let output; 4013 | switch (tleLines[tleLine][prop].type) { 4014 | case _DATA_TYPES._INT: 4015 | output = parseInt(substr, 10); 4016 | break; 4017 | 4018 | case _DATA_TYPES._FLOAT: 4019 | output = parseFloat(substr); 4020 | break; 4021 | 4022 | case _DATA_TYPES._DECIMAL_ASSUMED: 4023 | output = parseFloat(`${_LEADING_ZERO_ASSUMED_PREFIX}${substr}`); 4024 | break; 4025 | 4026 | case _DATA_TYPES._DECIMAL_ASSUMED_E: 4027 | output = _decimalAssumedEToFloat(substr); 4028 | break; 4029 | 4030 | case _DATA_TYPES._CHAR: 4031 | default: 4032 | output = substr.trim(); 4033 | break; 4034 | } 4035 | 4036 | return output; 4037 | }; 4038 | } 4039 | 4040 | /** 4041 | * Determines the Unix timestamp (in ms) of a TLE epoch (the time a TLE was generated). 4042 | * 4043 | * Example: 4044 | * getEpochTimestamp(tleStr); 4045 | * -> 1500956694771 4046 | */ 4047 | getEpochTimestamp(tle) { 4048 | const epochDay = this.getEpochDay(tle); 4049 | const epochYear = this.getEpochYear(tle); 4050 | return _dayOfYearToTimeStamp(epochDay, epochYear); 4051 | } 4052 | 4053 | /** 4054 | * Determines the name of a satellite, if present in the first line of a 3-line TLE. If not 4055 | * present, 'Unknown' is returned. 4056 | * 4057 | * Example: 4058 | * getSatelliteName(tleStr); 4059 | * -> 'ISS (ZARYA)' 4060 | */ 4061 | getSatelliteName(tle) { 4062 | const parsedTLE = this.parseTLE(tle); 4063 | return parsedTLE.name; 4064 | } 4065 | 4066 | /** 4067 | * Determines satellite position and look angles from an earth observer. 4068 | * 4069 | * Example: 4070 | * const timestampMS = 1501039265000; 4071 | * const observer = { 4072 | * lat: 34.243889, 4073 | * lng: -116.911389, 4074 | * height: 0 4075 | * }; 4076 | * const satInfo = tle.getSatelliteInfo( 4077 | * tleStr, // Satellite TLE string or array. 4078 | * timestampMS, // Timestamp (ms) 4079 | * observer.lat, // Observer latitude (degrees) 4080 | * observer.lng, // Observer longitude (degrees) 4081 | * observer.height // Observer elevation (km) 4082 | * ); 4083 | * 4084 | * -> 4085 | * { 4086 | * // satellite compass heading from observer in degrees (0 = north, 180 = south) 4087 | * azimuth: 294.5780478624994, 4088 | * 4089 | * // satellite elevation from observer in degrees (90 is directly overhead) 4090 | * elevation: 81.63903620330046, 4091 | * 4092 | * // km distance from observer to spacecraft 4093 | * range: 406.60211015810074, 4094 | * 4095 | * // spacecraft altitude in km 4096 | * height: 402.9082788620108, 4097 | 4098 | * // spacecraft latitude in degrees 4099 | * lat: 34.45112876592785, 4100 | 4101 | * // spacecraft longitude in degrees 4102 | * lng: -117.46176597710809, 4103 | * 4104 | * // spacecraft velocity in km/s 4105 | * velocity: 7.675627442183371 4106 | * } 4107 | */ 4108 | getSatelliteInfo(tle, timestamp, observerLat, observerLng, observerHeight) { 4109 | const fnName = 'getSatelliteInfo'; 4110 | 4111 | const timestampCopy = timestamp || Date.now(); 4112 | 4113 | const tleArr = (this.parseTLE(tle)).arr; 4114 | const tleStrShort = tleArr.join('').substr(0, 30); 4115 | 4116 | const defaultObserverPosition = { 4117 | lat: 36.9613422, 4118 | lng: -122.0308, 4119 | height: 0.370 4120 | }; 4121 | 4122 | const obsLat = observerLat || defaultObserverPosition.lat; 4123 | const obsLng = observerLng || defaultObserverPosition.lng; 4124 | const obsHeight = observerHeight || defaultObserverPosition.height; 4125 | 4126 | // Memoization 4127 | const cacheKey = `${fnName}-${tleStrShort}-${timestampCopy}-${observerLat}-${observerLng} 4128 | -${observerHeight}`; 4129 | if (this.cache[cacheKey]) return this.cache[cacheKey]; 4130 | 4131 | // Sanity check 4132 | if (!satellitejs) { 4133 | throw new Error('satellite.js not found'); 4134 | } 4135 | 4136 | // Initialize a satellite record 4137 | const satrec = satellitejs.twoline2satrec(tleArr[0], tleArr[1]); 4138 | 4139 | const time = new Date(timestampCopy); 4140 | 4141 | // Propagate SGP4. 4142 | const positionAndVelocity = satellitejs.propagate(satrec, time); 4143 | 4144 | if (satellitejs.error) { 4145 | throw new Error('Error: problematic TLE with unexpected eccentricity'); 4146 | } 4147 | 4148 | // The position_velocity result is a key-value pair of ECI coordinates. 4149 | // These are the base results from which all other coordinates are derived. 4150 | const positionEci = positionAndVelocity.position; 4151 | const velocityEci = positionAndVelocity.velocity; 4152 | 4153 | // Set the observer position (in radians). 4154 | const observerGd = { 4155 | latitude: _degreesToRadians(obsLat), 4156 | longitude: _degreesToRadians(obsLng), 4157 | height: obsHeight 4158 | }; 4159 | 4160 | // Get GMST for some coordinate transforms. 4161 | // http://en.wikipedia.org/wiki/Sidereal_time#Definition 4162 | const gmst = satellitejs.gstime(time); 4163 | 4164 | // Get ECF, Geodetic, Look Angles, and Doppler Factor. 4165 | const positionEcf = satellitejs.eciToEcf(positionEci, gmst); 4166 | const positionGd = satellitejs.eciToGeodetic(positionEci, gmst); 4167 | const lookAngles = satellitejs.ecfToLookAngles(observerGd, positionEcf); 4168 | 4169 | const velocityKmS = 4170 | Math.sqrt(Math.pow(velocityEci.x, 2) + 4171 | Math.pow(velocityEci.y, 2) + 4172 | Math.pow(velocityEci.z, 2)); 4173 | 4174 | // Azimuth: is simply the compass heading from the observer's position. 4175 | const azimuth = lookAngles.azimuth; 4176 | 4177 | // Geodetic coords are accessed via `longitude`, `latitude`, `height`. 4178 | const longitude = positionGd.longitude; 4179 | const latitude = positionGd.latitude; 4180 | const height = positionGd.height; 4181 | 4182 | const output = { 4183 | lng: satellitejs.degreesLong(longitude), 4184 | lat: satellitejs.degreesLat(latitude), 4185 | elevation: _radiansToDegrees(lookAngles.elevation), 4186 | azimuth: _radiansToDegrees(azimuth), 4187 | range: lookAngles.rangeSat, 4188 | height, 4189 | velocity: velocityKmS 4190 | }; 4191 | 4192 | this.cache[cacheKey] = output; 4193 | 4194 | return output; 4195 | } 4196 | 4197 | /** 4198 | * Determines current satellite position, or position at optional timestamp if passed in. 4199 | */ 4200 | getLatLon(tle, optionalTimestamp = Date.now()) { 4201 | const tleObj = this.parseTLE(tle); 4202 | 4203 | // Validation. 4204 | if (!this.isValidTLE(tleObj)) { 4205 | throw new Error('TLE could not be parsed:', tle); 4206 | } 4207 | 4208 | const satInfo = this.getSatelliteInfo(tleObj.arr, optionalTimestamp); 4209 | return { 4210 | lat: satInfo.lat, 4211 | lng: satInfo.lng 4212 | }; 4213 | } 4214 | 4215 | /** 4216 | * Determines current satellite position, or position at optional timestamp if passed in. 4217 | */ 4218 | getLatLonArr(tle, optionalTimestamp = Date.now()) { 4219 | const ll = this.getLatLon(tle, optionalTimestamp); 4220 | return [ll.lat, ll.lng]; 4221 | } 4222 | 4223 | /** 4224 | * Determines the position of the satellite at the time the TLE was generated. 4225 | */ 4226 | getLatLonAtEpoch(tle) { 4227 | return this.getLatLon(tle, this.getEpochTimestamp(tle)); 4228 | } 4229 | 4230 | /** 4231 | * Determines the average orbit length of the satellite in minutes. 4232 | */ 4233 | getAverageOrbitLengthMins(tle) { 4234 | const fnName = 'getAverageOrbitLengthMins'; 4235 | 4236 | const tleStr = tle.join('').substr(0, 30); 4237 | const cacheKey = `${fnName}-${tleStr}`; 4238 | if (this.cache[cacheKey]) return this.cache[cacheKey]; 4239 | 4240 | const meanMotionSeconds = (24 * 60) / this.getMeanMotion(tle); 4241 | 4242 | this.cache[cacheKey] = meanMotionSeconds; 4243 | 4244 | return meanMotionSeconds; 4245 | } 4246 | 4247 | /** 4248 | * Determines the Unix timestamp (in ms) of the the TLE epoch (when the TLE was generated). 4249 | */ 4250 | getTLEEpochTimestamp(tle) { 4251 | const epochYear = this.getEpochYear(tle); 4252 | const epochDayOfYear = this.getEpochDay(tle); 4253 | const timestamp = _dayOfYearToTimeStamp(epochDayOfYear, epochYear); 4254 | 4255 | return timestamp; 4256 | } 4257 | 4258 | /** 4259 | * Determines if the last antemeridian crossing has been cached. If it has, the time (in ms) 4260 | * is returned, otherwise it returns false. 4261 | */ 4262 | getCachedLastAntemeridianCrossingTimeMS(tle, timeMS) { 4263 | const orbitLengthMS = this.getAverageOrbitLengthMins(tle.arr) * 60 * 1000; 4264 | 4265 | const tleStr = tle.arr.join('').substr(0, 30); 4266 | 4267 | const cachedCrossingTimes = this.cache.antemeridianCrossings[tleStr]; 4268 | if (!cachedCrossingTimes) return false; 4269 | 4270 | if (cachedCrossingTimes === -1) return cachedCrossingTimes; 4271 | 4272 | const cachedTime = cachedCrossingTimes.filter(val => { 4273 | if (typeof val === 'object' && val.tle === tle) return -1; 4274 | 4275 | const diff = timeMS - val; 4276 | const isDiffPositive = diff > 0; 4277 | const isWithinOrbit = isDiffPositive && diff < orbitLengthMS; 4278 | return isWithinOrbit; 4279 | }); 4280 | 4281 | return cachedTime[0] || false; 4282 | } 4283 | 4284 | /** 4285 | * Determines the last time the satellite crossed the antemeridian. For mapping convenience 4286 | * and to avoid headaches, we want to avoid plotting ground tracks that cross the antemeridian. 4287 | */ 4288 | getLastAntemeridianCrossingTimeMS(tle, timeMS) { 4289 | const parsedTLE = this.parseTLE(tle); 4290 | 4291 | const cachedVal = this.getCachedLastAntemeridianCrossingTimeMS(parsedTLE, timeMS); 4292 | if (cachedVal) return cachedVal; 4293 | 4294 | const time = timeMS || Date.now(); 4295 | 4296 | let step = 1000 * 60 * 10; 4297 | let curLatLon = []; 4298 | let lastLatLon = []; 4299 | let curTimeMS = time; 4300 | let didCrossAntemeridian = false; 4301 | let tries = 0; 4302 | let isDone = false; 4303 | const maxTries = 1000; 4304 | while (!isDone) { 4305 | curLatLon = this.getLatLonArr(parsedTLE.arr, curTimeMS); 4306 | 4307 | didCrossAntemeridian = _crossesAntemeridian(lastLatLon[1], curLatLon[1]); 4308 | if (didCrossAntemeridian) { 4309 | // back up 4310 | curTimeMS += step; 4311 | step = (step > 20000) ? 20000 : step / 2; 4312 | } else { 4313 | curTimeMS -= step; 4314 | lastLatLon = curLatLon; 4315 | } 4316 | 4317 | isDone = step < 500 || tries >= maxTries; 4318 | 4319 | tries++; 4320 | } 4321 | 4322 | const couldNotFindCrossing = tries - 1 === maxTries; 4323 | const crossingTime = (couldNotFindCrossing) ? -1 : parseInt(curTimeMS, 10); 4324 | 4325 | const tleStr = parsedTLE.arr.join('').substr(0, 30); 4326 | if (!this.cache.antemeridianCrossings[tleStr]) this.cache.antemeridianCrossings[tleStr] = []; 4327 | 4328 | if (couldNotFindCrossing) { 4329 | this.cache.antemeridianCrossings[tleStr] = -1; 4330 | } else { 4331 | this.cache.antemeridianCrossings[tleStr].push(crossingTime); 4332 | } 4333 | 4334 | return crossingTime; 4335 | } 4336 | 4337 | /** 4338 | * Determines the average amount of milliseconds in one orbit. 4339 | */ 4340 | getOrbitTimeMS(tle) { 4341 | return parseInt(_MS_IN_A_DAY / this.getMeanMotion(tle), 10); 4342 | } 4343 | 4344 | /** 4345 | * Calculates three orbit arrays of latitude/longitude pairs. 4346 | * 4347 | * Example: 4348 | * const threeOrbitsArr = tle.getGroundTrackLatLng(tleStr); 4349 | * -> 4350 | * [ 4351 | * // previous orbit 4352 | * [ 4353 | * [ 45.85524291891481, -179.93297540317567 ], 4354 | * ... 4355 | * ], 4356 | * 4357 | * // current orbit 4358 | * [ 4359 | * [ 51.26165992503701, -179.9398612198045 ], 4360 | * ... 4361 | * ], 4362 | * 4363 | * // next orbit 4364 | * [ 4365 | * [ 51.0273714070371, -179.9190165549038 ], 4366 | * ... 4367 | * ] 4368 | * ] 4369 | */ 4370 | getGroundTrackLatLng(tle, stepMS, optionalTimeMS) { 4371 | const fnName = 'getGroundTrackLatLng'; 4372 | 4373 | const timeMS = optionalTimeMS || Date.now(); 4374 | const timeS = (timeMS / 1000).toFixed(); 4375 | 4376 | const parsedTLE = this.parseTLE(tle); 4377 | const tleStrTrimmed = parsedTLE.arr[1].substr(0, 30); 4378 | 4379 | const orbitTimeMS = this.getOrbitTimeMS(tle); 4380 | const curOrbitStartMS = this.getLastAntemeridianCrossingTimeMS(parsedTLE, timeMS); 4381 | 4382 | const foundCrossing = curOrbitStartMS !== -1; 4383 | 4384 | let cacheKey; 4385 | if (foundCrossing) { 4386 | const curOrbitStartS = (curOrbitStartMS / 1000).toFixed(); 4387 | 4388 | // Check for memoized values. 4389 | cacheKey = `${fnName}-${tleStrTrimmed}-${stepMS}-${curOrbitStartS}`; 4390 | if (this.cache[cacheKey]) return this.cache[cacheKey]; 4391 | } else { 4392 | // Geosync or unusual orbit. 4393 | 4394 | cacheKey = `${fnName}-${tleStrTrimmed}-${stepMS}-${timeS}`; 4395 | if (this.cache[cacheKey]) return this.cache[cacheKey]; 4396 | 4397 | this.cache[cacheKey] = [ 4398 | this.getOrbitTrack(parsedTLE.arr, timeMS, 600000, 86400000) 4399 | ]; 4400 | 4401 | return this.cache[cacheKey]; 4402 | } 4403 | 4404 | const lastOrbitStartMS = this.getLastAntemeridianCrossingTimeMS(tle, curOrbitStartMS - 10000); 4405 | const nextOrbitStartMS = this.getLastAntemeridianCrossingTimeMS( 4406 | tle, curOrbitStartMS + orbitTimeMS + (1000 * 60 * 30)); 4407 | 4408 | const orbitStartTimes = [ 4409 | lastOrbitStartMS, 4410 | curOrbitStartMS, 4411 | nextOrbitStartMS 4412 | ]; 4413 | 4414 | const orbitLatLons = orbitStartTimes.map( 4415 | orbitStartMS => this.getOrbitTrack(parsedTLE.arr, orbitStartMS, stepMS, false) 4416 | ); 4417 | 4418 | this.cache[cacheKey] = orbitLatLons; 4419 | 4420 | return orbitLatLons; 4421 | } 4422 | 4423 | /** 4424 | * Generates an array of lat/lng pairs representing a ground track (orbit track), starting 4425 | * from startTimeMS and continuing until crossing the antemeridian, which is considered the end 4426 | * of the orbit for convenience. 4427 | */ 4428 | getOrbitTrack(TLEArr, startTimeMS, stepMS, maxTimeMS = 6000000) { 4429 | const fnName = 'getOrbitTrack'; 4430 | 4431 | if (!startTimeMS) return []; 4432 | 4433 | // Memoization. 4434 | const tleStr = TLEArr.join(''); 4435 | const tleStrTrimmed = tleStr.substr(0, 30); 4436 | const startTime = (startTimeMS / 10000).toFixed(); 4437 | const cacheKey = `${fnName}-${tleStrTrimmed}-${startTime}-${stepMS}`; 4438 | if (this.cache[cacheKey]) return this.cache[cacheKey]; 4439 | 4440 | // default to 1 minute intervals 4441 | const defaultStepMS = 1000 * 60 * 1; 4442 | let stepMSCopy = stepMS || defaultStepMS; 4443 | 4444 | const latLons = []; 4445 | let curTimeMS = startTimeMS; 4446 | let lastLatLon = []; 4447 | let curLatLon = []; 4448 | let isDone = false; 4449 | let doesCrossAntemeridian = false; 4450 | while (!isDone) { 4451 | curLatLon = this.getLatLonArr(TLEArr, curTimeMS); 4452 | 4453 | doesCrossAntemeridian = _crossesAntemeridian(lastLatLon[1], curLatLon[1]); 4454 | if (doesCrossAntemeridian) { 4455 | if (stepMSCopy === 500) isDone = true; 4456 | 4457 | // Go back a bit. 4458 | curTimeMS -= stepMSCopy; 4459 | stepMSCopy = 500; 4460 | } else { 4461 | latLons.push(curLatLon); 4462 | curTimeMS += stepMSCopy; 4463 | lastLatLon = curLatLon; 4464 | } 4465 | 4466 | if (maxTimeMS && (curTimeMS - startTimeMS > maxTimeMS)) isDone = true; 4467 | } 4468 | 4469 | this.cache[cacheKey] = latLons; 4470 | 4471 | return latLons; 4472 | } 4473 | 4474 | /** 4475 | * Determes the compass bearing from the perspective of the satellite. Useful for 3D / pitched 4476 | * map perspectives. 4477 | * 4478 | * TODO: a bit buggy at extreme parts of orbits, where latitude hardly changes. 4479 | */ 4480 | getSatBearing(tle, customTimeMS) { 4481 | const parsedTLE = this.parseTLE(tle); 4482 | 4483 | const timeMS = customTimeMS || Date.now(); 4484 | 4485 | const latLon1 = this.getLatLonArr(parsedTLE.arr, timeMS); 4486 | const latLon2 = this.getLatLonArr(parsedTLE.arr, timeMS + 10000); 4487 | 4488 | const doesCrossAntemeridian = _crossesAntemeridian(latLon1[1], latLon2[1]); 4489 | 4490 | if (doesCrossAntemeridian) { 4491 | // TODO: fix 4492 | return {}; 4493 | // return this.getSatBearing(tle, customTimeMS + 10000); 4494 | } 4495 | 4496 | const lat1 = _degreesToRadians(latLon1[0]); 4497 | const lat2 = _degreesToRadians(latLon2[0]); 4498 | const lon1 = _degreesToRadians(latLon1[1]); 4499 | const lon2 = _degreesToRadians(latLon2[1]); 4500 | 4501 | const NS = (lat1 >= lat2) ? 'S' : 'N'; 4502 | const EW = (lon1 >= lon2) ? 'W' : 'E'; 4503 | 4504 | const y = Math.sin(lon2 - lon1) * Math.cos(lat2); 4505 | const x = (Math.cos(lat1) * Math.sin(lat2)) - 4506 | (Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)); 4507 | const degrees = _radiansToDegrees(Math.atan2(y, x)); 4508 | 4509 | return { 4510 | degrees, 4511 | compass: `${NS}${EW}` 4512 | }; 4513 | } 4514 | 4515 | /** 4516 | * Determines a set of three orbit ground tracks. Similar to getGroundTrackLatLng, except 4517 | * points are returned in reversed order ([longitude, latitude]), which is handy for GeoJSON. 4518 | */ 4519 | getGroundTrackLngLat(tle, stepMS, optionalTimeMS) { 4520 | const latLngArr = this.getGroundTrackLatLng(tle, stepMS, optionalTimeMS); 4521 | const lngLatArr = latLngArr.map(line => line.map(latLng => [latLng[1], latLng[0]])); 4522 | 4523 | return lngLatArr; 4524 | } 4525 | } 4526 | 4527 | module.exports = TLEJS; 4528 | 4529 | },{"./constants":18,"./line-defs":19,"./utils":21,"satellite.js":5}],21:[function(require,module,exports){ 4530 | const { _MS_IN_A_DAY, _LEADING_ZERO_ASSUMED_PREFIX } = require('./constants'); 4531 | 4532 | /** 4533 | * Determines if a number is positive. 4534 | */ 4535 | const _isPositive = num => num >= 0; 4536 | 4537 | /** 4538 | * Determines the amount of digits in a number. Used for converting a TLE's "leading decimal 4539 | * assumed" notation. 4540 | * 4541 | * Example: 4542 | * getDigitCount(12345); 4543 | * -> 5 4544 | */ 4545 | const _getDigitCount = (num) => { 4546 | const absVal = Math.abs(num); 4547 | return absVal.toString().length; 4548 | }; 4549 | 4550 | /** 4551 | * Converts a TLE's "leading decimal assumed" notation to a float representation. 4552 | * 4553 | * Example: 4554 | * toLeadingDecimal(12345); 4555 | * -> 0.12345 4556 | */ 4557 | const _toLeadingDecimal = (num) => { 4558 | const numDigits = _getDigitCount(num); 4559 | const zeroes = '0'.repeat(numDigits - 1); 4560 | return parseFloat(num * `${_LEADING_ZERO_ASSUMED_PREFIX}${zeroes}1`); 4561 | }; 4562 | 4563 | /** 4564 | * Converts a TLE's "leading decimal assumed" notation with leading zeroes to a float 4565 | * representation. 4566 | * 4567 | * Example: 4568 | * decimalAssumedEToFloat('12345-4'); 4569 | * -> 0.000012345 4570 | */ 4571 | const _decimalAssumedEToFloat = (str) => { 4572 | const numWithAssumedLeadingDecimal = str.substr(0, str.length - 2); 4573 | const num = _toLeadingDecimal(numWithAssumedLeadingDecimal); 4574 | const leadingDecimalPoints = parseInt(str.substr(str.length - 2, 2), 10); 4575 | const float = num * Math.pow(10, leadingDecimalPoints); 4576 | return float.toPrecision(5); 4577 | }; 4578 | 4579 | /** 4580 | * Converts a fractional day of the year to a timestamp. Used for parsing the TLE epoch. 4581 | */ 4582 | const _dayOfYearToTimeStamp = (dayOfYear, year = (new Date()).getFullYear()) => { 4583 | const yearStart = new Date(`1/1/${year} 0:0:0 Z`); 4584 | 4585 | const yearStartMS = yearStart.getTime(); 4586 | 4587 | return Math.floor(yearStartMS + ((dayOfYear - 1) * _MS_IN_A_DAY)); 4588 | }; 4589 | 4590 | /** 4591 | * Converts a string divided by spacer characters to camelCase representation. 4592 | * 4593 | * Examples: 4594 | * toCamelCase('foo-bar'); 4595 | * -> 'fooBar' 4596 | * toCamelCase('foo bar', ' '); 4597 | * -> 'fooBar' 4598 | */ 4599 | const _toCamelCase = (str, divider = '-') => { 4600 | const bits = str.split(divider); 4601 | 4602 | const output = []; 4603 | 4604 | output.push(bits[0]); 4605 | 4606 | for (let i = 1, len = bits.length; i < len; i++) { 4607 | output.push(bits[i].substr(0, 1).toUpperCase() + bits[i].substr(1, bits[i].length - 1)); 4608 | } 4609 | 4610 | return output.join(''); 4611 | }; 4612 | 4613 | /** 4614 | * Converts radians (0 to 2π) to degrees (0 to 360). 4615 | */ 4616 | const _radiansToDegrees = radians => radians * (180 / Math.PI); 4617 | 4618 | /** 4619 | * Converts degrees (0 to 360) to radians (0 to 2π). 4620 | */ 4621 | const _degreesToRadians = degrees => degrees * (Math.PI / 180); 4622 | 4623 | /** 4624 | * Determines if a pair of longitude points crosses over the antemeridian, which is a 4625 | * pain point for mapping software. 4626 | */ 4627 | const _crossesAntemeridian = (longitude1, longitude2) => { 4628 | if (!longitude1 || !longitude2) return false; 4629 | 4630 | const isLong1Positive = _isPositive(longitude1); 4631 | const isLong2Positive = _isPositive(longitude2); 4632 | const haveSameSigns = isLong1Positive === isLong2Positive; 4633 | 4634 | if (haveSameSigns) return false; 4635 | 4636 | // Signs don't match, so check if we're reasonably near the antemeridian (just to be sure it's 4637 | // not the prime meridian). 4638 | const isNearAntemeridian = Math.abs(longitude1) > 100; 4639 | 4640 | return isNearAntemeridian; 4641 | }; 4642 | 4643 | module.exports = { 4644 | _isPositive, 4645 | _getDigitCount, 4646 | _toLeadingDecimal, 4647 | _decimalAssumedEToFloat, 4648 | _dayOfYearToTimeStamp, 4649 | _toCamelCase, 4650 | _radiansToDegrees, 4651 | _degreesToRadians, 4652 | _crossesAntemeridian 4653 | }; 4654 | 4655 | },{"./constants":18}]},{},[1])(1) 4656 | }); 4657 | -------------------------------------------------------------------------------- /website/wx-ground-station.js: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Replace BUCKET_NAME with the bucket name. 4 | // 5 | var bucketName = ''; 6 | // Replace this block of code with the sample code located at: 7 | // Cognito -- Manage Identity Pools -- [identity_pool_name] -- Sample Code -- JavaScript 8 | // 9 | // Initialize the Amazon Cognito credentials provider 10 | AWS.config.region = 'us-west-2'; // Region 11 | AWS.config.credentials = new AWS.CognitoIdentityCredentials({ 12 | IdentityPoolId: '' 13 | }); 14 | 15 | // Create a mapbox.com account and get access token 16 | const MAP_BOX_ACCESS_TOKEN = 'YOUR MAPBOX TOKEN'; 17 | const GROUND_STATION_LAT = 45.0468; 18 | const GROUND_STATION_LON = -93.4747; 19 | const GROUND_STATION_NAME = 'my ground station'; 20 | const MAX_CAPTURES = 20; 21 | const DIR_NAME = "images"; 22 | 23 | // Create a new service object 24 | var s3 = new AWS.S3({ 25 | apiVersion: '2006-03-01', 26 | params: {Bucket: bucketName} 27 | }); 28 | 29 | var tlejs = new TLEJS(); 30 | var lastPositionOfNextPass; 31 | var nextPass = null 32 | 33 | function getSatelliteLink(tles) { 34 | var satNum = tlejs.getSatelliteNumber(tles); 35 | return "https://www.n2yo.com/satellite/?s=" + satNum; 36 | } 37 | 38 | 39 | function load() { 40 | $('#location').html(GROUND_STATION_LAT + ', ' + GROUND_STATION_LON); 41 | getUpcomingPassInfo(); 42 | getImageMetadata(DIR_NAME, function (err, metadata) { 43 | if (err) { 44 | $('#messages').html(''); 45 | return; 46 | } 47 | $('#messages').html(''); 48 | 49 | // show newest first 50 | var sortedMeta = metadata.sort(function (m1, m2) { 51 | var m1key = m1.date + "-" + m1.time; 52 | var m2key = m2.date + "-" + m2.time; 53 | return (m1key > m2key) ? -1 : 1; 54 | }); 55 | 56 | var captureCount = 0; 57 | 58 | sortedMeta.forEach(function (m) { 59 | if (++captureCount > MAX_CAPTURES) return; 60 | if (m == null) return; 61 | var mapId = m.imageKey + '-gt'; 62 | var satLink = '' + m.satellite + ''; 63 | $('#previous_passes').append([ 64 | //'
', 65 | '

', m.date, ' ', m.time, '

', 66 | '
', 67 | '
', 68 | '
', 69 | '
', 70 | '
satellite: ', satLink, '
', 71 | '
elevation: ', m.elevation, '°', '
', 72 | '
direction: ', m.direction, '
', 73 | '
downlink freq: ', m.frequency, ' MHz', '
', 74 | '
gain: ', m.gain, '
', 75 | '
channel A: ', m.chan_a, '
', 76 | '
channel B: ', m.chan_b, '
', 77 | '
', 78 | '
'].join('')); 79 | $('#previous_passes').append([ 80 | '
', 81 | '
', 82 | '
orbital elements:
', 83 | '
', m.tle1.replace(/ /g, " "), '
', 84 | '
', m.tle2.replace(/ /g, " "), '
', 85 | '
', 86 | '
'].join('')); 87 | 88 | var mapOptions = { 89 | zoomControl: false, 90 | attributionControl: false, 91 | scrollWheelZoom: false, 92 | touchZoom: false, 93 | doubleClickZoom: false, 94 | dragging: false 95 | }; 96 | var groundTrackMap = L.map(mapId, mapOptions).setView([GROUND_STATION_LAT, GROUND_STATION_LON], 4); 97 | 98 | L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { 99 | attribution: '© Mapbox © OpenStreetMap Improve this map', 100 | tileSize: 512, 101 | maxZoom: 18, 102 | zoomOffset: -1, 103 | id: 'mapbox/streets-v11', 104 | accessToken: MAP_BOX_ACCESS_TOKEN 105 | }).addTo(groundTrackMap); 106 | 107 | var bounds = groundTrackMap.getBounds(); 108 | var marker = L.marker([GROUND_STATION_LAT, GROUND_STATION_LON], {title: GROUND_STATION_NAME}).addTo(groundTrackMap); 109 | 110 | var t = m.time.split(' ').join(''); 111 | var captureTime = new Date(m.date + 'T' + t).getTime(); 112 | if (m.duration) { 113 | // get the current orbit at the middle of the pass duration so it is the correct orbit for the ground station location. 114 | captureTime += (m.duration/2) * 1000; 115 | } 116 | 117 | const orbits = tlejs.getGroundTrackLatLng( 118 | [m.tle1, m.tle2], 119 | 10000, 120 | captureTime 121 | ); 122 | var orbit = orbits[1]; 123 | var polyline = L.polyline(orbit, {color: 'red'}).addTo(groundTrackMap); 124 | const lat = 0; 125 | const lon = 1; 126 | const tickLength = 0.5; 127 | for(var i=0;i bounds.getSouth())) { 130 | // draw two ticks to indicate direction of orbit 131 | /* 132 | 133 | directionAngle: | 134 | +135deg /|\ -135deg 135 | | 136 | */ 137 | var dlon = orbit[i+1][lon] - origin[lon]; 138 | var dlat = orbit[i+1][lat] - origin[lat]; 139 | 140 | // angle from point i and point i+1 141 | var directionAngle = Math.atan2(dlat,dlon); 142 | 143 | var tickAngle = directionAngle - (135 * (Math.PI/180)) 144 | var tick = [tickLength * Math.sin(tickAngle), tickLength * Math.cos(tickAngle)]; 145 | var tickPoints = [ [origin[lat], origin[lon]], [origin[lat]+tick[lat], origin[lon]+tick[lon]] ]; 146 | L.polyline(tickPoints, {color: 'red'}).addTo(groundTrackMap); 147 | 148 | tickAngle = directionAngle + (135 * (Math.PI/180)) 149 | tick = [tickLength * Math.sin(tickAngle), tickLength * Math.cos(tickAngle)]; 150 | tickPoints = [ [origin[lat], origin[lon]], [origin[lat]+tick[lat], origin[lon]+tick[lon]] ]; 151 | L.polyline(tickPoints, {color: 'red'}).addTo(groundTrackMap); 152 | } 153 | } 154 | 155 | m.images.forEach(function (i) { 156 | if (i.filename.endsWith("-ZA.png")) i.order = 1; 157 | if (i.filename.endsWith("-MCIR.png")) i.order = 2; 158 | if (i.filename.endsWith("-NO.png")) i.order = 3; 159 | if (i.filename.endsWith("-MSA.png")) i.order = 4; 160 | if (i.filename.endsWith("-MSAPRECIP.png")) i.order = 5; 161 | if (i.filename.endsWith("-THERM.png")) i.order = 6; 162 | }); 163 | var images = m.images.sort(function (i1, i2) { 164 | return (i1.order < i2.order) ? -1 : 1; 165 | }); 166 | var imageHtml = [ 167 | '
' 168 | ]; 169 | images.forEach(function (i) { 170 | if (i.filename.endsWith('-MSAPRECIP.png')) { 171 | return; 172 | } 173 | if (m.chan_a == '3/3B (mid infrared)') { 174 | // Show MSA image if sensor 3 was used. 175 | if (i.filename.endsWith('-MSA.png')) { 176 | return; 177 | } 178 | } 179 | if (m.chan_a != '3/3B (mid infrared)') { 180 | // If no sensor 3 data, then show the thermal IR image. 181 | if (i.filename.endsWith('-THERM.png')) { 182 | return; 183 | } 184 | } 185 | var url = DIR_NAME + '/' + i.filename; 186 | var thumburl = DIR_NAME + '/' + i.thumbfilename; 187 | imageHtml.push([ 188 | '
', 189 | '', 190 | '', 191 | '', 192 | '
', 193 | i.enhancement, 194 | '
', 195 | '
'].join('')); 196 | }); 197 | imageHtml.push('
'); 198 | $('#previous_passes').append(imageHtml.join('')); 199 | }); 200 | }); 201 | } 202 | 203 | 204 | function getImageMetadata(DIR_NAME, cb) { 205 | var pattern = new RegExp(".+-[0-9]+[0-9]+\.json$"); 206 | s3.listObjects({Prefix: DIR_NAME}, function(err, data) { 207 | if (err) { 208 | return cb('There was an error viewing the directory: ' + err.message); 209 | } 210 | if (data && data.Contents && (data.Contents.length == 0)) { 211 | return cb('directory not found'); 212 | } 213 | var metadataFiles = data.Contents.filter(function (object) { 214 | return pattern.test(object.Key); 215 | }); 216 | 217 | var promises = metadataFiles.map(function(md) { 218 | var params = { 219 | Bucket: bucketName, 220 | Key: md.Key 221 | }; 222 | return s3.getObject(params).promise().then(function(data) { 223 | var s = JSON.parse(data.Body.toString()); 224 | return s; 225 | }); 226 | }); 227 | 228 | Promise.all(promises).then(function(results) { 229 | cb(null, results); 230 | }) 231 | 232 | }); 233 | } 234 | 235 | function getUpcomingPassInfo() { 236 | 237 | $.get(DIR_NAME + "/upcoming_passes.json", function(data) { 238 | var now = new Date(); 239 | var processingTime = 180000; // approx 3 minutes to process and upload images. 240 | for(var i=0;i now)) { 243 | nextPass = data[i]; 244 | } 245 | } 246 | var satLink = '' + nextPass.satellite + ''; 247 | var startDate = new Date(nextPass.start); 248 | var endDate = new Date(nextPass.end + processingTime); 249 | $("#upcoming_passes").append([ 250 | '
', 251 | '
next image capture: ', 252 | satLink, 253 | ' ', 254 | nextPass.direction, 255 | ' at ', 256 | nextPass.elevation, 257 | '° elevation', 258 | '
', 259 | '
capture begins at: ', 260 | ("0" + startDate.getHours()).slice(-2) + ":" + ("0" + startDate.getMinutes()).slice(-2), 261 | '
', 262 | '
imagery approx: ', 263 | ("0" + endDate.getHours()).slice(-2) + ":" + ("0" + endDate.getMinutes()).slice(-2), 264 | '
', 265 | '
'].join('') 266 | ); 267 | 268 | lastPositionOfNextPass = tlejs.getLatLon([nextPass.tle1, nextPass.tle2], new Date().getTime()); 269 | 270 | mapboxgl.accessToken = MAP_BOX_ACCESS_TOKEN; 271 | 272 | var flyoverMap = new mapboxgl.Map({ 273 | container: 'flyover_map', 274 | style: 'mapbox://styles/mapbox/satellite-streets-v10', 275 | center: [lastPositionOfNextPass.lng, lastPositionOfNextPass.lat], 276 | pitch: 60, 277 | bearing: 0, 278 | zoom: 3 279 | }); 280 | 281 | var staticMap = new mapboxgl.Map({ 282 | container: 'static_map', 283 | style: 'mapbox://styles/mapbox/streets-v11', 284 | center: [0, 0], 285 | zoom: 0 286 | }); 287 | 288 | function getSatLocation() { 289 | var location = tlejs.getLatLon([nextPass.tle1, nextPass.tle2], new Date().getTime()); 290 | return new mapboxgl.LngLat(location.lng, location.lat); 291 | } 292 | 293 | function getSatLocationPoint() { 294 | var l = getSatLocation(); 295 | return { 296 | "type": "Point", 297 | "coordinates": [l.lng, l.lat] 298 | }; 299 | } 300 | 301 | function getCurrentOrbit() { 302 | var orbits = tlejs.getGroundTrackLatLng( 303 | [nextPass.tle1, nextPass.tle2], 304 | 10000, 305 | new Date().getTime() 306 | ); 307 | var currentOrbit = orbits[1]; // [lat, lng] ordering 308 | var r = []; 309 | // Convert to [lng, lat] ordering as required by MapBox APIs 310 | for(var i=0;i { 356 | var currentLocation = getSatLocation(); 357 | var bearing = getBearing(currentLocation); 358 | flyoverMap.setCenter([currentLocation.lng, currentLocation.lat]); 359 | flyoverMap.setBearing(bearing); 360 | }, 500); 361 | }); 362 | 363 | 364 | 365 | staticMap.on('load', function() { 366 | staticMap.addSource('satellite-location', { 367 | "type": "geojson", 368 | "data": getSatLocationPoint() 369 | }); 370 | 371 | staticMap.addSource('current-orbit', { 372 | "type": "geojson", 373 | "data": getOrbitData() 374 | }); 375 | 376 | 377 | staticMap.addLayer({ 378 | 'id': 'orbit', 379 | 'type': 'line', 380 | 'source': 'current-orbit', 381 | 'layout': { 382 | 'line-cap': 'round', 383 | 'line-join': 'round' 384 | }, 385 | 'paint': { 386 | 'line-color': '#eeee00', 387 | 'line-width': 5, 388 | 'line-opacity': .8 389 | } 390 | }); 391 | 392 | staticMap.addLayer({ 393 | "id": "ground-station", 394 | "type": "circle", 395 | "source": { 396 | "type": "geojson", 397 | "data": { 398 | "type": "Point", 399 | "coordinates": [GROUND_STATION_LON, GROUND_STATION_LAT] 400 | } 401 | }, 402 | "paint": { 403 | "circle-radius": 10, 404 | "circle-color": "#ff0000" 405 | } 406 | }); 407 | 408 | staticMap.addLayer({ 409 | "id": "satellites", 410 | "source": "satellite-location", 411 | "type": "circle", 412 | "paint": { 413 | "circle-radius": 10, 414 | "circle-color": "#007cbf" 415 | } 416 | }); 417 | 418 | staticMap.setZoom(0); 419 | 420 | setInterval(() => { 421 | staticMap.getSource('satellite-location').setData(getSatLocationPoint()); 422 | }, 500); 423 | 424 | setInterval(() => { 425 | // set the current orbit every minute. 426 | staticMap.getSource('current-orbit').setData(getOrbitData()); 427 | }, 60000); 428 | 429 | 430 | }); 431 | 432 | }); 433 | } 434 | --------------------------------------------------------------------------------