Defining custom parameters for state transition matrix

Hi,

When implementing a Kalman filter for orbit determination I would like to define some parameters to be derived during the propagation. An easy one is the thrust as everything is already implemented.

But I’m getting a bit confused with things like solar radiation pressure. I would like to define the cross section as a parameter instead of the reflection and absorption coefficient. But I don’t know if it is possible, and if yes, how to do it…

Would you have any advise on this topic?

Thank you very much for your help!

Kind regards,

B.

Hi @benoist,

If I good understand, you want to estimate the cross section during the orbit determination process ?

If the answer of the previous question is yes, there is no solar radiation pressure model in Orekit able to estimate the cross section. You have to create it :wink:

An important point is that, in Orekit, parameters that can be estimated during an orbit determination process must be defined as ParameteDriver. So the goal is to create a custom radiation model where the cross section is a ParameterDriver instead of a simple double value.

To create your custom model you can take example of the IsotropicRadiationClassicalConvention class and create a new CustomIsotropicRadiation class. However, instead of defining absorptionParameterDriver and reflectionParameterDriver as ParameterDriver you can define them as double values (if you do not want to estimate them). In this new implementation, crossSection is now a ParameterDriver.

/** Driver for absorption coefficient. */
private final double absorptionParameterDriver;

/** Driver for specular reflection coefficient. */
private final double reflectionParameterDriver;

/** Cross section (m²). */
private final ParameterDriver crossSection;

Now, you have to initialize the crossSection driver in the model’s constructor.

public CustomIsotropicRadiation(final double crossSection, final double ca, final double cs) {
    try {
        crossSection = new ParameterDriver(name, referenceValue, scale, minValue, maxValue);
    } catch (OrekitException oe) {
        // this should never occur as valueChanged above never throws an exception
        throw new OrekitInternalError(oe);
    }
    this.absorptionParameterDriver = ca;
    this.reflectionParameterDriver = cs;
}

Here, you have to define name, referenceValue, scale, minValue and maxValue parameters.

Don’t forget to update the getRadiationParametersDrivers() method.

Finally, during the computation of the radiation pressure acceleration, the estimated parameters are store in the double[] parameters argument. Since you have just one estimated parameter for this model, the cross section is given by parameters[0]. In the case you have two estimated model parameters, they are given by parameters[0] and parameters[1], the 0 and 1 is defined by the order you use in the getRadiationParametersDrivers() method. So at the end, the solar radiation acceleration is computed like this:

public Vector3D radiationPressureAcceleration(final AbsoluteDate date, final Frame frame, final Vector3D position,
                                              final Rotation rotation, final double mass, final Vector3D flux,
                                              final double[] parameters) {
    final double ca = absorptionParameterDriver;
    final double cs = reflectionParameterDriver;
    final double kP = parameters[0] * (1 + 4 * (1.0 - ca - cs) / 9.0);
    return new Vector3D(kP / mass, flux);
}

I let you implement the “RealFieldElements” version of the method :slightly_smiling_face:

I hope my explanations are clear. ParameterDriver concept is something difficult to understand in Orekit.

I hope this will help you,
Bryan

Hi Bryan!

Thank you very much for your help. I’ve implemented it and I believe it works. I still need to PP the output to see if the behaviour is alright.

