Convert attitude (yaw, roll, pitch) from one frame to another frame

We have a requirement which is like following: (May I know is Orekit able to do this?)

we know the position of the satellite in ECEF frame. and also we know the attitude(i.e. yaw, pitch, roll) of the satellite in LVLH frame.

then the requirement is: get the attitude(i.e. yaw, pitch, roll) of the satellite in ECEF frame.

it is to implement the following function

Input:

  • satelliteAttitudeInLVLH: (yaw, pitch, roll),
  • satellitePositionInECEF: Vector3D,

Output:

  • SatelliteAttitudeInECEF: (yaw, pitch, roll),

we want to convert (yaw, pitch, roll), into a Vecotr
so it is equal to do

function(satelliteAttitudeInLVLH: Vector3D, satellitePositionInECEF: Vector3D)
Ouput is SatelliteAttitudeInECEF: Vector3D

You also need the velocity of the satellite, the position is not sufficient to bind LVLH frame to any other frame.

One you have both position and velocity, the way to do the computation is as follows:

  • create a provider corresponding to your PV coordinates in ECEF frame using pvProvider = new ConstantPVCoordinatesProvider(pvECEF, ecef)`
  • create attitude provider from inertial frame (for example EME2000) to spacecraft frame by applying the known angles using attProvider = new LofOffset(eme2000, LOFType.LVLH, RotationOrder.ZYX, yaw, pitch, roll), beware here you must use an inertial frame, you should not use ECEF otherwise velocity direction would be wrong
  • apply attitude provider to get the attitude directly in ECEF frame using attInECEF = attProvider.getAttitude(pvProvider, date, ecef). Note that internally, there will be two frames transforms used here, one to convert PV to inertial frame, and one to convert attitude back to ECEF, both are needed and important, you cannot simplify computation here

Another remark: using Vector3D to store 3 angular coordinates yaw, pitch, and roll is really not recommended. Vector3D represents Cartesian coordinates with a vector space algebra. You should rather create a dedicated container for your coordinates in order to avoid messing up.

1 Like

thanks @luc, the solution is really helpful and practical!

our main concern is the velocity of the satellite

we want to assume the velocity is always 0 , but this seems lead to an error : cannot normalize a zero norm vector (a PV with a zero velocity will lead to this problem when running the tests)

(so our recent conclusion is to use a value which is close to 0, but not exactly 0, for example 0.0011, but no idea whether the result from this approach is acceptable in terms of accuracy.)

So you are dealing with a perfectly geostationary satellite? Any other satellite would have a non-zero velocity in ECEF.

Yes, we want to simplify the model, and assumed it is a perfectly geostationary satellite for now…

hi @luc, we do assume a simplest mode in which we treat it as a perfectly geostationary satellite and the velocity of that satellite is zero.

is it possible to deal with this case, and avoid this problem?
: cannot normalize a zero norm vector (a PV with a zero velocity will lead to this problem when running the tests)

Could you show the code you wrote for implementing this, or at least the full stack trace of the error (in the message above, we only see the last few elements concerning the low level methods in Hipparchus.

@luc sorry for this late reponse…

the code was as below, and the main function was convertLVLHtoECEF
when the velocity of satellitePv: PVCoordinates is zero, then the error showed up

image

may i know is there anything wrong with this implementation?

class Converter {
    private val ecefFrame: Frame
    private val eme2000Frame: Frame

    init {
        OrekitLoader.loadOrekitData()
        ecefFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, false)
        eme2000Frame = FramesFactory.getEME2000()
    }

    fun convertLVLHtoECEF(
        lvlhPosition: Vector3D,
        satellitePv: PVCoordinates, // when the satellite's velocity is zero, it will trigger a problem
        date: AbsoluteDate,
    ): Vector3D {
        val eme2000Position = convertLVLHtoEME2000(lvlhPosition, satellitePv)
        return convertEME2000ToECEF(eme2000Position, date)
    }

    fun convertLVLHtoEME2000(
        lvlhPosition: Vector3D,
        satellitePvInEme2000: PVCoordinates,
    ): Vector3D {
        val rotate = LOFType.LVLH_CCSDS.rotationFromInertial(satellitePvInEme2000)
        return rotate.applyInverseTo(lvlhPosition)
    }

    fun convertEME2000ToECEF(
        eme2000Position: Vector3D,
        date: AbsoluteDate,
    ): Vector3D {
        return eme2000Frame.getTransformTo(ecefFrame, date).transformVector(eme2000Position)
    }

You should first build a complete position/velocity PVCoordinates instance from your ECEF position and zero vector velocity and then use the transformPVCoordinates method from the Transformclass to convert this into a PVCoordinates instance in inertial frame. Then you can apply the rotationFromInertial method.
The reason is that transforming position, transforming position/velocity and transforming vectors are three very different things. The one you need first is really transformPVCoordinates because it does apply frame velocity composition. This means that with your perfectly geostationary satellite, as it is 42000km away from Earth, then if it has a zero velocity with respect to Earth (it is geostationary), it will get a 3075m/s East/West velocity due to velocity composition from a rotating frame.

1 Like

yea, i will have a try based on this guidance!

thanks @luc that’s very helpful ! :slight_smile: