Satellite (satellite tracking)
The Satellite class provides SGP4/SDP4 orbital propagation for tracking satellites using TLE (Two-Line Element) data. It computes real-time and future positions, look angles from a ground observer, pass predictions, and Doppler estimations.
class Satellite {
constructor Satellite( string name );
// TLE management
bool setTLE( string line1, string line2 );
number getNorad();
// Position and observation
object getPosition( [number offset_seconds] );
object getLookAngle( Observer observer, [number offset_seconds] );
// Pass prediction
array predictPasses( Observer observer, number hours_ahead );
object getPassDetails( Observer observer, number offset_seconds, number length_seconds );
// Doppler
object getDopplerEstimation( Observer observer, number center_freq_hz, [number offset_seconds] );
compensateDoppler( Observer observer, IQData data );
// Illumination
object getIllumination( [number offset_seconds] );
// Visibility
boolean waitInView( Observer observer, [number timeout_ms], [number min_elevation] );
}
.setTLE
Set the two TLE lines for this satellite. Must be called before any position or pass computation.
.setTLE( line1, line2 );
- line1 : string, first TLE line
- line2 : string, second TLE line
Returns boolean indicating whether the TLE was valid and accepted.
var ISS = new Satellite('ISS');
var loaded = ISS.setTLE('1 25544U 98067A 20262.17917074 .00000067 00000-0 93590-5 0 9992',
'2 25544 51.6432 248.2943 0000854 102.9022 344.0032 15.48951196246432');
.getNorad
Get the NORAD catalog number from the loaded TLE.
.getNorad();
Returns number the NORAD ID.
.getPosition
Returns the current geodetic position of the satellite. To get a future position, provide an optional offset in seconds.
.getPosition( [offset_seconds] );
- offset_seconds : (optional) number, seconds in the future from now
Returns an object:
{
"latitude": number,
"longitude": number,
"altitude": number
}
- latitude : degrees
- longitude : degrees
- altitude : meters
var ISS = new Satellite('ISS');
ISS.setTLE('1 25544U 98067A 20262.17917074 .00000067 00000-0 93590-5 0 9992',
'2 25544 51.6432 248.2943 0000854 102.9022 344.0032 15.48951196246432');
var pos = ISS.getPosition();
print(JSON.stringify(pos));
// Position 60 seconds from now
var futurePos = ISS.getPosition(60);
.getLookAngle
Returns observation conditions for the satellite from a given observer, either now or at an optional time offset.
.getLookAngle( observer, [offset_seconds] );
- observer : Observer object
- offset_seconds : (optional) number, seconds in the future from now
Returns an object:
{
"azimuth": number,
"elevation": number,
"range": number,
"in_view": boolean
}
- azimuth : degrees
- elevation : degrees
- range : meters
- in_view : true if elevation > 0
.predictPasses
Predict the passages of the satellite for the given observer, up to hours_ahead hours in the future.
.predictPasses( observer, hours_ahead );
- observer : Observer object
- hours_ahead : number, prediction window in hours
Returns an array of objects:
[
{
"timestamp": number,
"aos": "dd/mm/yyyy hh:MM:ss",
"aos_secs": number,
"los": "dd/mm/yyyy hh:MM:ss",
"los_secs": number,
"max_elevation": number,
"pass_duration": number
}
]
- timestamp : AOS time in milliseconds since 01/01/1970
- aos : date/time string of Acquisition Of Signal
- aos_secs : seconds from now to AOS
- los : date/time string of Loss Of Signal
- los_secs : seconds from now to LOS
- max_elevation : maximum elevation in degrees during the pass
- pass_duration : duration of the pass in seconds
var home = new Observer('home');
home.setPosition( { 'longitude' : 0.029079, 'latitude' : 45.643868, 'asl' : 163 } );
var ISS = new Satellite('ISS');
ISS.setTLE('1 25544U 98067A 20262.17917074 .00000067 00000-0 93590-5 0 9992',
'2 25544 51.6432 248.2943 0000854 102.9022 344.0032 15.48951196246432');
var passes = ISS.predictPasses( home, 24.0 );
print( JSON.stringify( passes ));
.getPassDetails
Provide detailed per-second tracking data for a pass occurring at a given time offset and for a given duration.
.getPassDetails( observer, offset_seconds, length_seconds );
- observer : Observer object
- offset_seconds : number, seconds from now to the start of the pass
- length_seconds : number, duration to compute in seconds
Returns an object with the following structure:
{
"data_type": "satellite_pass",
"name": string,
"norad": number,
"pass_start_time": "yyyy/mm/dd hh:MM:ss",
"pass_end_time": "yyyy/mm/dd hh:MM:ss",
"observer": {
"latitude": number,
"longitude": number,
"altitude": number,
"observer_name": string
},
"event_count": number,
"pass_events": [
{
"when": "yyyy/mm/dd hh:MM:ss",
"ts": number,
"dt": number,
"az": number,
"el": number,
"range": number,
"dopp1Ghz": number,
"eclipsed": boolean
}
]
}
- name : satellite name
- norad : NORAD catalog number
- pass_start_time / pass_end_time : date/time strings for the computed window
- observer : observer position (latitude/longitude in degrees, altitude in meters)
- event_count : number of entries in pass_events
- pass_events : array with one entry per second:
- when : date/time string
- ts : timestamp in milliseconds since 01/01/1970
- dt : seconds since first element (relative time offset)
- az : azimuth in degrees
- el : elevation in degrees
- range : distance from observer in meters
- dopp1Ghz : Doppler shift assuming a 1 GHz transmit frequency
- eclipsed : true if the satellite is in Earth's shadow at this time
// Display 5 minutes of a pass occurring 4800 seconds from now
var pass = ISS.getPassDetails( home, 4800, 300 );
print(JSON.stringify(pass));
.getDopplerEstimation
Estimate the Doppler shift for a satellite at a given frequency, as seen from an observer.
Doppler is derived from the analytical range rate provided by the SGP4 look-angle computation, which uses both position and velocity vectors of the satellite and the observer. The observer's Earth-rotation velocity is accounted for at each step. The computation is performed over a 5-second window with 500 ms steps (10 values). The output provides average, min, and max Doppler over that interval.
.getDopplerEstimation( observer, center_freq_hz, [offset_seconds] );
- observer : Observer object
- center_freq_hz : number, center frequency in Hz
- offset_seconds : (optional) number, seconds in the future from now (default: 0)
Returns an object:
{
"doppler_min": number,
"doppler_max": number,
"doppler_avg": number
}
- doppler_min : minimum Doppler in Hz over the interval
- doppler_max : maximum Doppler in Hz over the interval
- doppler_avg : average Doppler in Hz over the interval
var ISS = new Satellite('ISS');
ISS.setTLE('1 25544U 98067A 20262.17917074 .00000067 00000-0 93590-5 0 9992',
'2 25544 51.6432 248.2943 0000854 102.9022 344.0032 15.48951196246432');
var home = new Observer('home');
home.setPosition( { 'longitude' : 1.829079, 'latitude' : 48.643868, 'asl' : 163 } );
// Estimate doppler now
var doppnow = ISS.getDopplerEstimation( home, 437.0e6 );
print( JSON.stringify( doppnow ));
// Doppler at T + 5 seconds
var futuredopp = ISS.getDopplerEstimation( home, 437.0e6, 5 );
print( JSON.stringify( futuredopp ));
.compensateDoppler
Apply Doppler compensation to an IQ data block based on the satellite's current motion relative to the observer. This modifies the IQ data in place.
.compensateDoppler( observer, iqdata );
- observer : Observer object
- iqdata : IQData object containing the samples to compensate
Returns nothing.
var IQ = rx.Capture(8192);
ISS.compensateDoppler( home, IQ );
.getIllumination
Check whether the satellite is sunlit or in Earth's shadow (eclipsed). Uses a cylindrical Earth shadow model: the satellite is considered eclipsed when it is on the night side of the Earth and its perpendicular distance to the Earth-Sun line is less than the Earth's radius.
This is useful for optical observers (a satellite must be sunlit to be visible), and for estimating whether a solar-powered satellite has its transponder active.
.getIllumination( [offset_seconds] );
- offset_seconds : (optional) number, seconds in the future from now (default: 0)
Returns an object:
{
"sunlit": boolean,
"depth": number
}
- sunlit : true if the satellite is illuminated by the Sun
- depth : normalized shadow depth, 0 = at shadow edge or sunlit, 1 = deepest shadow (passing through Earth center line)
var status = ISS.getIllumination();
if (status.sunlit) {
print('ISS is sunlit');
} else {
print('ISS is in shadow, depth: ' + status.depth);
}
// Check illumination 10 minutes from now
var future = ISS.getIllumination(600);
.waitInView
Wait for the satellite to be visible from the observer location. Visible means elevation above the horizon (0 degrees), or above a specified minimum elevation.
If no timeout is given, the function returns immediately with the current visibility status.
.waitInView( observer, [timeout_ms], [min_elevation] );
- observer : Observer object
- timeout_ms : (optional) number, maximum wait time in milliseconds
- min_elevation : (optional) number, minimum elevation angle in degrees (default: 0)
Returns boolean : true if the satellite is in view, false if the timeout expired.
// Wait up to 5 seconds for ISS to be above the horizon
while( ISS.waitInView( home, 5000 ) == false ) {
print('not yet...');
var currentPosition = ISS.getPosition();
print('Distance (km) : ' + currentPosition.altitude / 1e3);
}
// Check visibility immediately (no wait)
var visible = ISS.waitInView( home );
// Check for visibility above 10 degrees elevation
while( ISS.waitInView( home, 0, 10 ) == false ) {
print('not yet...');
}
Complete example
Predict next passes for the ISS:
var home = new Observer('home');
home.setPosition( { 'longitude' : 1.833807, 'latitude' : 48.650696, 'asl' : 207 } );
var found = false;
var tleData;
var ISS;
var satlist = TLE.loadTLE('https://www.celestrak.com/NORAD/elements/amateur.txt');
print('We have loaded ' + satlist.length + ' sat definitions.');
for (var j = 0; j < satlist.length; j++) {
tleData = satlist[j];
if (tleData.name == "ISS (ZARYA)") {
ISS = new Satellite(tleData.name);
ISS.setTLE( tleData.L1, tleData.L2 );
print(tleData.name);
found = true;
break;
}
}
if (found == false) {
print('Could not load TLE from this dataset.');
exit();
}
var passes = ISS.predictPasses( home, 96 ); // predict passes for the next 4 days
for (var i = 0; i < passes.length; i++) {
var next = passes[i];
print('--------------------------------------------------------------------------------');
print('Pass #' + i);
print(' AOS : ' + next.aos + ', LOS : ' + next.los + ', duration: ' + next.pass_duration + ' seconds');
print(' MAX Elev : ' + next.max_elevation);
}
Last update: April 8, 2026