Asking for help

Hi all,

I have been working for months on issue 1867. It is a huge change, with many side effects. I have set up a branch for it. It doesn’t work yet, far from it. It compiles, I think it has everything I wanted in it, I think the design is clean, but there are 35 tests that do not work, and I am at a loss now.

I also think the goal is not very far now, and I really do not want to throw away all this work, it should go into V14.0. I however really need help from someone else here to finish this task, I just cannot do it alone anymore. I am stuck in my own views and my own choices. I am sure I don’t see obvious design or coding errors I made, so I need fresh eyes and people fixing my errors and making all tests pass.

The basic principles of this change are simple: add some higher level interface above orbits, TLE and GNSS orbital elements, throw in a hierarchy of factories to built these just as we have factories for ODE integrators or propagators, and use everything in orbit determination, conversion, fitting, optimization… The low level implementation is complicated. Of course there is always the problem of primitive double and Field objects and how they interact. There is also the problem of partial derivatives, with Gradient fields, but also FieldGradient, matrices harvester, gradient converters. There is also the problem of the deep hierarchy needed for the GNSS stuff (all constellations, navigation messages, almanacs, propagators, legacy messages, civilian messages in several versions…).

One of the side effects of this change is that GNSS propagators now use Keplerian elements instead of Cartesian elements, and this changed state transition matrices. This was already partly supported as MatrixHarvester has agetOrbitType method, but at some points it was ignored. I discovered for example that AbstractAnalyticalMatricesHarvester.setReferenceState assumed we used Cartesian elements, ignoring what getOrbitType returned for the current harvester implementation. So there are mismatches at several places. I found and fixed some of them, but not all, so some tests have completely wrong results. I am also confused by the {Field}GNSSPropagator constructor that uses orbital elements as its first argument. When I set the initial state, I have to use Orbit orbit = propagateOrbit(orbitalElements.getDate()), i.e. I have to perform a 0 seconds propagation, I cannot use Orbit orbit = orbitalElements.getOrbit() despite it should already be correct. If I simplify the call (i.e. don’t propagate), one test behaves partly better (I get 1.0 for ∂a(t₀)/∂a₀, which is expected, but I get non zero values for other partial derivatives) but other tests fail dramatically. If I keep the propagation, this test fails for ∂a(t₀)/∂a₀, but some tests for other classes succeed.

The tests that fail are mainly concentrated on GNSS, TLE and DSST and related to orbit determination (all kinds: batch least squares, Kalman, unscented Kalman…) and partial derivatives.

As I wrote above, I am at a loss now, and I am in dire straits. Could someone step in and look at this mess?

I can’t promise anything, mostly because I have a 5-month little boy who’s cutting his first tooth, but I will try to have a look next week.

Bryan

Hello Luc,

I’ll also try to look into it but i guess that it will take some time to familiarize myself with your changes.

Cheers,
Vincent

I can pitch in some effort. I’ll try to get familiar with the updates this week.

Hi Luc,

Same here, I’ll try to take a look if I find some time!

Maxime

I understood why the 0 seconds propagation is needed. The orbitalElements.getOrbit() only has the Keplerian elements, but the propagation also takes the non-Keplerian elements from orbitalElements.getParametersDrivers().

I started off looking at the first test that failed with a big error. There were several before where the number of iterations to convergence changed. However I’ve seen we tweak those numbers from version to version and a spot check looked like it was converging on a reasonable solution, albeit with several extra iterations.

So the first one I saw was the failure of the KalmanEstimatorTest.testEstimationStepWithBStarOnly. I’ve isolated the problem but before attempting a solution I wanted to just post to the group the progress so far. In the test you can see that it activates the BStar non-Keplerian propagation parameter on line 77. You can see that it is updated in the propogatorBuilder too. However when the estimator calls the builder the actual created propagator is coming from a chain of calls initiated at at TLEPropagator.selectExtrapolator. That ultimately calls into the main TLEPropagator constructor. This is where the problem occurs. As it is currently coded the bStarDriver is hardcoded to be the initial BStar from the TLE with other default values. This field is private and final so inaccessible and unchangeable.

My first cut at this was going to be something where we take the propagator and then override the settings on bStarDriver with the ones we configured in the initial step. That would require exposing bStarDriver directly. By doing that the TLEPropagatorBuilder can do something like it does with adding impulsive maneuvers and override it, or check if it is different and then override it or something. Thoughts?

Good catch, Hank!
You are right, the non-Keplerian drivers should be exposed (B-star for TLE, M2 for Brouwer-Lyddane, all the slopes and harmonic elements for GNSS)… and then used for estimation or optimization.

Actually, we do something like that already in the Brouwer-Lyddane stuff. Making the similar change for TLE in the buildPropagator method goes something like:

final TLEPropagator propagator = TLEPropagator.selectExtrapolator(tle, getAttitudeProvider(), getMass(),
                                                                   getOrbitalParameterFactory().getFrame());
final boolean selected = this.getPropagationParametersDrivers().getDrivers().get(0).isSelected();
        
propagator.getParametersDrivers().get(0).setSelected(selected);
        getImpulseManeuvers().forEach(propagator::addEventDetector);