DEV Community

Omar Shehata
Omar Shehata

Posted on

Create a satellite tracker from scratch in 30 lines of JavaScript

This tutorial will walk you through how to create a web app that visualizes the location of any satellite in real-time, like the International Space Station.

We're going to do this from scratch, using the same techniques a real rocket scientist would!

  1. We'll look at where to get the data about individual satellites that the government publishes, known as Two-Line Element Sets, or TLE's.
  2. We'll use satellite-js to predict the orbit of the satellite given the TLE's (this is the rocket science part).
  3. We'll use CesiumJS to visualize the result, but you can use any library/engine that can take in longitude, latitude, and height.

Below is a preview of the final result.

This is showing the path of the International Space Station, sped up by 40x. To see its current location in real-time, click the clock icon at the top-left of the clock wheel.

Here's a direct link to the app. And the source code on Glitch.

1 - Get the satellite's Two-Line Element Set

A Two-Line Element Set, or TLE, is a data format that describes the motion of an object orbiting the Earth. It was created by the North American Aerospace Defense Command (NORAD). You can read more about it and its history here.

Given this description of the orbit, we can predict the location of where it's going to be at any moment in time (which is step 2 below).

This means that most "live" satellite trackers are not live in the same way that tracking a delivery car on a map is. Instead of relying on constantly receiving position updates, those who track objects in space will often get the latest TLE's (which are regularly updated) and use that to predict where the object is right now.

Where do we get the TLE's? There is no one global official registry. Whoever owns the satellite and is monitoring it is responsible for updating and publishing the TLE for the benefit of the global space community (unless it's a spy satellite).

We can find these TLE's on Space Track which is a registry run by the United States Space Command.

Another source is this list on CeleStrak maintained by Dr. T.S. Kelso.

We're going to use CeleStrak since it doesn't require a login. To find the TLE for the International Space Station, click on the Space Stations link.

The first one is the TLE for the ISS:

ISS (ZARYA)             
1 25544U 98067A   21122.75616700  .00027980  00000-0  51432-3 0  9994
2 25544  51.6442 207.4449 0002769 310.1189 193.6568 15.48993527281553
Enter fullscreen mode Exit fullscreen mode

The meaning of these numbers is listed in table 1 in Dr T.S. Kelso's column. Most of them are identifiers and metadata about the satellite, like when it was launched.

You can find TLE's for weather satellites, GPS satellites, and even SpaceX's Starlink constellation in this same format.

2 - Predict the satellite orbit

Now that you know how to get the TLE of the object you're interested in tracking, the next step is converting that to a position in time.

We're going to use satellite-js for this.

Include the library from a CDN:

<script src="https://cdnjs.cloudflare.com/ajax/libs/satellite.js/4.0.0/satellite.min.js"></script>
Enter fullscreen mode Exit fullscreen mode

Then pass the TLE to it, and a time:

const ISS_TLE = 
    `1 25544U 98067A   21122.75616700  .00027980  00000-0  51432-3 0  9994
     2 25544  51.6442 207.4449 0002769 310.1189 193.6568 15.48993527281553`;
// Initialize the satellite record with this TLE
const satrec = satellite.twoline2satrec(
  ISS_TLE.split('\n')[0].trim(), 
  ISS_TLE.split('\n')[1].trim()
);
// Get the position of the satellite at the given date
const date = new Date();
const positionAndVelocity = satellite.propagate(satrec, date);
const gmst = satellite.gstime(date);
const position = satellite.eciToGeodetic(positionAndVelocity.position, gmst);

console.log(position.longitude);// in radians
console.log(position.latitude);// in radians
console.log(position.height);// in km
Enter fullscreen mode Exit fullscreen mode

Now we have the position of the satellite at the current time, new Date().

This position is produced as a result of simulating a specific model of satellite motion. This model is called SGP4/SDP4. All TLE's assume this specific model.

If you're wondering about the accuracy of this model, the short answer is, it depends.

Accuracy of the two-line element sets is dependent upon a number of factors. These range from the particular sensors used and amount of data collected to the type of orbit and condition of the space environment. Unfortunately, since these factors vary for each element set, so does the accuracy. While NORAD has experimented with methods to incorporate prediction quality into the element sets, none of these methods has yet proved successful.

