Numerical Propagator different results

Hi all,

I am trying to propagate a satellite’s state using numerical propagator. I am referring the tutorial to code the propagator.
The tutorial is using the following method to propagate to a specific timestamp.

final SpacecraftState finalState = propagator.propagate(pDate);
final CartesianOrbit orbit = (CartesianOrbit) OrbitType.CARTESIAN.convertType(finalState.getOrbit());
System.out.println(pDate.toString()+" position2 = "+finalState.getPVCoordinates().getPosition().getX());

In the orbit determination tutorials the state is propagated using EphemerisGenerator. The two methods produces slightly different results when propagated to the same time stamp.
For example,

2022-11-17T23:58:36.501Z position  = -2.687806757116504E7 ( With EphemerisGenerator)
2022-11-17T23:58:36.501Z position2 = -2.68780675711712E7 

I am not sure why and appreciate some help to understand what is happening.

Thank you.

Following is the code

public class PropagateSatellite {
	public static void main(String[] args) throws Exception {

		final String satId = "00000";
	public static void propagateSatellite(final String satId) {
		final FactoryManagedFrame orbitFrame = FramesFactory.getEME2000();
		final double kmToMeters = 1e3;
		JsonNode data = getStateData(satId,0);
		double xpos = -42096.595969393245*kmToMeters;
		double ypos = -2287.448341771474*kmToMeters;
		double zpos = 85.43497860699213*kmToMeters;
		double xvel = 0.16757315787000582*kmToMeters;
		double yvel = -3.070583889218538*kmToMeters;
		double zvel = -2.722574558365106e-4*kmToMeters;
		double cr = 0.044294;
		final String epochStr = "2022-11-19T23:45:43.501000Z";
		final AbsoluteDate epoch = new AbsoluteDate(epochStr,TimeScalesFactory.getUTC());
		final double[] pos = {xpos,ypos,zpos};
        	final double[] vel = {xvel,yvel,zvel};
        	//Gravity field
        	final int degree =20;
        	final int order  = 20;
        	final NormalizedSphericalHarmonicsProvider gravityProvider = GravityFieldFactory.getConstantNormalizedProvider(degree, order);
        	/** Initial Orbit */
		final Orbit initialOrbit = new CartesianOrbit(new PVCoordinates(new Vector3D(pos), new Vector3D(vel)),orbitFrame,epoch,gravityProvider.getMu());
		final OrbitType propagationType = OrbitType.CARTESIAN;
		/** Spacecraft state*/
        	SpacecraftState initialState = new SpacecraftState(initialOrbit);
        	final int secondsPerDay = 86400;
        	final double days = 4.0;
        	final double deltaTime = 30*60.0;
        	final AbsoluteDate startEpoch = epoch.shiftedBy(-secondsPerDay*days);

		/** Initialize Propagator*/
		final double minStep = 0.001;
		final double maxStep = 300.0;
		final double positionError = 10.0;
		 final double[][] tolerances = NumericalPropagator.tolerances(positionError, initialOrbit, propagationType);
		final DormandPrince853Integrator integrator = new DormandPrince853Integrator(minStep,maxStep,tolerances[0],tolerances[1]);
		// Initialize the numerical builder

		final NumericalPropagator propagator = new NumericalPropagator(integrator);
		// Adding force models
		final CelestialBody bodySun = CelestialBodyFactory.getBody("sun");
		final CelestialBody bodyMoon = CelestialBodyFactory.getBody("moon");
		propagator.addForceModel(new ThirdBodyAttraction(bodySun));
		propagator.addForceModel(new ThirdBodyAttraction(bodyMoon));
	    	propagator.addForceModel(new HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010,true),gravityProvider));
	    	System.out.println("Gravity force added to propagator");
        	final RadiationSensitive spacecraft =new IsotropicRadiationSingleCoefficient(1.0, cr);
        	final SolarRadiationPressure solarPressureForce = new SolarRadiationPressure(CelestialBodyFactory.getSun(),Constants.WGS84_EARTH_EQUATORIAL_RADIUS, spacecraft);
        	//Initial stae
        	// Ephemeris generator
        	final EphemerisGenerator generator = propagator.getEphemerisGenerator();
		final Propagator boundedPropagator = generator.getGeneratedEphemeris();
		AbsoluteDate pDate = startEpoch;
		while (pDate.compareTo(epoch)<=0) {
			//First Method with bounded propagator
			PVCoordinates pv = boundedPropagator.getPVCoordinates(pDate, orbitFrame);
			Vector3D posProp = pv.getPosition();
			Vector3D velProp = pv.getVelocity();
			System.out.println(pDate.toString()+" position  = "+posProp.getX());
			//Second method 
			// Extrapolate from the initial to the final date
	        	final SpacecraftState finalState = propagator.propagate(pDate);
	        	final CartesianOrbit orbit = (CartesianOrbit) OrbitType.CARTESIAN.convertType(finalState.getOrbit());
	        	System.out.println(pDate.toString()+" position2 = "+orbit.getPVCoordinates().getPosition().getX());

			pDate = pDate.shiftedBy(deltaTime);	



Hi there,

Here are my two cents (from not having coded it): the ephemeris mode means that first, you propagate between the two dates in question. Then, using the existing, calculated integration steps, you interpolate w.r.t. to time to get any intermediate dates you requested (instead of actually computing step after step to reach them). So there is some difference, normally negligible if your integrator is well tuned for your trajectory. But it’s more performant, as the cost of interpolation is usually lower than starting and stopping your integration repeatedly, especially if you want many intermediate state.

Romain S.

1 Like

Hi ,

Thank you for the explanations. This helps a lot. The difference of position values are less than 1mm. Therefore, I think it can be ignored.

That actually raises a question from me to the dev team: what does the EphemerisGenerator do in practise with propagators that are not based on an integration scheme, such as KeplerianPropagator?

Edit: found the answer in the docstring of

Analytical propagators will mainly store only the start and stop date and the model itself, so ephemeris will just call the model back.


1 Like