Orbit Determination with Dormand Prince

I am trying to understand how Orbit Determination should work, along with the covariance matrix generated. The test I had set up uses Beidou’s high precision ephemerides as inputs (position only) into the propagator. I also set the initial guess to be the very first state in the observation. My expectation was that the orbit generated would be almost identical to the initial guess since there aren’t any noise in the data itself. Therefore, the residual at the first guess should almost be 0. This was the case though, and now I am wondering if there are gaps in my understanding of Orbit determination itself. I have my code attached below, which is mostly taken from the tutorial.
Another point of issue is the STD that goes in the measurement. The value of the STD determines the covariance matrix which makes sense in theory, but when I input the actual STD of my observation, the covariance matrix generated feels too large in my opinion, but I don’t have a way to verify this.

public OrekitCovariancePropagation(List<SpacecraftState> observations) {
    // Initial guess from first observation
    TimeStampedPVCoordinates pv = observations.get(0).getPVCoordinates();
    Orbit initialGuess = new CartesianOrbit(pv, frameTEME, pv.getDate(), mu);

    DormandPrince853IntegratorBuilder integrator = new DormandPrince853IntegratorBuilder(0.001, 1000, 1e-6);
    NumericalPropagatorBuilder builder = new NumericalPropagatorBuilder(initialGuess, integrator, PositionAngleType.TRUE, 1.0);

    // Run BLS
    Propagator[] props = BLS(observations, builder);
    estimatedState = new SpacecraftState(props[0].getInitialState().getOrbit());
}

private Propagator[] BLS(List<SpacecraftState> observations, PropagatorBuilder builder) {
    LeastSquaresOptimizer lso = new LevenbergMarquardtOptimizer();
    BatchLSEstimator estimator = new BatchLSEstimator(lso, builder);
    
    // Using scaled STD for measurements
    double[] stdArr = getSTD(observations);
    double[] posArr = { stdArr[0] / 100, stdArr[1] / 100, stdArr[2] / 100 };

    for (SpacecraftState state : observations) {
        Position pos = new Position(state.getDate(), state.getPosition(), posArr, 1.0, new ObservableSatellite(0));
        estimator.addMeasurement(pos);
    }

    Propagator[] estimatedPropagators = estimator.estimate();
    covarianceMatrix = estimator.getPhysicalCovariances(1e-15);
    return estimatedPropagators;
}

I also added all the required force models to the propagator (sun and moon third body), gravity field, and Solar Radiation Pressure.

The result I get for the Estimated State is

Estimated State: {33,633,478.11606355; -14,827,612.255421402; 20,675,806.279446695}

True State: {33,624,630.958034806; -14,821,489.93243606; 20,693,313.03396951}

Any help would be greatly appreciated.

Hello @Tomi,

True but i would add:

  • It will depend on the force configuration
  • Only true for the very first iteration as the orbit determination process may then slightly change the parameters to better fit all your “measurements”

Could you provide some numerical values ?

Could you provide the detailed force configuration ?

Cheers,
Vincent

This is what I have for my propagator configurations.

private Propagator numericalPropBuilder(SpacecraftState stateInit) {
	final double minStep = 0.001;
	final double maxStep = 1000.0;
	final double positionTolerance = 1e-6;
	final OrbitType orbitType = OrbitType.CARTESIAN;
	final ToleranceProvider toleranceProvider = ToleranceProvider
			.of(CartesianToleranceProvider.of(positionTolerance));
	final double[][] tol = toleranceProvider.getTolerances(stateInit.getOrbit(), orbitType);
	final ODEIntegrator integrator = new DormandPrince853Integrator(minStep, maxStep, tol[0], tol[1]);

	// Propagator
	final NumericalPropagator propagator = new NumericalPropagator(integrator);
	propagator.setInitialState(stateInit);
	propagator.setOrbitType(orbitType);

	CelestialBody sun = CelestialBodyFactory.getSun();

	OneAxisEllipsoid ell = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
			Constants.WGS84_EARTH_FLATTENING, ETrustedFrame.ITRFt.frame);

	IsotropicRadiationSingleCoefficient coeff = new IsotropicRadiationSingleCoefficient(70.8, 0.1);
	SolarRadiationPressure srp = new SolarRadiationPressure(sun, ell, coeff);

	ThirdBodyAttraction tbaMoon = new ThirdBodyAttraction(CelestialBodyFactory.getMoon());
	ThirdBodyAttraction tbaSun = new ThirdBodyAttraction(CelestialBodyFactory.getSun());

	// Add a force model
	final NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(8, 8);
	final ForceModel holmesFeatherstone = new HolmesFeatherstoneAttractionModel(
			FramesFactory.getITRF(IERSConventions.IERS_2010, true), provider);
	propagator.addForceModel(holmesFeatherstone);
	propagator.addForceModel(srp);
	propagator.addForceModel(tbaSun);
	propagator.addForceModel(tbaMoon);

	// Return
	return propagator;
}

