Attitude dynamics integration

Good afternoon,

I’m currently writing an attitude dynamics model to be integrated by the propagator. It implements the AdditionalEquations interface and the integration performs correctly. It also implements the AttitudeProvider interface to provide the attitude based on those integrated additional states. In order to achieve this, I need to access the full spacecraft state in the getAttitude() method (see https://www.orekit.org/mailing-list-archives/orekit-users/msg00175.html).

Do you know a workaround or a way to build a bridge between the AdditionalEquations and the AttituteProvider ?

Thanks in advance,

Paul Dewost for Exotrail

Hi @PaulDewost

Sorry for the late answer.

This is a difficult task that I don’t think is possible with the current version of Orekit. To access the spacecraft state in the getAttitude() method, the method signature must be modified. However, I don’t know if it’s a good idea.
Can your attitude model implement both interface (i.e. AttitudeProvider and AdditionalEquations) ?

Best regards,
Bryan

Hi @bcazabonne,

My model is already implementing both interfaces.
The problem is to get the additional state inside the getAttitude() method at the exact right date.
I’ve tried to also implement the StepHandler interface and store the value of the additional state each time a propagation step is performed, but it does not work well because the time steps are usually way too big to simulate attitude dynamics accurately…

Best regards,

Paul

Instead of storing the state, you could try to store the interpolator, but this implies that you implement OrekitStepHandler and not OrekitFixedStepHandler. In fact, I doubt this will work and it may generate infinite recursion: the step handler manages state that includes attitude so we have a chicken and egg problem.

Hi @luc,
Thanks for your reply. Indeed you are right, implementing OrekitStepHandler might end up in some kind of infinite recursion. I will look into it.
Best regards,
Paul

Some more thoughts about this problem.

State handling is quite involved in Orekit. State contains orbit, attitude, mass and additional states. Additional states may be constants that are copied around, values evaluated from analytical equations, or values obtained by ODE integration (the last one only for numerical and DSST propagators which are integration-based).
During propagation, each state is built up progressively. Here is what happens for integration- based propagators:

  1. position-velocity is computed by integrating space dynamics equations
  2. attitude is added using attitude provider (in OsculatingMapper.mapArrayToState or MeanPlusShortPeriodicMapper.mapArrayToState)
  3. constants (unmanaged) additional states are added (in AbstractPropagator.updateAdditionalStates)
  4. analytical additional states are added (in AbstractPropagator.updateAdditionalStates)
  5. additional states managed by integrated equations are added

So attitude is handled early in this process. Even if we were to pass the SpacecraftState to AttitudeProvider it would probably not help in your case because the additional states have not been included yet.

What I could suggest (but it is only a wild guess, I did not check it works or not) would be to add a 6th step that would apply a user-provided post-processing object to the state before it is returned. This post-processing would simply take a state and return a possibly modified state. In your case, you could have the attitude really handled there, overriding the attitude used in earlier step, perhaps by simple interpolation, or using a crude model, or reusing data extracted the previous call if you use the same object as attitude provider and as state post-processor.

I did not check we could insert this everywhere properly (for example, is it taken into account before force models are called in the core of the integration, I don’t know yet).

Such a new feature could be added in 10.3 if required.

What do you think of this proposal?

1 Like

Hi @luc,
Thank you for the idea !
We will definitely look into this in the coming days. Anyway this would be a very nice feature to add in Orekit 10.3, as it would allow many other developments of this kind.
I will keep you informed of our progress on this thread.
Best regards,
Paul

Hello! Excuse me for reviving this topic. I would like to ask if @PaulDewost had any progress, or if @luc has updates on his above proposal.

Our team is considering using the attitude package to implement a simple control law that manages and updates the momentum of reaction wheels, configured at various orientations. The wheel speeds are coupled with the satellite’s attitude and I can see how the “extra step” that @luc suggests could be used to fully manage attitude state.

Our above use case also considers incorporating constraints in the control (e.g. maximum angular rate and acceleration, as defined by wheel specifications etc.)

Any update is greatly appreciated, I will consider starting a new topic if there is any progress or bottlenecks (from our end)!

Kind regards,
Manny

No, we did not add this in 10.3.
Could you open an issue in the issue tracker issue tracker and set up “Feature” label on it?

I tried the solution proposed by @luc and it worked.
But we ended up not implementing it in the end, because the time scales are too different between our space mechanics simulations (including low thrust burns) and the ADCS time scale, which is much shorter.
If you are interested I can try to elaborate on what I did exactly.

Thank you both for your valuable answers!

@luc I created a new issue here, but added the label in the text body only, I could not set up the label otherwise (I’m sure I miss something simple!).

@PaulDewost I understand, the low thrust trajectory timescale of weeks or months is way larger than the tiny integration steps of an attitude controller. Great job on making it work!

I would be grateful if you could share your approach on implementing @luc’s suggestion. Our team’s use-case revolves more around LEO orbiting satellites. Elaborating on a high-level about the solution should be enough to get me started, and report my progress here. Any help is greatly appreciated!

Kind regards,
Manny

Sure thing :slight_smile:
So what I did is the following.

Build a StateConverter interface with a public SpacecraftState convert(SpacecraftState s) method
Create an AttitudeModel class which implements AdditionalEquations, AttitudeProvider and StateConverter

Modify the AbstractIntegratedPropagator class of Orekit as follows:

1/ Add this method:

private List<StateConverter> stateConverters;
public void addStateConverter(final StateConverter converter) {
    stateConverters.add(converter);
}

2/ Modify this method:

private SpacecraftState convert(final ODEStateAndDerivative os) {
    SpacecraftState s = stateMapper.mapArrayToState(os.getTime(), os.getPrimaryState(), os.getPrimaryDerivative(), propagationType);
    s = updateAdditionalStates(s);
    for (int i = 0; i < additionalEquations.size(); ++i) {
        final double[] secondary = os.getSecondaryState(i + 1);
        s = s.addAdditionalState(additionalEquations.get(i).getName(), secondary);
    }
    // TODO: use state converters to update spacecraft state with additional equations
    for (StateConverter converter : stateConverters) {
        s = converter.convert(s);
    }
    return s;
}

The problem here is that in AttitudeModel you will need to store the previously integrated attitudes in a structure such as private TreeMap<AbsoluteDate, Attitude> attitudes;

In AttitudeModel, each call to the convert method stores the integrated attitude in the TreeMap.

@Override
public SpacecraftState convert(SpacecraftState state) {

    AbsoluteDate date = state.getDate();
    double[] attitudeState = state.getAdditionalState(ATTITUDE_MODEL_KEY);

    [...]
    
    Attitude attitude = new Attitude(referenceFrame, orientation);

    SpacecraftState newState;

    if (state.isOrbitDefined()) {

        newState = new SpacecraftState(state.getOrbit(), attitude, state.getMass(),
                state.getAdditionalStates());

    } else {

        newState = new SpacecraftState(state.getAbsPVA(), attitude, state.getMass(),
                state.getAdditionalStates());
    }

    // Update attitudes state map
    attitudes.put(date, attitude);

    return newState;
}

Finally the getAttitude methods is as follows:

@Override
public Attitude getAttitude(PVCoordinatesProvider pvProv, AbsoluteDate date, Frame frame) {

    AbsoluteDate nearestPreviousDate = attitudes.floorKey(date);
    Attitude nearestPreviousAttitude = attitudes.get(nearestPreviousDate);

    return new Attitude(date, frame, nearestPreviousAttitude.getOrientation());
}

This can probably be greatly improved. One of the downsizes of this implementation is that it prevents any kind of propagation of the attitude model in the future. A getAttitude call performed by Orekit at a date where the integration has not been performed yet will return a past attitude. I have not found a way to improve this behavior. My guess is that you would need to modify Orekit a bit more deeply to succeed.

2 Likes