# Distance between GRACE-FO 1 and 2

I calculated the distance between the two satellites by propagating some TLEs with the SGP4. Here’s the result:

the solid lines are for TLEs 19197.90524586 (GRACE-FO 1) / 19197.90551489 (GRACE-FO 2) and the dashed lines are for TLEs 19195.14720185 / 19195.14747067.

I thought that the distance should be kept strictly constant, but instead there are variations of about 1 km.

Does anyone know the true distance? Do those variations depend on the inaccuracy of the TLEs?

I thought that the distance should be kept strictly constant...

Not strictly, the satellites are allowed to orbit and the variation in the distance between them is one particularly sensitive measure of the variations in Earth's gravity field. This variation in distance is one key part of the raw data provided by the pair.

As I show below, if you have two spacecraft at 490 km orbiting at a distance of 170 km, then they are going to naturally drift closer and farther from each other during each orbit by about 1 kilometer due to variations in Earth's gravity. This is how Grace works!

How it works

GRACE-FO's raw data will be a series of measurements showing how far apart two satellites are from each other. The twin satellites follow each other in orbit around the Earth, separated by about 137 miles (220 km). They will constantly send microwave signals to each other to measure the distance between them.

As the pair circles the Earth, areas of slightly stronger gravity (greater mass concentration) affect the lead satellite first, pulling it away from the trailiing satellite. As the satellites continue, the trailing satellite is pulled toward the lead satellite as it passes over the gravity anomaly. The change in distance would certainly be imperceptible to our eyes, but the extremely precise microwave ranging system on GRACE-FO is designed to detect minuscule changes in the distance between the satellites.

As far as

...but instead there are variations of about 1 km... Do those variations depend on the inaccuracy of the TLEs?

If you are propagating TLEs using SGP4 then you are using a relatively simple model (the "S" or "simplified" in the name) for Earth's gravity. SGP4 can potentially use different models, most likely you will be using WGS-72 or WGS-84 (see for examplethis).

Per Wikipedia's Simplified_perturbations_models

The SGP4 model has an error ~1 km at epoch and grows at ~1–3 km per day. This data is updated frequently in NASA and NORAD sources due to this error.

@Tristan's authoritative answer describes the situation much better and his other answer puts the best error (near epoch) about 0.6 km, so +/-0.6 is 1.2 km.

So if you are seeing ~1 km variation, that could be real, or it could be error or of course it could be some combination of both!

Does anyone know the true distance?

Yes, people who are well versed in reading GRACE-FO data sets know it quite accurately (historically, after analysis). There are at least three ways to get this distance:

1. propagate TLEs (what we are talking about here)
2. propagate using much more accurate models and more accurate data
3. use data from GRACE-FO and analyze it

It's only #1 that we can do easily.

## So let's do an experiment!

Here's the current TLE for GRACE-FO 1 from Celestrak

GRACE-FO 1
1 43476U 18047A   19198.89026106  .00000165  00000-0  54608-5 0  9994
2 43476  88.9993 182.7845 0020328 105.7963 254.5528 15.23884137 64118


Line 2 says there are about 15.23884137 orbits per 86400 seconds, so the period is about 5670 seconds and the velocity is about 7611 m/s. That means your 171 km puts them about 23 seconds or 0.00027 days apart. We can make a fake TLE by adding 0.00027 to the epoch day number and propagate it, and see what the satellite separation does.

By changing only the epoch, and only by 23 seconds, all we are doing is rotating the Earth underneath the otherwise-identical TLE-specified orbit, by about 171 kilometers, and the only thing that does is rotate Earth's gravity field (modeled) by about 1.5 degrees.

It turns out that that is all that's necessary to produce a variation in the separation of ~1 km.

Answer: Under normal circumstances runout between SGP4 propagation of two different TLEs of only ~1 km would just be ignored and assumed to be error due to SGP's approximations and errors in TLEs. However, even if the TLEs and SGP were perfect (which they can never be) there would still be a variation of about 1 km during each orbit due to real gravitational variation.

You have several sources that can cause this kind of variation, it is impossible to say how much of each one is necessary to agree with your plot. I've used identical orbits and just moved the Earth. You are using two different TLEs and each one of those can have additional and uncorrelated noise and error.

For fun only, I took the 2nd and 3rd differences (approximate numerical higher derivatives) to show higher order "wiggles" during the orbit.

Python 3 script:

# https://celestrak.com/satcat/
TLE_real = """Real GRACE-FO 1
1 43476U 18047A   19198.89026106  .00000165  00000-0  54608-5 0  9994
2 43476  88.9993 182.7845 0020328 105.7963 254.5528 15.23884137 64118"""

TLE_fake = """Fake GRACE-FO 1
1 43476U 18047A   19198.89052000  .00000165  00000-0  54608-5 0  9994
2 43476  88.9993 182.7845 0020328 105.7963 254.5528 15.23884137 64118"""

import numpy as np
import matplotlib.pyplot as plt
from skyfield.api import Topos, Loader, EarthSatellite

earth = de421['earth']

minutes = np.arange(201.)
times   = ts.utc(2019, 7, 18, 0, minutes)

name, L1, L2 = TLE_real.splitlines()
realsat = EarthSatellite(L1, L2)

name, L1, L2 = TLE_fake.splitlines()
fakesat = EarthSatellite(L1, L2)

realpos = realsat.at(times).position.km
fakepos = fakesat.at(times).position.km

separation = np.sqrt(((realpos-fakepos)**2).sum(axis=0))  # kilometers
change     = separation[1:]-separation[:-1]    # kilometers per minute
range_rate = change * 1E+06 / 60    # millilmeters per second

second     = (range_rate[1:] - range_rate[:-1]) / 60.
third      = (second[1:] - second[:-1]) / 60.
if True:
plt.figure()
plt.subplot(2, 1, 1)
plt.plot(minutes, separation)
# plt.xlabel('minutes', fontsize=14)
plt.ylabel('separation (km)', fontsize=14)
plt.suptitle('real GRACE-FO 1 versus fake GRACE-FO 1 (epoch + 23 sec)', fontsize=14)
plt.subplot(2, 1, 2)
plt.plot(minutes[:-1], range_rate)
plt.xlabel('minutes', fontsize=14)
plt.ylabel('range rate (mm/sec)', fontsize=14)
plt.show()

if True:
plt.figure()
plt.subplot(2, 1, 1)
plt.suptitle('real GRACE-FO 1 versus fake GRACE-FO 1 (epoch + 23 sec)', fontsize=14)
plt.plot(minutes[1:-1], second)
# plt.xlabel('minutes', fontsize=14)
plt.ylabel('second diff (mm/sec^2)', fontsize=14)
plt.subplot(2, 1, 2)
plt.plot(minutes[1:-2], third)
plt.xlabel('minutes', fontsize=14)
plt.ylabel('third diff (mm/sec^3)', fontsize=14)
plt.show()