BeidouData.txt (10.5 KB)
This is the data that I use for the OD, and the standard deviation used below

Standard Deviation: 3.003661578975602E7 1.747966708126279E7 2.423737321762487E7

This is the covariance (top quadrant of the covariance matrix) in units of km^2 (for easier readability)

1629.768417609 830.651195819 -1282.291335782
830.651195819 1077.192002361 -554.591925424
-1282.291335782 -554.591925423 1666.347346185

I should say that this was all done in the TEME reference frame by the way.

Thanks a lot for the help!

I performed some quick testing on my end using provided positions and got the following results :

=== Estimated orbit at epoch 2021-04-16T23:59:42.000Z ===
Cartesian:  X=33624664.921 m, Y=-14821399.461 m, Z=20693317.200 m
            Vx=1828.147615 m/s, Vy=1449.266386 m/s, Vz=-2001.755309 m/s

=== Covariance matrix ===
                                  Px              Py              Pz              Vx              Vy              Vz global radiation factor
Px                      6.762704e-01    7.229639e-01   -1.009075e+00   -1.188351e+00   -9.924043e-02    1.318309e-01    3.662914e-03
Py                      7.229639e-01    4.224151e+00   -3.150838e+00   -3.374845e+00    5.743703e-01   -8.205817e-01    5.509008e-02
Pz                     -1.009075e+00   -3.150838e+00    6.356360e+00    4.687925e+00   -8.040637e-01    1.143176e+00   -7.729419e-02
Vx                     -1.188351e+00   -3.374845e+00    4.687925e+00    4.659940e+00   -5.701877e-01    8.070055e-01   -3.839385e-02
Vy                     -9.924043e-02    5.743703e-01   -8.040637e-01   -5.701877e-01    1.569304e+00    6.179402e-01    1.286421e-02
Vz                      1.318309e-01   -8.205817e-01    1.143176e+00    8.070055e-01    6.179402e-01    1.166635e+00   -1.833154e-02
global radiation factor    3.662914e-03    5.509008e-02   -7.729419e-02   -3.839385e-02    1.286421e-02   -1.833154e-02    2.507494e-03

=== Standard deviations (1-sigma) ===
  Px                      8.223566e-01
  Py                      2.055274e+00
  Pz                      2.521182e+00
  Vx                      2.158689e+00
  Vy                      1.252719e+00
  Vz                      1.080109e+00
  global radiation factor    5.007489e-02

I’m using SI units by the way.

I believe you did not activate the reflection coefficient estimation ? If so, i suggest you to estimate it as it will greatly improve the orbit determination convergence:

      // Solar radiation pressure (area = 70.8 m², Cr = 0.1)
      IsotropicRadiationSingleCoefficient radiationSensitive = new IsotropicRadiationSingleCoefficient(70.8, 0.1);

      // estimate SRP Coefficient
      radiationSensitive.getRadiationParametersDrivers().get(0).setSelected(true);

I used a standard mass of 1000kg as it seems close to the real mass of these platforms according to https://ilrs.gsfc.nasa.gov/missions/satellite_missions/current_missions/bm13_general.html.

I’ll try to find some time to clean my code because it is clearly not ready to be posted here :sweat_smile:.

Cheers,
Vincent

Thanks a lot for that. It’s very helpful. Do you mind if I ask, how did you come about the standard deviation used? My calculated standard deviation was 7 orders of magnitude greater than what you have, and I need to figure out where my mistake lies.

My bad i forgot to mention i was using some default values for the position measurements sigma.

Are you sure that you are not using variance instead of standard deviation ? That would explain why you are getting a bigger covariance.

When using the square root of 1e7 (around 3162 m) for the standard deviation of the position measurements (so that it is the same order of magnitude as you), i get the following standard deviations:

=== Standard deviations (1-sigma) ===
  Px                      2.600520e+02
  Py                      6.499347e+02
  Pz                      7.972678e+02
  Vx                      6.826376e+02
  Vy                      3.961444e+02
  Vz                      3.415604e+02
  global radiation factor    1.583507e+01

Cheers,
Vincent

Perfect! Thank you so much for all the help.

1 Like