3 - Visualize the result

Now we have a way to get the location of any satellite, at any given time. We can pass in future times to animate its path, which we'll do in the next step.

First, let's see how to visualize an individual point in space using CesiumJS.

We load the library from CDN:

<script src="https://cesium.com/downloads/cesiumjs/releases/1.81/Build/Cesium/Cesium.js"></script>
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.81/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
Enter fullscreen mode Exit fullscreen mode

And create a container element:

<div id="cesiumContainer"></div>
Enter fullscreen mode Exit fullscreen mode

We then initialize the viewer. Here we pass in some extra options to disable functionality that requires an access token:

// Initialize the Cesium viewer.
const viewer = new Cesium.Viewer('cesiumContainer', {
  imageryProvider: new Cesium.TileMapServiceImageryProvider({
    url: Cesium.buildModuleUrl("Assets/Textures/NaturalEarthII"),
  }),
  baseLayerPicker: false, geocoder: false, homeButton: false, infoBox: false,
  navigationHelpButton: false, sceneModePicker: false
});
viewer.scene.globe.enableLighting = true;
Enter fullscreen mode Exit fullscreen mode

Finally, we'll visualize the satellite position as a red dot in space:

const satellitePoint = viewer.entities.add({
  position: Cesium.Cartesian3.fromRadians(
    position.longitude, position.latitude, position.height * 1000
  ),
  point: { pixelSize: 5, color: Cesium.Color.RED }
});
Enter fullscreen mode Exit fullscreen mode

See the complete source code of this step in simple.html on Glitch.

4 - Animate the path

To animate the path, we just need to sample more positions in the future. CesiumJS has a built-in way to interpolate between these samples over time.

The setup for this is a bit verbose. You can see the full code on Glitch. The important concepts are described below.

We create a SampledPositionProperty. This is an object that will hold position samples over time and will interpolate between them:

const positionsOverTime = new Cesium.SampledPositionProperty();
Enter fullscreen mode Exit fullscreen mode

We loop through however many samples we want to get, and for each sample, we construct a time object, called JulianDate in CesiumJS, and a position, and we add that as a sample:

for (let i = 0; i < totalSeconds; i+= timestepInSeconds) {
  const time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
  // ...Get position from satellite-js...
  const position = Cesium.Cartesian3.fromRadians(p.longitude, p.latitude, p.height * 1000);
  positionsOverTime.addSample(time, position);
}
Enter fullscreen mode Exit fullscreen mode

Finally, we pass this positionsOverTime object to our point.

const satellitePoint = viewer.entities.add({
  position: positionsOverTime,
  point: { pixelSize: 5, color: Cesium.Color.RED }
});
Enter fullscreen mode Exit fullscreen mode

The point will move as the timeline at the bottom moves. To attach the camera to the moving point we do:

viewer.trackedEntity = satellitePoint;
Enter fullscreen mode Exit fullscreen mode

Conclusion

I hope you enjoyed learning a little bit about what goes into building a satellite tracker. There's a lot more to the topic we didn't touch on, like what exactly do the parameters in the TLE mean? How often are they updated? How are they updated?

I don't know, but I find it really empowering to know what formats this kind of data is published in & where to get it, and quite amazing that we can do all this directly in the browser with JavaScript!

Here's a couple fun ideas to explore now that we can do this:

Visualize multiple satellites, like the entire Starlink constellation. Inspired by Celestrak's viewer which shows every satellite in its catalog. Perhaps visualize how the number of Starlink satellites grew over time?

Or simulate what it would look from street level. Maybe add buildings/elevation data to find the best place in the city to see the satellite?

There's a prototype of this in street-level.html in the Glitch source code. Demo: https://satellite-viewer.glitch.me/street-level.html.

satellite-street

See also James Darpinian's "See a satellite tonight" app which uses a combination of CesiumJS and Google street view.

It might also be fun to use a 3D model of the right scale instead of a dot, and get a real sense of how close satellites get to each other in space.

Top comments (1)

Collapse
 
lacoste profile image
Lacoste

Hi Omar Shehata, i like your project, i went through it and it is awersome. nice job