Hello. I’m not sure if this is the right place for this post as I am unsure if the issue is user error, expected behaviour, or a bug. Please let me know if this better fits elsewhere.
I’m using Orekit to perform initial orbit determination on a set of observations I have taken. However, while using the Orbit returned by IodGooding, I found that the getPVCoordinates() function was not returning me an instance of TimeStampedPVCoordinates with the same AbsoluteDate as the AbsoluteDate I provided the function. I have been able to replicate the error with made up data in the following code.
// Create three test observations for use in IOD
final double lat = -30;
final double lon = 134;
final double alt = 90.0;
GeodeticPoint stationPoint = new GeodeticPoint(FastMath.toRadians(lat), FastMath.toRadians(lon), alt);
TopocentricFrame stationFrame = new TopocentricFrame(ReferenceEllipsoid.getWgs84(FramesFactory.getITRF(IERSConventions.IERS_2010, true)), stationPoint, "home");
GroundStation groundStation = new GroundStation(stationFrame);
ObservableSatellite satellite = new ObservableSatellite(0);
Frame frame = FramesFactory.getGCRF();
AbsoluteDate date1 = new AbsoluteDate("2025-12-15T11:11:00.000000000000000000Z", TimeScalesFactory.getUTC());
AbsoluteDate date2 = new AbsoluteDate("2025-12-15T14:56:00.000000000000000000Z", TimeScalesFactory.getUTC());
AbsoluteDate date3 = new AbsoluteDate("2025-12-15T18:39:00.000000000000000000Z", TimeScalesFactory.getUTC());
double[] angular1 = {1.5, 0.08};
double[] angular2 = {2.5, 0.04};
double[] angular3 = {3.5, 0.05};
double[] sigma = {FastMath.toRadians(5.0 / 3600.0), FastMath.toRadians(5.0 / 3600.0)};
double[] baseWeight = {1.0, 1.0};
AngularRaDec ob1 = new AngularRaDec(
groundStation,
frame,
date1,
angular1,
sigma,
baseWeight,
satellite
);
ob1.addModifier(new AberrationModifier());
AngularRaDec ob2 = new AngularRaDec(
groundStation,
frame,
date2,
angular2,
sigma,
baseWeight,
satellite
);
ob2.addModifier(new AberrationModifier());
AngularRaDec ob3 = new AngularRaDec(
groundStation,
frame,
date3,
angular3,
sigma,
baseWeight,
satellite
);
ob3.addModifier(new AberrationModifier());
// Perform IOD on observations
NormalizedSphericalHarmonicsProvider gravityField = GravityFieldFactory.getNormalizedProvider(10, 10);
IodGooding iodGooding = new IodGooding(gravityField.getMu());
Orbit orbit = iodGooding.estimate(frame, ob1, ob2, ob3);
// Get coordinates at time of first observation
TimeStampedPVCoordinates pvCoordinates = orbit.getPVCoordinates(date1, frame);
System.out.println("Expected: " + date1);
System.out.println("Actual: " + pvCoordinates.getDate());
TimeOffset difference = pvCoordinates.getDate().accurateDurationFrom(date1);
System.out.println("Difference: " + difference.getAttoSeconds() + "e-18s");
// Shift observation date and try again
ob2 = new AngularRaDec(
groundStation,
frame,
date2.shiftedBy(0.123456789),
angular2,
sigma,
baseWeight,
satellite
);
ob2.addModifier(new AberrationModifier());
orbit = iodGooding.estimate(frame, ob1, ob2, ob3);
pvCoordinates = orbit.getPVCoordinates(date1, frame);
System.out.println("\nExpected: " + date1);
System.out.println("Actual: " + pvCoordinates.getDate());
difference = pvCoordinates.getDate().accurateDurationFrom(date1);
System.out.println("Difference: " + difference.getAttoSeconds() + "e-18s");
// Shift observation date and try again
ob2 = new AngularRaDec(
groundStation,
frame,
date2.shiftedBy(100e-18),
angular2,
sigma,
baseWeight,
satellite
);
ob2.addModifier(new AberrationModifier());
orbit = iodGooding.estimate(frame, ob1, ob2, ob3);
pvCoordinates = orbit.getPVCoordinates(date1, frame);
System.out.println("\nExpected: " + date1);
System.out.println("Actual: " + pvCoordinates.getDate());
difference = pvCoordinates.getDate().accurateDurationFrom(date1);
System.out.println("Difference: " + difference.getAttoSeconds() + "e-18s");
// Shift observation date and try again
date1 = date1.shiftedBy(0.5);
ob1 = new AngularRaDec(
groundStation,
frame,
date1,
angular1,
sigma,
baseWeight,
satellite
);
ob1.addModifier(new AberrationModifier());
ob2 = new AngularRaDec(
groundStation,
frame,
date2.shiftedBy(0.5),
angular2,
sigma,
baseWeight,
satellite
);
ob2.addModifier(new AberrationModifier());
ob3 = new AngularRaDec(
groundStation,
frame,
date3.shiftedBy(0.5),
angular3,
sigma,
baseWeight,
satellite
);
ob3.addModifier(new AberrationModifier());
orbit = iodGooding.estimate(frame, ob1, ob2, ob3);
pvCoordinates = orbit.getPVCoordinates(date1, frame);
System.out.println("\nExpected: " + date1);
System.out.println("Actual: " + pvCoordinates.getDate());
difference = pvCoordinates.getDate().accurateDurationFrom(date1);
System.out.println("Difference: " + difference.getAttoSeconds() + "e-18s");
This code produces the following output:
Expected: 2025-12-15T11:11:00.000Z
Actual: 2025-12-15T11:11:00.000Z
Difference: 0e-18sExpected: 2025-12-15T11:11:00.000Z
Actual: 2025-12-15T11:10:59.999999999999443552Z
Difference: 999999999999443552e-18sExpected: 2025-12-15T11:11:00.000Z
Actual: 2025-12-15T11:11:00.0000000000000001Z
Difference: 100e-18sExpected: 2025-12-15T11:11:00.500Z
Actual: 2025-12-15T11:11:00.500Z
Difference: 0e-18s
In attempting to replicate the behaviour, I found that the issue didn’t occur when the dates I provided had the same fractional second part (see above). The real observations used had fractional second parts .961481000000000384s, .435758000000000384s, and .852012000000000384s (the 384 at the end appears after processing my observations with Orekit) with a difference from the expected date of 652992e-18s.
I am using Orekit version 13.1.2 and OpenJDK 16.
Since I was attempting to validate AbsoluteDates at several points in my pipeline, this behaviour was causing issues, as the dates returned were not as expected. I was able to rewrite my code to avoid using getPVCoordinates(), but I still thought it worth mentioning somewhere that I came across this issue. Thanks.
