Orbit Determination Covariance - What frame is it in, and how to convert it?

Hello, I have a few question regarding the covariance produced via orbit determination with the BatchLSEstimator, and how to correctly transform the covariance to different elements / frames. I will say I’m fairly new to Orekit, so my apologies if these questions are off base.

  1. When calling BatchLSEstimator.getPhysicalCovariances() what frame is this expressed in? My assumption is that it is the frame associated with the initial OrbitDeterminationPropagatorBuilder supplied to the BatchLSEstimator. Is that correct?

  2. What is the order of the orbital elements in the covariance matrix? Does this order match the OrbitType.getDrivers() list (assuming no changes are made to the selected driver parameters)

  3. I’m trying to be get the resulting covariance and convert it to equinoctal elements in the LVLH frame…does this look correct?

private RealMatrix getCovariance(BatchLSEstimator estimator, Propagator estimatedPropagator) {
	RealMatrix covariance = null;
	RealMatrix uvwMatrix = null;
	try {
	    covariance = estimator.getPhysicalCovariances(1.0e-10);
	} catch (OrekitException e) {
	    // Log when covariance can't be retrieved
	    log.info("Unable to retrieve covariance matrix for Orekit orbit determination");
	}
	if (covariance != null) {
	    PositionAngle angle = PositionAngle.TRUE;
	    if (estimatedPropagator instanceof NumericalPropagator) {
		angle = ((NumericalPropagator) estimatedPropagator).getPositionAngleType();
	    }

	    // get a 6x6 matrix with only the orbital elements used to solve. If orbital
	    // elements were de-selected, their column / rows will be 0
	    List<String> selectedDriverParameters = estimator.getOrbitalParametersDrivers(true).getDrivers().stream()
		    .map(dp -> dp.getName()).collect(Collectors.toList());
	    RealMatrix fullOrbitalMatrix = OrekitCovarianceMatrixUtil.buildOrbitalCovarianceMatrix(
		    estimatedPropagator.getInitialState().getOrbit(), selectedDriverParameters, covariance);

	    // convert the matrix to be expressed in equinoctial elements
	    RealMatrix equinoctialMatrix = transformCovarianceToEquinoctial(
		    estimatedPropagator.getInitialState().getOrbit(), fullOrbitalMatrix, angle);

	    // setup transform to convert our equinoctial covariance in the source frame to
	    // the LVLH (UVW) frame
	    Orbit orbit = OrbitType.CARTESIAN.convertType(estimatedPropagator.getInitialState().getOrbit());
	    Frame sourceFrame = estimatedPropagator.getFrame();
	    LocalOrbitalFrame lvlh = new LocalOrbitalFrame(sourceFrame, LOFType.LVLH, orbit, "LVLH");
	    Transform localFrameTransform = sourceFrame.getTransformTo(lvlh, orbit.getDate()).freeze();

	    // do the transform
	    RealMatrix jacobian = getCartesianPvJacobian(localFrameTransform);
	    uvwMatrix = jacobian.multiply(equinoctialMatrix.multiply(jacobian.transpose()));
	}

	return uvwMatrix;
    }

    private RealMatrix transformCovarianceToEquinoctial(Orbit inputOrbit, RealMatrix covarianceMatrix,
	    PositionAngle positionAngle) {
	if (covarianceMatrix.getColumnDimension() != 6 || covarianceMatrix.getRowDimension() != 6) {
	    return null;
	}

	if (OrbitType.EQUINOCTIAL.equals(inputOrbit.getType())) {
	    return covarianceMatrix;
	}

	// convert to Cartesian covariance
	RealMatrix cartesianMatrix = null;
	if (OrbitType.CARTESIAN.equals(inputOrbit.getType())) {
	    cartesianMatrix = covarianceMatrix;
	} else {
	    double[][] orbitalJacobian = new double[6][6];
	    inputOrbit.getJacobianWrtParameters(positionAngle, orbitalJacobian);
	    RealMatrix orbitalJacobianMatrix = MatrixUtils.createRealMatrix(orbitalJacobian);
	    cartesianMatrix = orbitalJacobianMatrix.multiply(covarianceMatrix)
		    .multiplyTransposed(orbitalJacobianMatrix);
	}

	// convert to Equinoctial covariance
	double[][] equinoctalJacobian = new double[6][6];
	EquinoctialOrbit equinoctalOrbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(inputOrbit);
	equinoctalOrbit.getJacobianWrtCartesian(positionAngle, equinoctalJacobian);
	RealMatrix dEdC = MatrixUtils.createRealMatrix(equinoctalJacobian);
	return dEdC.multiply(cartesianMatrix).multiplyTransposed(dEdC);
    }

    private RealMatrix getCartesianPvJacobian(final Transform transform) {
	// Get the Jacobian of the transform
	final double[][] jacobian = new double[6][6];
	transform.getJacobian(CartesianDerivativesFilter.USE_PV, jacobian);
	// Return
	return new Array2DRowRealMatrix(jacobian, false);
    }

Thank you for all of your assistance. The Orekit community / forums have been really helpful while learning the library!

-Eddie

Hi @etb101 and welcome to the Orekit forum!

Yes, it is correct.

Yes, it does. The 6 orbital elements follow this order. If you estimate propagation parameters (i.e., drag or reflection coefficients) they will be are index 7, 8, etc.

I can recommend you to use the StateCovariance class. It is a nice class providing methods for covriance transformation: orbit type and frames. I think it can answer your problem.

Best regards,
Bryan

3 Likes

Hey Bryan,

Thank you for the suggestion, that greatly simplified my code!

Thanks,
Eddie