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