Copyright Copyright import { DEG2RAD , DISTANCE_TO_SUN , MILLISECONDS_PER_DAY , MINUTES_PER_DAY , PLANETARIUM_DIST , RAD2DEG , RADIUS_OF_EARTH , TAU } from '@app/js/lib/constants' ; import { getUnique , saveCsv , stringPad } from '@app/js/lib/helpers' ; import { ReadonlyMat3 , vec3 } from 'gl-matrix' ; import $ from 'jquery' ; import numeric from 'numeric' ; import * as Ootk from 'ootk' ; import { SatRec } from 'satellite.js' ; import { keepTrackApi } from '../api/keepTrackApi' ; import { Eci , EciArr3 , SatGroupCollection , SatMath , SatObject , SensorManager , SensorObject , SunObject , TearrData } from '../api/keepTrackTypes' ; import { SpaceObjectType } from '../api/SpaceObjectType' ; import { dateFormat } from '../lib/external/dateFormat.js' ; import { mat3 } from '../lib/external/gl-matrix' ; import { jday } from '../timeManager/transforms' ; import { getOrbitByLatLon } from './getOrbitByLatLon' ; import { formatArgumentOfPerigee , formatInclination , formatMeanAnomaly , formatMeanMotion , formatRightAscension , StringifiedNubmer } from './tleFormater' ; window . _numeric = numeric ; type sccPassTimes = { sccNum : string ; time : number ; } ; export const getTearData = ( now , satrec , sensors , isInFOV ? ) : TearrData => { const sensor = sensors [ 0 ] ; let aer = satellite . getRae ( now , satrec , sensor ) ; isInFOV ??= satellite . checkIsInView ( sensor , aer ) ; if ( isInFOV ) { if ( satellite . isRiseSetLookangles ) { var now1 = new Date ( ) ; now1 . setTime ( Number ( now ) - 1000 ) ; let aer1 = satellite . getRae ( now1 , satrec , sensor ) ; let isInFOV1 = satellite . checkIsInView ( sensor , aer1 ) ; if ( ! isInFOV1 ) { return { time : dateFormat ( now , 'isoDateTime' , true ) , rng : aer . rng , az : aer . az , el : aer . el , name : sensor . shortName , } ; } else { now1 . setTime ( Number ( now ) + 1000 ) ; aer1 = satellite . getRae ( now1 , satrec , sensor ) ; isInFOV1 = satellite . checkIsInView ( sensor , aer1 ) ; if ( ! isInFOV1 ) { return { time : dateFormat ( now , 'isoDateTime' , true ) , rng : aer . rng , az : aer . az , el : aer . el , name : sensor . shortName , } ; } } return { time : '' , rng : - 1 , az : - 1 , el : - 1 , name : sensor . shortName , } ; } return { time : dateFormat ( now , 'isoDateTime' , true ) , rng : aer . rng , az : aer . az , el : aer . el , name : sensor . shortName , } ; } return { time : '' , rng : - 1 , az : - 1 , el : - 1 , name : sensor . shortName , } ; } ; export const currentEpoch = ( currentDate ) : [ string , string ] => { const { timeManager } = keepTrackApi . programs ; const currentDateObj = new Date ( currentDate ) ; let epochYear = currentDateObj . getUTCFullYear ( ) . toString ( ) . substr ( 2 , 2 ) ; let epochDay = timeManager . getDayOfYear ( currentDateObj ) ; let timeOfDay = ( currentDateObj . getUTCHours ( ) * 60 + currentDateObj . getUTCMinutes ( ) ) / 1440 ; const epochDayStr = stringPad . pad0 ( ( epochDay + timeOfDay ) . toFixed ( 8 ) , 12 ) ; return [ epochYear , epochDayStr ] ; } ; export const distance = ( hoverSat , selectedSat ) : string => { const { satSet , sensorManager } = keepTrackApi . programs ; if ( hoverSat == null || selectedSat == null ) return '' ; hoverSat = satSet . getSat ( hoverSat . id ) ; selectedSat = satSet . getSat ( selectedSat . id ) ; if ( selectedSat == null || hoverSat == null ) { return '' ; } if ( selectedSat . type === SpaceObjectType . STAR || hoverSat . type === SpaceObjectType . STAR ) return '' ; let distanceApartX = Math . pow ( hoverSat . position . x - selectedSat . position . x , 2 ) ; let distanceApartY = Math . pow ( hoverSat . position . y - selectedSat . position . y , 2 ) ; let distanceApartZ = Math . pow ( hoverSat . position . z - selectedSat . position . z , 2 ) ; let distanceApart = Math . sqrt ( distanceApartX + distanceApartY + distanceApartZ ) . toFixed ( 0 ) ; let sameBeamStr = '' ; try { if ( satellite . currentTEARR . inView ) { if ( parseFloat ( distanceApart ) < satellite . currentTEARR . rng * Math . sin ( DEG2RAD * sensorManager . currentSensor [ 0 ] . beamwidth ) ) { if ( satellite . currentTEARR . rng < sensorManager . currentSensor [ 0 ] . obsmaxrange && satellite . currentTEARR . rng > 0 ) { sameBeamStr = ' (Within One Beam)' ; } } } } catch { } return '<br />Range: ' + distanceApart + ' km' + sameBeamStr ; } ; export const setobs = ( sensors ) => { const { sensorManager } = keepTrackApi . programs ; try { if ( typeof sensors == 'undefined' || sensors == null ) { sensorManager . setCurrentSensor ( null ) ; $ ( '.sensor-reset-menu' ) . hide ( ) ; return ; } else { $ ( '#menu-sensor-info' ) . removeClass ( 'bmenu-item-disabled' ) ; $ ( '#menu-fov-bubble' ) . removeClass ( 'bmenu-item-disabled' ) ; $ ( '#menu-surveillance' ) . removeClass ( 'bmenu-item-disabled' ) ; $ ( '#menu-planetarium' ) . removeClass ( 'bmenu-item-disabled' ) ; $ ( '#menu-astronomy' ) . removeClass ( 'bmenu-item-disabled' ) ; $ ( '.sensor-reset-menu' ) . show ( ) ; } sensorManager . setCurrentSensor ( sensors ) ; sensorManager . currentSensorList = sensors ; sensorManager . currentSensorMultiSensor = sensors . length > 1 ; sensorManager . currentSensor [ 0 ] . observerGd = { lat : sensors [ 0 ] . lat * DEG2RAD , lon : sensors [ 0 ] . lon * DEG2RAD , alt : sensors [ 0 ] . alt , } ; } catch ( error ) { console . debug ( error ) ; } } ; export const calculateVisMag = ( sat , sensor , propTime , sun ) : number => { const satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; const rae = satellite . getRae ( propTime , satrec , sensor ) ; const distanceToSatellite = rae . rng ; const theta = Math . acos ( < number > numeric . dot ( [ - sat . position . x , - sat . position . y , - sat . position . z ] , [ sat . position . x + sun . eci . x , - sat . position . y + sun . eci . y , - sat . position . z + sun . eci . z ] ) / ( Math . sqrt ( Math . pow ( - sat . position . x , 2 ) + Math . pow ( - sat . position . y , 2 ) + Math . pow ( - sat . position . z , 2 ) ) * Math . sqrt ( Math . pow ( - sat . position . x + sun . eci . x , 2 ) + Math . pow ( - sat . position . y + sun . eci . y , 2 ) + Math . pow ( - sat . position . z + sun . eci . z , 2 ) ) ) ) ; const intrinsicMagnitude = - 1.8 ; const term2 = 5.0 * Math . log10 ( distanceToSatellite ) ; const arg = Math . sin ( theta ) + ( Math . PI - theta ) * Math . cos ( theta ) ; const term3 = - 2.5 * Math . log10 ( arg ) ; return intrinsicMagnitude + term2 + term3 ; } ; export const altitudeCheck = ( tle1 , tle2 , now ) => { const satrec = satellite . twoline2satrec ( tle1 , tle2 ) ; const { m , gmst } = calculateTimeVariables ( now , satrec ) ; let positionEci = satellite . sgp4 ( satrec , m ) ; let gpos ; try { gpos = satellite . eciToGeodetic ( positionEci . position , gmst ) ; } catch ( e ) { return 0 ; } return gpos . alt ; } ; export const setTEARR = ( currentTEARR ) : void => { satellite . currentTEARR = currentTEARR ; } ; export const getTEARR = ( sat ? , sensors ? , propTime ? ) : TearrData => { const { timeManager , sensorManager } = keepTrackApi . programs ; const currentTEARR = < TearrData > { } ; sensors = verifySensors ( sensors , sensorManager ) ; const sensor = sensors [ 0 ] ; let satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; const now = typeof propTime !== 'undefined' ? propTime : timeManager . calculateSimulationTime ( ) ; const { m , gmst } = calculateTimeVariables ( now , satrec ) ; let positionEci = satellite . sgp4 ( satrec , m ) ; try { let gpos = satellite . eciToGeodetic ( positionEci . position , gmst ) ; currentTEARR . alt = gpos . alt ; currentTEARR . lon = gpos . lon ; currentTEARR . lat = gpos . lat ; let positionEcf = satellite . eciToEcf ( positionEci . position , gmst ) ; let lookAngles = satellite . ecfToLookAngles ( sensor . observerGd , positionEcf ) ; currentTEARR . az = lookAngles . az * RAD2DEG ; currentTEARR . el = lookAngles . el * RAD2DEG ; currentTEARR . rng = lookAngles . rng ; } catch { currentTEARR . alt = 0 ; currentTEARR . lon = 0 ; currentTEARR . lat = 0 ; currentTEARR . az = 0 ; currentTEARR . el = 0 ; currentTEARR . rng = 0 ; } currentTEARR . inView = satellite . checkIsInView ( sensor , { az : currentTEARR . az , el : currentTEARR . el , rng : currentTEARR . rng , } ) ; satellite . currentTEARR = currentTEARR ; return currentTEARR ; } ; export const nextpassList = ( satArray ) : sccPassTimes [ ] => { let nextPassArray = [ ] ; settingsManager . nextNPassesCount ??= 1 ; for ( let s = 0 ; s < satArray . length ; s ++ ) { let time = nextNpasses ( satArray [ s ] , null , 7 , satellite . lookanglesInterval , settingsManager . nextNPassesCount ) ; for ( let i = 0 ; i < time . length ; i ++ ) { nextPassArray . push ( { sccNum : satArray [ s ] . sccNum , time : time [ i ] , } ) ; } } return nextPassArray ; } ; export const nextpass = ( sat , sensors ? , searchLength ? , interval ? ) => { const { timeManager , sensorManager } = keepTrackApi . programs ; sensors = verifySensors ( sensors , sensorManager ) ; const sensor = sensors [ 0 ] ; searchLength ??= satellite . lookanglesLength ; interval ??= satellite . lookanglesInterval ; const simulationTime = timeManager . calculateSimulationTime ( ) ; let offset = 0 ; let satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; for ( let i = 0 ; i < searchLength * 24 * 60 * 60 ; i += interval ) { offset = i * 1000 ; let now = timeManager . getOffsetTimeObj ( offset , simulationTime ) ; let aer = satellite . getRae ( now , satrec , sensor ) ; let isInFOV = satellite . checkIsInView ( sensor , aer ) ; if ( isInFOV ) return dateFormat ( now , 'isoDateTime' , true ) ; } return 'No Passes in ' + searchLength + ' Days' ; } ; export const nextNpasses = ( sat , sensors , searchLength , interval , numPasses ) => { const { timeManager , sensorManager } = keepTrackApi . programs ; sensors = verifySensors ( sensors , sensorManager ) ; const sensor = sensors [ 0 ] ; searchLength = searchLength || satellite . lookanglesLength ; interval = interval || satellite . lookanglesInterval ; numPasses = numPasses || 1 ; let passTimesArray = [ ] ; const simulationTime = timeManager . calculateSimulationTime ( ) ; let offset = 0 ; let satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; const orbitalPeriod = MINUTES_PER_DAY / ( ( satrec . no * MINUTES_PER_DAY ) / TAU ) ; for ( let i = 0 ; i < searchLength * 24 * 60 * 60 ; i += interval ) { if ( passTimesArray . length >= numPasses ) { return passTimesArray ; } offset = i * 1000 ; let now = timeManager . getOffsetTimeObj ( offset , simulationTime ) ; let aer = satellite . getRae ( now , satrec , sensor ) ; let isInFOV = satellite . checkIsInView ( sensor , aer ) ; if ( isInFOV ) { passTimesArray . push ( now ) ; i = i + orbitalPeriod * 60 * 0.75 ; } } return passTimesArray ; } ; export const getlookangles = ( sat ) : TearrData [ ] => { const { timeManager , sensorManager } = keepTrackApi . programs ; if ( ! sensorManager . checkSensorSelected ( ) ) { console . debug ( 'satellite.getlookangles requires a sensor to be set!' ) ; return [ ] ; } let sensor = sensorManager . currentSensor ; const simulationTime = timeManager . calculateSimulationTime ( ) ; let offset = 0 ; let satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; let lookanglesInterval = satellite . isRiseSetLookangles ? 1 : satellite . lookanglesInterval ; let looksArray = [ ] ; for ( let i = 0 ; i < satellite . lookanglesLength * 24 * 60 * 60 ; i += lookanglesInterval ) { offset = i * 1000 ; let now = timeManager . getOffsetTimeObj ( offset , simulationTime ) ; let looksPass = getTearData ( now , satrec , sensor ) ; if ( looksPass . time !== '' ) { looksArray . push ( looksPass ) ; } if ( looksArray . length >= 1500 ) { break ; } } looksArray . sort ( ( a , b ) => new Date ( a . time ) . getTime ( ) - new Date ( b . time ) . getTime ( ) ) ; satellite . lastlooksArray = looksArray ; ( ( ) => { let tbl = < HTMLTableElement > document . getElementById ( 'looks' ) ; tbl . innerHTML = '' ; let tr = tbl . insertRow ( ) ; let tdT = tr . insertCell ( ) ; tdT . appendChild ( document . createTextNode ( 'Time' ) ) ; tdT . setAttribute ( 'style' , 'text-decoration: underline' ) ; let tdE = tr . insertCell ( ) ; tdE . appendChild ( document . createTextNode ( 'El' ) ) ; tdE . setAttribute ( 'style' , 'text-decoration: underline' ) ; let tdA = tr . insertCell ( ) ; tdA . appendChild ( document . createTextNode ( 'Az' ) ) ; tdA . setAttribute ( 'style' , 'text-decoration: underline' ) ; let tdR = tr . insertCell ( ) ; tdR . appendChild ( document . createTextNode ( 'Rng' ) ) ; tdR . setAttribute ( 'style' , 'text-decoration: underline' ) ; for ( let i = 0 ; i < looksArray . length ; i ++ ) { if ( tbl . rows . length > 0 ) { for ( let r = 0 ; r < tbl . rows . length ; r ++ ) { let dateString = tbl . rows [ r ] . cells [ 0 ] . textContent ; let sYear = parseInt ( dateString . substr ( 0 , 4 ) ) ; let sMon = parseInt ( dateString . substr ( 5 , 2 ) ) - 1 ; let sDay = parseInt ( dateString . substr ( 8 , 2 ) ) ; let sHour = parseInt ( dateString . substr ( 11 , 2 ) ) ; let sMin = parseInt ( dateString . substr ( 14 , 2 ) ) ; let sSec = parseInt ( dateString . substr ( 17 , 2 ) ) ; let topTime = new Date ( sYear , sMon , sDay , sHour , sMin , sSec ) ; topTime . setUTCDate ( sDay ) ; topTime . setUTCHours ( sHour ) ; if ( new Date ( looksArray [ i ] . time ) < topTime ) { tr = tbl . insertRow ( i ) ; break ; } } } if ( tr == null ) { tr = tbl . insertRow ( ) ; } tdT = tr . insertCell ( ) ; tdT . appendChild ( document . createTextNode ( dateFormat ( looksArray [ i ] . time , 'isoDateTime' , false ) ) ) ; tdE = tr . insertCell ( ) ; tdE . appendChild ( document . createTextNode ( looksArray [ i ] . el . toFixed ( 1 ) ) ) ; tdA = tr . insertCell ( ) ; tdA . appendChild ( document . createTextNode ( looksArray [ i ] . az . toFixed ( 0 ) ) ) ; tdR = tr . insertCell ( ) ; tdR . appendChild ( document . createTextNode ( looksArray [ i ] . rng . toFixed ( 0 ) ) ) ; } } ) ( ) ; return looksArray ; } ; const propagateMultiSite = ( now , satrec , sensor ) : TearrData => { const aer = satellite . getRae ( now , satrec , sensor ) ; if ( satellite . checkIsInView ( sensor , aer ) ) { return { time : now . toISOString ( ) , el : aer . el , az : aer . az , rng : aer . rng , name : sensor . shortName , } ; } else { return { time : '' , el : 0 , az : 0 , rng : 0 , name : '' , } ; } } ; export const getlookanglesMultiSite = ( sat ) => { const { timeManager , sensorManager } = keepTrackApi . programs ; const isResetToDefault = ! sensorManager . checkSensorSelected ( ) ; sensorManager . tempSensor = sensorManager . currentSensor ; const simulationTime = timeManager . calculateSimulationTime ( ) ; let offset = 0 ; let satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; const orbitalPeriod = MINUTES_PER_DAY / ( ( satrec . no * MINUTES_PER_DAY ) / TAU ) ; const multiSiteArray = < TearrData [ ] > [ ] ; for ( const sensor in sensorManager . sensorList ) { satellite . setobs ( [ sensorManager . sensorList [ sensor ] ] ) ; for ( let i = 0 ; i < satellite . lookanglesLength * 24 * 60 * 60 ; i += satellite . lookanglesInterval ) { offset = i * 1000 ; let now = timeManager . getOffsetTimeObj ( offset , simulationTime ) ; let multiSitePass = propagateMultiSite ( now , satrec , sensorManager . sensorList [ sensor ] ) ; if ( multiSitePass . time !== '' ) { multiSiteArray . push ( multiSitePass ) ; i = i + orbitalPeriod * 60 * 0.75 ; } } } multiSiteArray . sort ( ( a , b ) => new Date ( a . time ) . getTime ( ) - new Date ( b . time ) . getTime ( ) ) ; satellite . lastMultiSiteArray = multiSiteArray ; populateMultiSiteTable ( multiSiteArray ) ; isResetToDefault ? sensorManager . setCurrentSensor ( sensorManager . defaultSensor ) : sensorManager . setCurrentSensor ( sensorManager . tempSensor ) ; } ; const getSatPos = ( offset , satrec ) : Eci => { try { const now = new Date ( ) ; now . setTime ( Number ( Date . now ( ) ) + offset ) ; const { m } = calculateTimeVariables ( now , satrec ) ; return satellite . sgp4 ( satrec , m ) ; } catch { return { position : { x : 0 , y : 0 , z : 0 } , velocity : { x : 0 , y : 0 , z : 0 } } ; } } ; export const findCloseObjects = ( ) => { const { satSet } = keepTrackApi . programs ; const searchRadius = 50 ; let csoList = [ ] ; let satList = < SatObject [ ] > [ ] ; for ( let i = 0 ; i < satSet . numSats ; i ++ ) { const sat = satSet . getSat ( i ) ; if ( typeof sat . TLE1 == 'undefined' ) continue ; if ( sat . apogee > 5556 ) continue ; sat . satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; sat . position = getSatPos ( 0 , sat . satrec ) . position ; if ( sat . position === { x : 0 , y : 0 , z : 0 } ) continue ; satList . push ( sat ) ; } satList = getUnique ( satList ) ; for ( let i = 0 ; i < satList . length ; i ++ ) { let sat1 = satList [ i ] ; let pos1 = sat1 . position ; let posXmin = pos1 . x - searchRadius ; let posXmax = pos1 . x + searchRadius ; let posYmin = pos1 . y - searchRadius ; let posYmax = pos1 . y + searchRadius ; let posZmin = pos1 . z - searchRadius ; let posZmax = pos1 . z + searchRadius ; for ( let j = 0 ; j < satList . length ; j ++ ) { let sat2 = satList [ j ] ; if ( sat1 == sat2 ) continue ; let pos2 = sat2 . position ; if ( pos2 . x < posXmax && pos2 . x > posXmin && pos2 . y < posYmax && pos2 . y > posYmin && pos2 . z < posZmax && pos2 . z > posZmin ) { csoList . push ( { sat1 : sat1 , sat2 : sat2 } ) ; } } } let csoListUnique = getUnique ( csoList ) ; const csoStrArr = [ ] ; for ( let i = 0 ; i < csoListUnique . length ; i ++ ) { let sat = csoListUnique [ i ] . sat1 ; let eci = getSatPos ( 1000 * 60 * 30 , sat . satrec ) ; if ( eci . position === { x : 0 , y : 0 , z : 0 } ) continue ; csoListUnique [ i ] . sat1 . position = eci . position ; sat = csoListUnique [ i ] . sat2 ; eci = getSatPos ( 1000 * 60 * 30 , sat . satrec ) ; if ( eci . position === { x : 0 , y : 0 , z : 0 } ) continue ; sat . position = eci . position ; csoListUnique [ i ] . sat2 . position = eci . position ; } for ( let i = 0 ; i < csoListUnique . length ; i ++ ) { let sat1 = csoListUnique [ i ] . sat1 ; let pos1 = sat1 . position ; if ( typeof pos1 == 'undefined' ) continue ; let posXmin = pos1 . x - searchRadius ; let posXmax = pos1 . x + searchRadius ; let posYmin = pos1 . y - searchRadius ; let posYmax = pos1 . y + searchRadius ; let posZmin = pos1 . z - searchRadius ; let posZmax = pos1 . z + searchRadius ; let sat2 = csoListUnique [ i ] . sat2 ; let pos2 = sat2 . position ; if ( typeof pos2 == 'undefined' ) continue ; if ( pos2 . x < posXmax && pos2 . x > posXmin && pos2 . y < posYmax && pos2 . y > posYmin && pos2 . z < posZmax && pos2 . z > posZmin ) { csoStrArr . push ( sat1 . sccNum ) ; csoStrArr . push ( sat2 . sccNum ) ; } } const csoListUniqueArr = Array . from ( new Set ( csoStrArr ) ) ; let searchStr = '' ; for ( let i = 0 ; i < csoListUniqueArr . length ; i ++ ) { if ( i == csoListUniqueArr . length - 1 ) { searchStr += csoListUniqueArr [ i ] ; } else { searchStr += csoListUniqueArr [ i ] + ',' ; } } return searchStr ; } ; export const calculateLookAngles = ( sat , sensors ) => { const { sensorManager , timeManager } = keepTrackApi . programs ; ( function _inputValidation ( ) { if ( typeof sensors == 'undefined' ) { if ( sensorManager . checkSensorSelected ( ) ) { sensors = sensorManager . currentSensor ; } else { console . debug ( 'getlookangles2 requires a sensor!' ) ; return ; } } else { if ( typeof sensors [ 0 ] . obsminaz == 'undefined' ) { console . debug ( 'sensors[0] format incorrect' ) ; return ; } sensors [ 0 ] . observerGd = { lat : sensors [ 0 ] . lat * DEG2RAD , lon : sensors [ 0 ] . lon * DEG2RAD , alt : sensors [ 0 ] . alt , } ; } if ( typeof sat == 'undefined' ) { console . debug ( 'sat parameter required!' ) ; } else { if ( typeof sat . TLE1 == 'undefined' || typeof sat . TLE2 == 'undefined' ) { console . debug ( 'sat parameter invalid format!' ) ; } } if ( typeof satellite . isRiseSetLookangles == 'undefined' ) { satellite . isRiseSetLookangles = false ; } } ) ( ) ; const sensor = sensors [ 0 ] ; const simulationTime = timeManager . calculateSimulationTime ( ) ; let offset = 0 ; var satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; var lookanglesTable = [ ] ; var tempLookanglesInterval ; if ( satellite . isRiseSetLookangles ) { tempLookanglesInterval = satellite . lookanglesInterval ; satellite . lookanglesInterval = 1 ; } for ( var i = 0 ; i < satellite . lookanglesLength * 24 * 60 * 60 ; i += satellite . lookanglesInterval ) { offset = i * 1000 ; let now = timeManager . getOffsetTimeObj ( offset , simulationTime ) ; if ( lookanglesTable . length <= 5000 ) { let lookanglesRow = getTearData ( now , satrec , [ sensor ] ) ; if ( lookanglesRow . time !== '' ) { lookanglesTable . push ( lookanglesRow ) ; } } } if ( satellite . isRiseSetLookangles ) { satellite . lookanglesInterval = tempLookanglesInterval ; } return lookanglesTable ; } ; export const findBestPasses = ( sats , sensor ) => { const { satSet } = keepTrackApi . programs ; sats = sats . replace ( / /gu , ',' ) ; const satArray = sats . split ( ',' ) ; let tableSatTimes = [ ] ; for ( let i = 0 ; i < satArray . length ; i ++ ) { try { let satId = satArray [ i ] ; if ( typeof satId == 'undefined' || satId == null || satId == '' || satId == ' ' ) continue ; let sat = satSet . getSatFromObjNum ( parseInt ( satId ) ) ; let satPasses = satellite . findBestPass ( sat , [ sensor ] , 0 ) ; for ( let s = 0 ; s < satPasses . length ; s ++ ) { tableSatTimes . push ( satPasses [ s ] ) ; } } catch ( e ) { console . debug ( e ) ; } } tableSatTimes . sort ( ( a , b ) => b . sortTime - a . sortTime ) ; tableSatTimes . reverse ( ) ; tableSatTimes . forEach ( ( v ) => { delete v . sortTime ; } ) ; for ( let i = 0 ; i < tableSatTimes . length ; i ++ ) { tableSatTimes [ i ] . startDate = tableSatTimes [ i ] . startDate . toISOString ( ) . split ( 'T' ) [ 0 ] ; tableSatTimes [ i ] . startTime = tableSatTimes [ i ] . startTime . toISOString ( ) . split ( 'T' ) [ 1 ] . split ( '.' ) [ 0 ] ; tableSatTimes [ i ] . stopDate = tableSatTimes [ i ] . stopDate . toISOString ( ) . split ( 'T' ) [ 0 ] ; tableSatTimes [ i ] . stopTime = tableSatTimes [ i ] . stopTime . toISOString ( ) . split ( 'T' ) [ 1 ] . split ( '.' ) [ 0 ] ; } saveCsv ( tableSatTimes , 'bestSatTimes' ) ; } ; export const findBestPass = ( sat , sensors ) => { const { sensorManager , timeManager } = keepTrackApi . programs ; ( function _inputValidation ( ) { if ( typeof sensors == 'undefined' ) { if ( sensorManager . checkSensorSelected ( ) ) { sensors = sensorManager . currentSensor ; } else { console . debug ( 'findBestPass requires a sensor!' ) ; return ; } } else { if ( typeof sensors [ 0 ] . obsminaz == 'undefined' ) { console . debug ( 'sensor format incorrect' ) ; return ; } sensors [ 0 ] . observerGd = { lat : sensors [ 0 ] . lat * DEG2RAD , lon : sensors [ 0 ] . lon * DEG2RAD , alt : sensors [ 0 ] . alt , } ; } if ( typeof sat == 'undefined' ) { console . debug ( 'sat parameter required!' ) ; } else { if ( typeof sat . TLE1 == 'undefined' || typeof sat . TLE2 == 'undefined' ) { console . debug ( 'sat parameter invalid format!' ) ; } } } ) ( ) ; const sensor = sensors [ 0 ] ; const simulationTime = timeManager . calculateSimulationTime ( ) ; let offset = 0 ; var satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; var lookanglesTable = [ ] ; let looksInterval = 5 ; let looksLength = 7 ; let score = 0 ; let sAz = < string | null > null ; let sEl = < string | null > null ; let srng = < string | null > null ; let sTime = < number | null > null ; let passMinrng = sensor . obsmaxrange ; let passMaxEl = 0 ; let start3 = false ; let stop3 = false ; let orbitalPeriod = MINUTES_PER_DAY / ( ( satrec . no * MINUTES_PER_DAY ) / TAU ) ; let _propagateBestPass = ( now , satrec ) => { let aer = satellite . getRae ( now , satrec , sensor ) ; let isInFOV = satellite . checkIsInView ( sensor , aer ) ; if ( isInFOV ) { let now1 = timeManager . getOffsetTimeObj ( offset - looksInterval * 1000 , simulationTime ) ; let aer1 = satellite . getRae ( now1 , satrec , sensor ) ; let isInFOV1 = satellite . checkIsInView ( sensor , aer1 ) ; if ( ! isInFOV1 ) { if ( aer . el <= 3.5 ) { start3 = true ; } sTime = now . getTime ( ) ; sAz = aer . az . toFixed ( 0 ) ; sEl = aer . el . toFixed ( 1 ) ; srng = aer . rng . toFixed ( 0 ) ; } else { let now1 = timeManager . getOffsetTimeObj ( offset + looksInterval * 1000 , simulationTime ) ; aer1 = satellite . getRae ( now1 , satrec , sensor ) ; isInFOV1 = satellite . checkIsInView ( sensor , aer1 ) ; if ( ! isInFOV1 ) { stop3 = aer . el <= 3.5 ; score = Math . min ( ( ( ( now . getTime ( ) - sTime ) / 1000 / 60 ) * 10 ) / 8 , 10 ) ; let elScore = Math . min ( ( passMaxEl / 50 ) * 10 , 10 ) ; elScore *= start3 && stop3 ? 2 : 1 ; score += elScore ; score += Math . min ( ( 10 * 750 ) / passMinrng , 10 ) ; let tic = 0 ; try { tic = ( now . getTime ( ) - sTime ) / 1000 ; } catch ( e ) { tic = 0 ; } if ( sTime == null ) return { } ; return { sortTime : sTime , scc : satrec . satnum , score : score , startDate : sTime , startTime : sTime , startAz : sAz , startEl : sEl , startrng : srng , stopDate : now , stopTime : now , stopAz : aer . az . toFixed ( 0 ) , stopEl : aer . el . toFixed ( 1 ) , stoprng : aer . rng . toFixed ( 0 ) , tic : tic , minrng : passMinrng . toFixed ( 0 ) , passMaxEl : passMaxEl . toFixed ( 1 ) , } ; } } if ( passMaxEl < aer . el ) passMaxEl = aer . el ; if ( passMinrng > aer . rng ) passMinrng = aer . rng ; } return { } ; } ; for ( var i = 0 ; i < looksLength * 24 * 60 * 60 ; i += looksInterval ) { offset = i * 1000 ; let now = timeManager . getOffsetTimeObj ( offset , simulationTime ) ; if ( lookanglesTable . length <= 5000 ) { let lookanglesRow = _propagateBestPass ( now , satrec ) ; if ( typeof lookanglesRow . score !== 'undefined' ) { lookanglesTable . push ( lookanglesRow ) ; score = 0 ; sAz = null ; sEl = null ; srng = null ; sTime = null ; passMinrng = sensor . obsmaxrange ; passMaxEl = 0 ; start3 = false ; stop3 = false ; i = i + orbitalPeriod * 60 * 0.75 ; } } } return lookanglesTable ; } ; export const calculateTimeVariables = ( now , satrec ? ) : { gmst : number ; m : number ; j : number ; } => { const j = jday ( now . getUTCFullYear ( ) , now . getUTCMonth ( ) + 1 , now . getUTCDate ( ) , now . getUTCHours ( ) , now . getUTCMinutes ( ) , now . getUTCSeconds ( ) ) + now . getUTCMilliseconds ( ) * MILLISECONDS_PER_DAY ; const gmst = satellite . gstime ( j ) ; const m = satrec ? ( j - satrec . jdsatepoch ) * MINUTES_PER_DAY : null ; return { gmst , m , j } ; } ; export const getRae = ( now , satrec , sensor ) => { const { gmst , m } = calculateTimeVariables ( now , satrec ) ; let positionEci = satellite . sgp4 ( satrec , m ) ; let positionEcf = satellite . eciToEcf ( positionEci . position , gmst ) ; sensor . observerGd ??= { lat : sensor . lat * DEG2RAD , lon : sensor . lon * DEG2RAD , alt : sensor . alt } ; let lookAngles = satellite . ecfToLookAngles ( sensor . observerGd , positionEcf ) ; let az = lookAngles . az * RAD2DEG ; let el = lookAngles . el * RAD2DEG ; let rng = lookAngles . rng ; return { az , el , rng } ; } ; export const eci2Rae = ( now , eci , sensor ) => { now = new Date ( now ) ; const { gmst } = calculateTimeVariables ( now ) ; let positionEcf = satellite . eciToEcf ( eci , gmst ) ; let lookAngles = satellite . ecfToLookAngles ( sensor . observerGd , positionEcf ) ; let az = lookAngles . az * RAD2DEG ; let el = lookAngles . el * RAD2DEG ; let rng = lookAngles . rng ; return { az : az , el : el , rng : rng } ; } ; export const getEci = ( sat , now ) => { let satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; const { m } = calculateTimeVariables ( now , satrec ) ; return satellite . sgp4 ( satrec , m ) ; } ; export const findNearbyObjectsByOrbit = ( sat ) => { const { satSet } = keepTrackApi . programs ; let catalog = satSet . satData ; let possibleMatches = [ ] ; let maxPeriod = sat . period * 1.05 ; let minPeriod = sat . period * 0.95 ; let maxInclination = sat . inclination * 1.025 ; let minInclination = sat . inclination * 0.975 ; let maxRaan = sat . raan * 1.025 ; let minRaan = sat . raan * 0.975 ; for ( let ss = 0 ; ss < catalog . length ; ss ++ ) { let sat2 = catalog [ ss ] ; if ( sat2 . static ) break ; if ( sat2 . period > maxPeriod || sat2 . period < minPeriod ) continue ; if ( sat2 . inclination > maxInclination || sat2 . inclination < minInclination ) continue ; if ( sat2 . raan > maxRaan || sat2 . raan < minRaan ) continue ; possibleMatches . push ( sat2 . id ) ; } return possibleMatches ; } ; export const findClosestApproachTime = ( sat1 , sat2 , propLength ? ) : { offset : number ; dist : number ; ric : { position : [ number , number , number ] ; velocity : [ number , number , number ] ; } ; } => { let offset = 0 ; propLength ??= 1440 * 60 ; let minDist = 1000000 ; let result = { offset : null , dist : null , ric : null , } ; sat1 . satrec = satellite . twoline2satrec ( sat1 . TLE1 , sat1 . TLE2 ) ; sat2 . satrec = satellite . twoline2satrec ( sat2 . TLE1 , sat2 . TLE2 ) ; for ( let t = 0 ; t < propLength ; t ++ ) { offset = t * 1000 ; sat1 = < SatObject > { ... sat1 , ... getSatPos ( offset , sat1 . satrec ) } ; sat2 = < SatObject > { ... sat2 , ... getSatPos ( offset , sat2 . satrec ) } ; const pv = sat2ric ( sat1 , sat2 ) ; const dist = Math . sqrt ( pv . position [ 0 ] ** 2 + pv . position [ 1 ] ** 2 + pv . position [ 2 ] ** 2 ) ; if ( dist < minDist && ! ( pv . position [ 0 ] === 0 && pv . position [ 1 ] === 0 && pv . position [ 2 ] === 0 ) ) { minDist = dist ; result = { offset , dist : dist , ric : pv , } ; } } keepTrackApi . programs . timeManager . changeStaticOffset ( result . offset ) ; return result ; } ; const checkIsInView = ( sensor , rae ) : boolean => { const { az , el , rng } = rae ; if ( sensor . obsminaz > sensor . obsmaxaz ) { if ( ( ( az >= sensor . obsminaz || az <= sensor . obsmaxaz ) && el >= sensor . obsminel && el <= sensor . obsmaxel && rng <= sensor . obsmaxrange && rng >= sensor . obsminrange ) || ( ( az >= sensor . obsminaz2 || az <= sensor . obsmaxaz2 ) && el >= sensor . obsminel2 && el <= sensor . obsmaxel2 && rng <= sensor . obsmaxrange2 && rng >= sensor . obsminrange2 ) ) { return true ; } else { return false ; } } else { if ( ( az >= sensor . obsminaz && az <= sensor . obsmaxaz && el >= sensor . obsminel && el <= sensor . obsmaxel && rng <= sensor . obsmaxrange && rng >= sensor . obsminrange ) || ( az >= sensor . obsminaz2 && az <= sensor . obsmaxaz2 && el >= sensor . obsminel2 && el <= sensor . obsmaxel2 && rng <= sensor . obsmaxrange2 && rng >= sensor . obsminrange2 ) ) { return true ; } else { return false ; } } } ; export const updateDopsTable = ( lat , lon , alt ) => { const { timeManager } = keepTrackApi . programs ; try { let tbl = < HTMLTableElement > document . getElementById ( 'dops' ) ; tbl . innerHTML = '' ; const simulationTime = timeManager . calculateSimulationTime ( ) ; let offset = 0 ; let tr = tbl . insertRow ( ) ; let tdT = tr . insertCell ( ) ; tdT . appendChild ( document . createTextNode ( 'Time' ) ) ; let tdH = tr . insertCell ( ) ; tdH . appendChild ( document . createTextNode ( 'HDOP' ) ) ; let tdP = tr . insertCell ( ) ; tdP . appendChild ( document . createTextNode ( 'PDOP' ) ) ; let tdG = tr . insertCell ( ) ; tdG . appendChild ( document . createTextNode ( 'GDOP' ) ) ; for ( let t = 0 ; t < 1440 ; t ++ ) { offset = t * 1000 * 60 ; const now = timeManager . getOffsetTimeObj ( offset , simulationTime ) ; let dops = satellite . getDops ( lat , lon , alt , now ) ; tr = tbl . insertRow ( ) ; tdT = tr . insertCell ( ) ; tdT . appendChild ( document . createTextNode ( dateFormat ( now , 'isoDateTime' , true ) ) ) ; tdH = tr . insertCell ( ) ; tdH . appendChild ( document . createTextNode ( dops . hdop ) ) ; tdP = tr . insertCell ( ) ; tdP . appendChild ( document . createTextNode ( dops . pdop ) ) ; tdG = tr . insertCell ( ) ; tdG . appendChild ( document . createTextNode ( dops . gdop ) ) ; } } catch ( error ) { console . debug ( error ) ; } } ; export const getDops = ( lat , lon , alt ? , propTime ? ) => { const { timeManager , groupsManager , satSet } = keepTrackApi . programs ; if ( typeof lat == 'undefined' ) throw new Error ( 'Latitude is undefined' ) ; if ( typeof lon == 'undefined' ) throw new Error ( 'Longitude is undefined' ) ; lat = lat * DEG2RAD ; lon = lon * DEG2RAD ; alt ??= 0 ; groupsManager . GPSGroup ??= groupsManager . createGroup ( 'nameRegex' , /NAVSTAR/iu ) ; propTime ??= timeManager . calculateSimulationTime ( ) ; const { gmst } = calculateTimeVariables ( propTime ) ; let inViewList = [ ] ; groupsManager . GPSGroup . sats . forEach ( ( satObj ) => { const sat = satSet . getSat ( satObj . satId ) ; const lookAngles = satellite . ecfToLookAngles ( { lon : lon , lat : lat , alt : alt } , satellite . eciToEcf ( sat . position , gmst ) ) ; sat . az = lookAngles . az * RAD2DEG ; sat . el = lookAngles . el * RAD2DEG ; if ( sat . el > settingsManager . gpsElevationMask ) { inViewList . push ( sat ) ; } } ) ; return calculateDops ( inViewList ) ; } ; export const calculateDops = ( satList ) : { pdop : string ; hdop : string ; gdop : string ; vdop : string ; tdop : string ; } => { var dops = { } ; let nsat = satList . length ; if ( nsat < 4 ) { dops . pdop = 50 ; dops . hdop = 50 ; dops . gdop = 50 ; dops . vdop = 50 ; dops . tdop = 50 ; return dops ; } var A = < any > numeric . rep ( [ nsat , 4 ] , 0 ) ; var azlist = [ ] ; var ellist = [ ] ; for ( var n = 1 ; n <= nsat ; n ++ ) { var cursat = satList [ n - 1 ] ; var az = cursat . az ; var el = cursat . el ; azlist . push ( az ) ; ellist . push ( el ) ; var B = [ Math . cos ( ( el * Math . PI ) / 180.0 ) * Math . sin ( ( az * Math . PI ) / 180.0 ) , Math . cos ( ( el * Math . PI ) / 180.0 ) * Math . cos ( ( az * Math . PI ) / 180.0 ) , Math . sin ( ( el * Math . PI ) / 180.0 ) , 1 , ] ; numeric . setBlock ( A , [ n - 1 , 0 ] , [ n - 1 , 3 ] , [ B ] ) ; } var Q = < number [ ] [ ] > numeric . dot ( numeric . transpose ( A ) , A ) ; var Qinv = numeric . inv ( Q ) ; var pdop = Math . sqrt ( Qinv [ 0 ] [ 0 ] + Qinv [ 1 ] [ 1 ] + Qinv [ 2 ] [ 2 ] ) ; var hdop = Math . sqrt ( Qinv [ 0 ] [ 0 ] + Qinv [ 1 ] [ 1 ] ) ; var gdop = Math . sqrt ( Qinv [ 0 ] [ 0 ] + Qinv [ 1 ] [ 1 ] + Qinv [ 2 ] [ 2 ] + Qinv [ 3 ] [ 3 ] ) ; var vdop = Math . sqrt ( Qinv [ 2 ] [ 2 ] ) ; var tdop = Math . sqrt ( Qinv [ 3 ] [ 3 ] ) ; dops . pdop = ( Math . round ( pdop * 100 ) / 100 ) . toFixed ( 2 ) ; dops . hdop = ( Math . round ( hdop * 100 ) / 100 ) . toFixed ( 2 ) ; dops . gdop = ( Math . round ( gdop * 100 ) / 100 ) . toFixed ( 2 ) ; dops . vdop = ( Math . round ( vdop * 100 ) / 100 ) . toFixed ( 2 ) ; dops . tdop = ( Math . round ( tdop * 100 ) / 100 ) . toFixed ( 2 ) ; return dops ; } ; export const getSunDirection = ( jd ) : EciArr3 => { const n = jd - 2451545 ; let L = 280.46 + 0.9856474 * n ; let g = 357.528 + 0.9856003 * n ; L = L % 360.0 ; g = g % 360.0 ; const ecLon = L + 1.915 * Math . sin ( g * DEG2RAD ) + 0.02 * Math . sin ( 2 * g * DEG2RAD ) ; const t = ( jd - 2451545 ) / 3652500 ; const obliq = 84381.448 - 4680.93 * t - 1.55 * Math . pow ( t , 2 ) + 1999.25 * Math . pow ( t , 3 ) - 51.38 * Math . pow ( t , 4 ) - 249.67 * Math . pow ( t , 5 ) - 39.05 * Math . pow ( t , 6 ) + 7.12 * Math . pow ( t , 7 ) + 27.87 * Math . pow ( t , 8 ) + 5.79 * Math . pow ( t , 9 ) + 2.45 * Math . pow ( t , 10 ) ; const ob = obliq / 3600.0 ; const x = DISTANCE_TO_SUN * Math . cos ( ecLon * DEG2RAD ) ; const y = DISTANCE_TO_SUN * Math . cos ( ob * DEG2RAD ) * Math . sin ( ecLon * DEG2RAD ) ; const z = DISTANCE_TO_SUN * Math . sin ( ob * DEG2RAD ) * Math . sin ( ecLon * DEG2RAD ) ; return [ x , y , z ] ; } ; export const getSunTimes = ( sat , sensors ? , searchLength ? , interval ? ) => { const { timeManager , sensorManager } = keepTrackApi . programs ; sensors = verifySensors ( sensors , sensorManager ) ; const sensor = sensors [ 0 ] ; searchLength ??= satellite . lookanglesLength ; interval ??= satellite . lookanglesInterval ; const simulationTime = timeManager . calculateSimulationTime ( ) ; let offset = 0 ; const satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; let minDistanceApart = 100000000000 ; for ( let i = 0 ; i < searchLength * 24 * 60 * 60 ; i += interval ) { offset = i * 1000 ; const now = timeManager . getOffsetTimeObj ( offset , simulationTime ) ; const { m , j , gmst } = calculateTimeVariables ( now , satrec ) ; const [ sunX , sunY , sunZ ] = getSunDirection ( j ) ; const eci = satellite . sgp4 ( satrec , m ) . position ; const distX = Math . pow ( sunX - eci . x , 2 ) ; const distY = Math . pow ( sunY - eci . y , 2 ) ; const distZ = Math . pow ( sunZ - eci . z , 2 ) ; const dist = Math . sqrt ( distX + distY + distZ ) ; const positionEcf = satellite . eciToEcf ( eci , gmst ) ; const lookAngles = satellite . ecfToLookAngles ( sensor . observerGd , positionEcf ) ; const az = lookAngles . az * RAD2DEG ; const el = lookAngles . el * RAD2DEG ; const rng = lookAngles . rng ; if ( sensor . obsminaz > sensor . obsmaxaz ) { if ( ( ( az >= sensor . obsminaz || az <= sensor . obsmaxaz ) && el >= sensor . obsminel && el <= sensor . obsmaxel && rng <= sensor . obsmaxrange && rng >= sensor . obsminrange ) || ( ( az >= sensor . obsminaz2 || az <= sensor . obsmaxaz2 ) && el >= sensor . obsminel2 && el <= sensor . obsmaxel2 && rng <= sensor . obsmaxrange2 && rng >= sensor . obsminrange2 ) ) { if ( dist < minDistanceApart ) { minDistanceApart = dist ; } } } else { if ( ( az >= sensor . obsminaz && az <= sensor . obsmaxaz && el >= sensor . obsminel && el <= sensor . obsmaxel && rng <= sensor . obsmaxrange && rng >= sensor . obsminrange ) || ( az >= sensor . obsminaz2 && az <= sensor . obsmaxaz2 && el >= sensor . obsminel2 && el <= sensor . obsmaxel2 && rng <= sensor . obsmaxrange2 && rng >= sensor . obsminrange2 ) ) { if ( dist < minDistanceApart ) { minDistanceApart = dist ; } } } } } ; export const lookAngles2Ecf = ( az , el , rng , lat , lon , alt ) : { x : number ; y : number ; z : number ; } => { const geodeticCoords = { lat : lat , lon : lon , alt : alt , } ; const site = satellite . geodeticToEcf ( geodeticCoords ) ; const sitex = site . x ; const sitey = site . y ; const sitez = site . z ; const slat = Math . sin ( lat ) ; const slon = Math . sin ( lon ) ; const clat = Math . cos ( lat ) ; const clon = Math . cos ( lon ) ; az *= DEG2RAD ; el *= DEG2RAD ; const south = - rng * Math . cos ( el ) * Math . cos ( az ) ; const east = rng * Math . cos ( el ) * Math . sin ( az ) ; const zenith = rng * Math . sin ( el ) ; const x = slat * clon * south + - slon * east + clat * clon * zenith + sitex ; const y = slat * slon * south + clon * east + clat * slon * zenith + sitey ; const z = - clat * south + slat * zenith + sitez ; return { x : x , y : y , z : z } ; } ; export const eci2ll = ( x , y , z ) : { lat : number ; lon : number ; alt : number ; } => { const { timeManager } = keepTrackApi . programs ; const now = timeManager . calculateSimulationTime ( ) ; const { gmst } = calculateTimeVariables ( now ) ; var latLon = satellite . eciToGeodetic ( { x : x , y : y , z : z } , gmst ) ; latLon . lat = latLon . lat * RAD2DEG ; latLon . lon = latLon . lon * RAD2DEG ; latLon . lon = latLon . lon > 180 ? latLon . lon - 360 : latLon . lon ; latLon . lon = latLon . lon < - 180 ? latLon . lon + 360 : latLon . lon ; return latLon ; } ; param { SatObject } sat param { SatObject } reference returns { { position : vec3 , velocity : vec3 } } export const sat2ric = ( sat , reference ) : { position : vec3 ; velocity : vec3 ; } => { const { position , velocity } = sat ; const r = vec3 . fromValues ( position . x , position . y , position . z ) ; const v = vec3 . fromValues ( velocity . x , velocity . y , velocity . z ) ; const ru = vec3 . normalize ( vec3 . create ( ) , r ) ; const h = vec3 . cross ( vec3 . create ( ) , r , v ) ; const cu = vec3 . normalize ( vec3 . create ( ) , h ) ; const iu = vec3 . cross ( vec3 . create ( ) , cu , ru ) ; const matrix = mat3 . fromValues ( ru [ 0 ] , iu [ 0 ] , cu [ 0 ] , ru [ 1 ] , iu [ 1 ] , cu [ 1 ] , ru [ 2 ] , iu [ 2 ] , cu [ 2 ] ) ; const { position : refPosition , velocity : refVelocity } = reference ; const dp = vec3 . sub ( vec3 . create ( ) , r , [ refPosition . x , refPosition . y , refPosition . z ] ) ; const dv = vec3 . sub ( vec3 . create ( ) , v , [ refVelocity . x , refVelocity . y , refVelocity . z ] ) ; return { position : vec3 . transformMat3 ( vec3 . create ( ) , dp , < ReadonlyMat3 > ( < unknown > matrix ) ) , velocity : vec3 . transformMat3 ( vec3 . create ( ) , dv , < ReadonlyMat3 > ( < unknown > matrix ) ) , } ; } ; export const getLlaTimeView = ( now , sat ) => { const { sensorManager } = keepTrackApi . programs ; const satrec = satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; const { m , gmst } = calculateTimeVariables ( now , satrec ) ; const positionEci = satellite . sgp4 ( satrec , m ) ; const gpos = satellite . eciToGeodetic ( positionEci . position , gmst ) ; const lat = satellite . degreesLat ( gpos . lat ) ; const lon = satellite . degreesLong ( gpos . lon ) ; const time = dateFormat ( now , 'isoDateTime' , true ) ; const positionEcf = satellite . eciToEcf ( positionEci . position , gmst ) ; const lookAngles = satellite . ecfToLookAngles ( sensorManager . currentSensor [ 0 ] . observerGd , positionEcf ) ; const az = lookAngles . az * RAD2DEG ; const el = lookAngles . el * RAD2DEG ; const rng = lookAngles . rng ; const inView = checkIsInView ( sensorManager . currentSensor [ 0 ] , { az , el , rng } ) ; return { lat , lon , time , inView } ; } ; export const map = ( sat , i ) : { time : string ; lat : number ; lon : number ; inView : boolean ; } => { const { timeManager } = keepTrackApi . programs ; const simulationTime = timeManager . calculateSimulationTime ( ) ; let offset = ( ( i * sat . period ) / 50 ) * 60 * 1000 ; const now = timeManager . getOffsetTimeObj ( offset , simulationTime ) ; return getLlaTimeView ( now , sat ) ; } ; export const calculateSensorPos = ( sensors ? ) : { x : number ; y : number ; z : number ; lat : number ; lon : number ; gmst : number ; } => { const { timeManager , sensorManager } = keepTrackApi . programs ; sensors = verifySensors ( sensors , sensorManager ) ; const sensor = sensors [ 0 ] ; const now = timeManager . calculateSimulationTime ( ) ; const { gmst } = calculateTimeVariables ( now ) ; const cosLat = Math . cos ( sensor . lat * DEG2RAD ) ; const sinLat = Math . sin ( sensor . lat * DEG2RAD ) ; const cosLon = Math . cos ( sensor . lon * DEG2RAD + gmst ) ; const sinLon = Math . sin ( sensor . lon * DEG2RAD + gmst ) ; return { x : ( RADIUS_OF_EARTH + PLANETARIUM_DIST ) * cosLat * cosLon , y : ( RADIUS_OF_EARTH + PLANETARIUM_DIST ) * cosLat * sinLon , z : ( RADIUS_OF_EARTH + PLANETARIUM_DIST ) * sinLat , gmst : gmst , lat : sensor . lat , lon : sensor . lon , } ; } ; export type TleParams = { sat : SatObject ; inc : StringifiedNubmer ; meanmo : StringifiedNubmer ; rasc : StringifiedNubmer ; argPe : StringifiedNubmer ; meana : StringifiedNubmer ; ecen : string ; epochyr : string ; epochday : string ; intl : string ; scc : string ; } ; export const createTle = ( tleParams ) : { TLE1 : string ; TLE2 : string ; } => { let { sat , inc , meanmo , rasc , argPe , meana , ecen , epochyr , epochday , intl , scc } = tleParams ; const incStr = formatInclination ( inc ) ; const meanmoStr = formatMeanMotion ( meanmo ) ; const rascStr = formatRightAscension ( rasc ) ; const argPeStr = formatArgumentOfPerigee ( argPe ) ; const meanaStr = formatMeanAnomaly ( meana ) ; const TLE1Ending = sat . TLE1 . substr ( 32 , 39 ) ; const TLE1 = '1 ' + scc + 'U ' + intl + ' ' + epochyr + epochday + TLE1Ending ; const TLE2 = '2 ' + scc + ' ' + incStr + ' ' + rascStr + ' ' + ecen + ' ' + argPeStr + ' ' + meanaStr + ' ' + meanmoStr + '    10' ; return { TLE1 , TLE2 } ; } ; const verifySensors = ( sensors , sensorManager ) : SensorObject [ ] => { if ( typeof sensors == 'undefined' || sensors == null ) { if ( typeof sensorManager . currentSensor == 'undefined' ) { throw new Error ( 'getTEARR requires a sensor or for a sensor to be currently selected.' ) ; } else { sensors = sensorManager . currentSensor ; } } if ( typeof sensors [ 0 ] . observerGd == 'undefined' ) { try { sensors [ 0 ] . observerGd = { alt : sensors [ 0 ] . alt , lat : sensors [ 0 ] . lat , lon : sensors [ 0 ] . lon , } ; } catch ( e ) { throw new Error ( 'observerGd is not set and could not be guessed.' ) ; } } return sensors ; } ; export const satellite = { sgp4 : Ootk . Sgp4 . propagate , gstime : Ootk . Sgp4 . gstime , twoline2satrec : Ootk . Sgp4 . createSatrec , geodeticToEcf : Ootk . Transforms . lla2ecf , ecfToEci : Ootk . Transforms . ecf2eci , eciToEcf : Ootk . Transforms . eci2ecf , eciToGeodetic : Ootk . Transforms . eci2lla , degreesLat : Ootk . Transforms . getDegLat , degreesLong : Ootk . Transforms . getDegLon , ecfToLookAngles : Ootk . Transforms . ecf2rae , altitudeCheck , calculateDops , calculateLookAngles , calculateSensorPos , calculateVisMag , checkIsInView , createTle , currentEpoch , distance , eci2ll , eci2Rae , findBestPass , findBestPasses , findCloseObjects , findClosestApproachTime , findNearbyObjectsByOrbit , getDops , getEci , getlookangles , getlookanglesMultiSite , getOrbitByLatLon , getRae , getSunTimes , getTEARR , isRiseSetLookangles : false , lastlooksArray : [ ] , lastMultiSiteArray : [ ] , lookAngles2Ecf , lookanglesInterval : 30 , lookanglesLength : 1 , map , nextNpasses , nextpass , nextpassList , obsmaxrange : 0 , obsminrange : 0 , sat2ric , setobs , setTEARR , updateDopsTable , currentTEARR : { time : '' , az : 0 , el : 0 , rng : 0 , name : '' , } , } ; window . satellite = satellite ; const populateMultiSiteTable = ( multiSiteArray ) => { const tbl = < HTMLTableElement > document . getElementById ( 'looksmultisite' ) ; tbl . innerHTML = '' ; let tr = tbl . insertRow ( ) ; let tdT = tr . insertCell ( ) ; tdT . appendChild ( document . createTextNode ( 'Time' ) ) ; tdT . setAttribute ( 'style' , 'text-decoration: underline' ) ; let tdR = tr . insertCell ( ) ; tdR . appendChild ( document . createTextNode ( 'Rng' ) ) ; tdR . setAttribute ( 'style' , 'text-decoration: underline' ) ; let tdA = tr . insertCell ( ) ; tdA . appendChild ( document . createTextNode ( 'Az' ) ) ; tdA . setAttribute ( 'style' , 'text-decoration: underline' ) ; let tdE = tr . insertCell ( ) ; tdE . appendChild ( document . createTextNode ( 'El' ) ) ; tdE . setAttribute ( 'style' , 'text-decoration: underline' ) ; let tdS = tr . insertCell ( ) ; tdS . appendChild ( document . createTextNode ( 'Sensor' ) ) ; tdS . setAttribute ( 'style' , 'text-decoration: underline' ) ; for ( let i = 0 ; i < multiSiteArray . length ; i ++ ) { if ( tbl . rows . length > 0 ) { for ( let r = 0 ; r < tbl . rows . length ; r ++ ) { const dateString = tbl . rows [ r ] . cells [ 0 ] . textContent ; const sYear = parseInt ( dateString . substr ( 0 , 4 ) ) ; const sMon = parseInt ( dateString . substr ( 5 , 2 ) ) - 1 ; const sDay = parseInt ( dateString . substr ( 8 , 2 ) ) ; const sHour = parseInt ( dateString . substr ( 11 , 2 ) ) ; const sMin = parseInt ( dateString . substr ( 14 , 2 ) ) ; const sSec = parseInt ( dateString . substr ( 17 , 2 ) ) ; const topTime = new Date ( sYear , sMon , sDay , sHour , sMin , sSec ) ; topTime . setUTCDate ( sDay ) ; topTime . setUTCHours ( sHour ) ; if ( new Date ( multiSiteArray [ i ] . time ) < topTime ) { tr = tbl . insertRow ( i ) ; break ; } } } if ( tr == null ) { tr = tbl . insertRow ( ) ; } tdT = tr . insertCell ( ) ; tdT . appendChild ( document . createTextNode ( dateFormat ( multiSiteArray [ i ] . time , 'isoDateTime' , true ) ) ) ; tdE = tr . insertCell ( ) ; tdE . appendChild ( document . createTextNode ( multiSiteArray [ i ] . el . toFixed ( 1 ) ) ) ; tdA = tr . insertCell ( ) ; tdA . appendChild ( document . createTextNode ( multiSiteArray [ i ] . az . toFixed ( 0 ) ) ) ; tdR = tr . insertCell ( ) ; tdR . appendChild ( document . createTextNode ( multiSiteArray [ i ] . rng . toFixed ( 0 ) ) ) ; tdS = tr . insertCell ( ) ; tdS . appendChild ( document . createTextNode ( multiSiteArray [ i ] . name ) ) ; } } ;