import { RAD2DEG } from '@app/js/lib/constants' ; import { stringPad } from '@app/js/lib/helpers' ; import { SatRec } from 'satellite.js' ; import { SatObject } from '../api/keepTrackTypes' ; import { calculateTimeVariables , satellite } from './satMath' ; enum PropagationOptions { MeanAnomaly = 1 , RightAscensionOfAscendingNode = 2 , ArgumentOfPerigee = 3 } enum PropagationResults { Near = 0 , Success = 1 , Error = 2 , Far = 3 } export const getOrbitByLatLon = ( sat , goalLat , goalLon , goalDirection , now , goalAlt ? , raanOffset ? ) : [ string , string ] => { let newMeana = null ; let newArgPer = null ; goalAlt = goalAlt || null ; raanOffset = raanOffset || 0 ; let lastLat = null ; let currentDirection ; const intl = sat . TLE1 . substr ( 9 , 8 ) ; const epochyr = sat . TLE1 . substr ( 18 , 2 ) ; const epochday = sat . TLE1 . substr ( 20 , 12 ) ; const meanmo = sat . TLE2 . substr ( 52 , 11 ) ; const inc = stringPad . pad0 ( ( sat . inclination * RAD2DEG ) . toPrecision ( 7 ) , 8 ) ; const ecen = sat . eccentricity . toPrecision ( 7 ) . substr ( 2 , 7 ) ; const TLE1Ending = sat . TLE1 . substr ( 32 , 39 ) ; param { number } meana returns { PropagationResults } const meanaCalc = ( meana ) => { let satrec = < SatRec > satellite . twoline2satrec ( sat . TLE1 , sat . TLE2 ) ; meana = meana / 10 ; const meanaStr = stringPad . pad0 ( meana . toPrecision ( 7 ) , 8 ) ; const raan = stringPad . pad0 ( ( sat . raan * RAD2DEG ) . toPrecision ( 7 ) , 8 ) ; const argPe = newArgPer ? stringPad . pad0 ( ( parseFloat ( newArgPer ) / 10 ) . toPrecision ( 7 ) , 8 ) : stringPad . pad0 ( ( sat . argPe * RAD2DEG ) . toPrecision ( 7 ) , 8 ) ; const TLE1Ending = sat . TLE1 . substr ( 32 , 39 ) ; const TLE1 = '1 ' + sat . sccNum + 'U ' + intl + ' ' + epochyr + epochday + TLE1Ending ; const TLE2 = '2 ' + sat . sccNum + ' ' + inc + ' ' + raan + ' ' + ecen + ' ' + argPe + ' ' + meanaStr + ' ' + meanmo + '    10' ; satrec = satellite . twoline2satrec ( TLE1 , TLE2 ) ; const results = getOrbitByLatLonPropagate ( now , satrec , PropagationOptions . MeanAnomaly ) ; if ( results === PropagationResults . Success ) { sat . TLE1 = TLE1 ; sat . TLE2 = TLE2 ; newMeana = meanaStr ; } return results ; } ; param { string } argPe returns { PropagationResults } const argPerCalc = ( argPe ) : PropagationResults => { const meana = newMeana ; const raan = stringPad . pad0 ( ( sat . raan * RAD2DEG ) . toPrecision ( 7 ) , 8 ) ; argPe = stringPad . pad0 ( ( parseFloat ( argPe ) / 10 ) . toPrecision ( 7 ) , 8 ) ; const TLE1 = '1 ' + sat . sccNum + 'U ' + intl + ' ' + epochyr + epochday + TLE1Ending ; const TLE2 = '2 ' + sat . sccNum + ' ' + inc + ' ' + raan + ' ' + ecen + ' ' + argPe + ' ' + meana + ' ' + meanmo + '    10' ; const satrec = < SatRec > satellite . twoline2satrec ( TLE1 , TLE2 ) ; const results = getOrbitByLatLonPropagate ( now , satrec , PropagationOptions . ArgumentOfPerigee ) ; if ( results === PropagationResults . Success ) { sat . TLE1 = TLE1 ; sat . TLE2 = TLE2 ; newArgPer = argPe ; } return results ; } ; param { number } raan param { number } raanOffset returns { PropagationResults } const raanCalc = ( raan , raanOffset ) : PropagationResults => { const origRaan = raan ; raan = raan / 100 ; raan = raan > 360 ? raan - 360 : raan ; const raanStr = stringPad . pad0 ( raan . toPrecision ( 7 ) , 8 ) ; const argPe = newArgPer ? stringPad . pad0 ( ( parseFloat ( newArgPer ) / 10 ) . toPrecision ( 7 ) , 8 ) : stringPad . pad0 ( ( sat . argPe * RAD2DEG ) . toPrecision ( 7 ) , 8 ) ; const TLE1 = '1 ' + sat . sccNum + 'U ' + intl + ' ' + epochyr + epochday + TLE1Ending ; const TLE2 = '2 ' + sat . sccNum + ' ' + inc + ' ' + raanStr + ' ' + ecen + ' ' + argPe + ' ' + newMeana + ' ' + meanmo + '    10' ; const satrec = < SatRec > satellite . twoline2satrec ( TLE1 , TLE2 ) ; const results = getOrbitByLatLonPropagate ( now , satrec , PropagationOptions . RightAscensionOfAscendingNode ) ; if ( results === PropagationResults . Success ) { raan = origRaan / 100 + raanOffset ; raan = raan > 360 ? raan - 360 : raan ; raan = raan < 0 ? raan + 360 : raan ; const raanStr = stringPad . pad0 ( raan . toPrecision ( 7 ) , 8 ) ; const TLE2 = '2 ' + sat . sccNum + ' ' + inc + ' ' + raanStr + ' ' + ecen + ' ' + argPe + ' ' + newMeana + ' ' + meanmo + '    10' ; sat . TLE1 = TLE1 ; sat . TLE2 = TLE2 ; } return results ; } ; const getOrbitByLatLonPropagate = ( now , satrec , type ) : PropagationResults => { const { m , gmst } = calculateTimeVariables ( now , satrec ) ; const positionEci = satellite . sgp4 ( satrec , m ) ; const gpos = satellite . eciToGeodetic ( positionEci . position , gmst ) ; let { lat , lon , alt } = gpos ; lat = satellite . degreesLat ( lat ) ; lon = satellite . degreesLong ( lon ) ; lastLat = lastLat ? lastLat : lat ; if ( type === PropagationOptions . MeanAnomaly ) { if ( lat === lastLat ) { return 0 ; } if ( lat > lastLat ) { currentDirection = 'N' ; } if ( lat < lastLat ) { currentDirection = 'S' ; } lastLat = lat ; } if ( type === PropagationOptions . MeanAnomaly && lat > goalLat - 0.15 && lat < goalLat + 0.15 ) { return PropagationResults . Success ; } if ( type === PropagationOptions . RightAscensionOfAscendingNode && lon > goalLon - 0.15 && lon < goalLon + 0.15 ) { return PropagationResults . Success ; } if ( type === PropagationOptions . ArgumentOfPerigee && alt > goalAlt - 30 && alt < goalAlt + 30 ) { return PropagationResults . Success ; } if ( type === PropagationOptions . MeanAnomaly && ! ( lat > goalLat - 11 && lat < goalLat + 11 ) ) { return PropagationResults . Far ; } if ( type === PropagationOptions . RightAscensionOfAscendingNode && ! ( lon > goalLon - 11 && lon < goalLon + 11 ) ) { return PropagationResults . Far ; } if ( type === PropagationOptions . ArgumentOfPerigee && ( alt < goalAlt - 100 || alt > goalAlt + 100 ) ) { return PropagationResults . Far ; } return PropagationResults . Near ; } ; let argPerCalcResults ; let meanACalcResults ; let raanCalcResults ; /** Rotate Mean Anomaly 0.1 Degree at a Time for Up To 520 Degrees */ for ( let i = 0 ; i < 520 * 10 ; i += 1 ) { meanACalcResults = meanaCalc ( i ) ; if ( meanACalcResults === PropagationResults . Success ) { if ( currentDirection !== goalDirection ) { i += 20 ; } else { break ; } } if ( meanACalcResults === PropagationResults . Far ) { i += 100 ; } } if ( meanACalcResults !== PropagationResults . Success ) { return [ 'Error' , 'Failed to find a solution for Mean Anomaly' ] ; } if ( goalAlt ) { meanACalcResults = 0 ; for ( let i = 0 ; i < 360 * 10 ; i += 1 ) { /** Rotate ArgPer 0.1 Degree at a Time for Up To 400 Degrees */ argPerCalcResults = argPerCalc ( i . toString ( ) ) ; if ( argPerCalcResults === PropagationResults . Success ) { if ( meanACalcResults === PropagationResults . Success ) { if ( currentDirection === goalDirection ) { break ; } } else { } } else { } if ( argPerCalcResults === PropagationResults . Far ) { i += 5 * 10 ; } if ( argPerCalcResults === PropagationResults . Error ) { return [ 'Error' , 'Failed to find a solution for Argument of Perigee' ] ; } for ( let j = 0 ; j < 520 * 10 ; j += 1 ) { /** Rotate Mean Anomaly 0.1 Degree at a Time for Up To 400 Degrees */ meanACalcResults = meanaCalc ( j ) ; if ( meanACalcResults === PropagationResults . Success ) { if ( currentDirection !== goalDirection ) { j = j + 20 ; } else { break ; } } j = meanACalcResults === PropagationResults . Far ? j + 100 : j ; if ( meanACalcResults === PropagationResults . Error ) { return [ 'Error' , '' ] ; } } } if ( argPerCalcResults !== PropagationResults . Success ) { return [ 'Error' , 'Failed to find a solution for Argument of Perigee' ] ; } } for ( let i = 0 ; i < 520 * 100 ; i += 1 ) { raanCalcResults = raanCalc ( i , raanOffset ) ; if ( raanCalcResults === PropagationResults . Success ) { break ; } if ( raanCalcResults === PropagationResults . Far ) { i += 10 * 100 ; } } if ( raanCalcResults !== PropagationResults . Success ) { return [ 'Error' , 'Failed to find a solution for Right Ascension of Ascending Node' ] ; } return [ sat . TLE1 , sat . TLE2 ] ; } ;