I tried doing the same for the isotropic drag. This time I want to define (Cd*S/M) as a parameter driver. I’ve changed the code accordingly:

    private final ParameterDriver[] dragParametersDrivers;

    /** CSM coefficient. */
    private double CSM;

    /** Simple constructor.
     * @param crossSection Surface (m²)
     * @param dragCoeff drag coefficient
     */
    public CustomIsotropicDrag(final double crossSection, final double dragCoeff, final double mass) {
        
    	this.dragParametersDrivers  = new ParameterDriver[1];
        this.CSM 					= crossSection*dragCoeff/mass;
        
        try {
            // in some corner cases (unknown spacecraft, fuel leaks, active piloting ...)
            // the single coefficient may be arbitrary, and even negative
        	dragParametersDrivers[0] = new ParameterDriver("dragCSM",
            												CSM, SCALE,
                                                           Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
            dragParametersDrivers[0].addObserver(new ParameterObserver() {
                /** {@inheritDoc} */
                @Override
                public void valueChanged(final double previousValue, final ParameterDriver driver) {
                    CustomIsotropicDrag.this.CSM = driver.getValue();
                }
            });
        } catch (OrekitException oe) {
            // this should never occur as valueChanged above never throws an exception
            throw new OrekitInternalError(oe);
        };               
    }

And I’ve only modified the equations of the other methods. But I’m facing the issue of having to define additional methods which were not there before in IsotropicDrag. I left them untouched but my algorithm crashes with a NullPointer error. I believe it tries using the additional methods (which return null). But I have no idea how to populate them or why they even exist in the first place as IsotropicDrag was not using them before. The methods I’m mentioning are the following:

	@Override
	public Vector3D dragAcceleration(AbsoluteDate arg0, Frame arg1,
			Vector3D arg2, Rotation arg3, double arg4, double arg5,
			Vector3D arg6, double[] arg7) throws OrekitException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public <T extends RealFieldElement<T>> FieldVector3D<T> dragAcceleration(
			FieldAbsoluteDate<T> arg0, Frame arg1, FieldVector3D<T> arg2,
			FieldRotation<T> arg3, T arg4, T arg5, FieldVector3D<T> arg6,
			T[] arg7) throws OrekitException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public FieldVector3D<DerivativeStructure> dragAcceleration(
			AbsoluteDate arg0, Frame arg1, Vector3D arg2, Rotation arg3,
			double arg4, double arg5, Vector3D arg6, double[] arg7, String arg8)
			throws OrekitException {
		// TODO Auto-generated method stub
		return null;
	}

Would you have any idea why I have to create these additional methods?

Thank you very much for your help.

Kind regards,

B.

Hi @benoist,

Are you sure the dragAcceleration methods were not in the original IsotropicDrag class ? It is weird.
What version of Orekit are you using ?

Maxime

Hi @benoist and @MaximeJ,

I think the first thing to do is to use a more recent version of Orekit, v10.0 for instance. Indeed, the
FieldVector3D<DerivativeStructure> dragAcceleration() method no longer exists in Orekit. Updating your version of Orekit will not change my previous comments and tips about the CustomIsotropicRadiation class. Concerning the CustomIsotropicDrag model, you will have to follow the following steps:

  1. Constructor. As your CSM is now an estimated parameter, you do not need to declare the CSM argument. It will be stored inside the array of estimated parameters (i.e. double[] parameters). Hence, the constructor of your custom model will be:

     /** Drivers for drag coefficient parameter. */
     private final ParameterDriver[] dragParametersDrivers;
    
     /** Constructor with drag coefficient min/max set to ±∞.
      * @param crossSection Surface (m²)
      * @param dragCoeff drag coefficient
      * @param mass spacecraft mass
      */
     public CustomIsotropicDrag(final double crossSection, final double dragCoeff, final double mass) {
         // Initial value of the CSM coefficient
         final double csmInit = dragCoeff * crossSection / mass;
         this.dragParametersDrivers     = new ParameterDriver[1];
         try {
             dragParametersDrivers[0] = new ParameterDriver("drag CSM",
                                                            csmInit, SCALE,
                                                            Double.NEGATIVE_INFINITY,
                                                            Double.POSITIVE_INFINITY);
         } catch (OrekitException oe) {
             // this should never occur as valueChanged above never throws an exception
             throw new OrekitInternalError(oe);
         }
     }
    
  2. Implement the two dragAceleration() methods (i.e RealFieldElements and non-RealFieldElements versions). Just remember that now the csm parameter is stored in the double[] parameters arguement (at index [0] because it’s the only estimated parameter for this model).

    public Vector3D dragAcceleration(final AbsoluteDate date, final Frame frame, finalVector3Dposition,
                                     final Rotation rotation, final double mass,
                                     final double density, final Vector3D relativeVelocity,
                                     final double[] parameters) {
        final double csm = parameters[0];
        return new Vector3D(relativeVelocity.getNorm() * density * csm * 0.5,
                           relativeVelocity);
    

    }

  3. Perform the same implementation for the RealFieldElements implementation.

     public <T extends RealFieldElement<T>> FieldVector3D<T>
     dragAcceleration(final FieldAbsoluteDate<T> date, final Frame frame,
                      final FieldVector3D<T> position, final FieldRotation<T> rotation,
                      final T mass, final T density,
                      final FieldVector3D<T> relativeVelocity,
                      final T[] parameters) {
     final T csm = parameters[0];
     return new FieldVector3D<>(relativeVelocity.getNorm().multiply(density.multiply(csm.multiply(0.5))),
                                relativeVelocity);
    

    }

Kind regards,
Bryan

Hi both,

Thank you very much for your help! Indeed it was a problem of version… I’ve migrated to Orekit 10.0, I was still using 9.2 and it now works fine.

B.