Yaw Compensation attitude provider

Hi, I have a question about the Yaw Compensation Attitude Provider, specifically the algorithm used to calculate the attitude.

I have been developing my own algorithm and testing it against the orekit YawCompensation provider, and have been getting an error of about 0.006 degrees.

To get the normal vector (which should be normal to the relative velocity wrt ground), the Spacecraft velocity obtained in an ECEF frame should be sufficient right? This should take into account the movement of the earth as well.

The code is as follows:

       #compute earth-fixed references at the specified date
        sat_PV = pvcoordinates_provider.getPVCoordinates(date, self.earth_fixed_frame)
        sat_pos = sat_PV.getPosition()
        sat_vel = sat_PV.getVelocity()

        # Compute nadir position in celestial frame
        gp_sat = self.earth_shape.transform(
            sat_PV.getPosition(), self.earth_fixed_frame, date
        )
        gp_nadir = GeodeticPoint(
            gp_sat.getLatitude(), gp_sat.getLongitude(), float(0.0)
        )
        nadir_pos = self.earth_shape.transform(gp_nadir)

        relativePosition = nadir_pos.subtract(sat_pos)

        relativeNormal = Vector3D.crossProduct(relativePosition, sat_vel)

        # compute transform from earth-fixed frame to satellite frame
        earthToSatRotation = Rotation(
            relativePosition, relativeNormal, Vector3D.PLUS_K, Vector3D.PLUS_J
        )

However, this does not produce a similar result. I am wondering where my thought process might be wrong. Would appreciate any help. Thanks!

Simple projections are not sufficient.
The behavior is really counter-intuitive, but we have to consider the ground curvature at target point. Since the Earth is an oblate spheroid and not a sphere, this curvature has two main components, one in the East-West direction and one in the North-Sourth direction, and these two curvatures are slightly different (these are called the principal curvatures of a surface). The travel direction of the target point is generally not aligned with either East-West or North-South, so we fly over the curved surface with a slanted path. Here is where this becomes highly counter-intuitive: due to these different curvatures, the projection of the velocity is not in the plane defined by spacecraft velocity, it is skewed. The effect is small, but not negligible, I guess your 0.006° corresponds to this.

The algorithm used in Yaw compensation takes this into account, but the exact method depends on the underlying ground pointing method (NadirPointing uses a 4 points finite difference, LofOffsetPointing uses a sampling interval around current date, BodyCenterPointing uses an analytical method).

I guess we could in fact rely on an analytical method for NadirPointing, because we now have one in OneAxisEllipsoid.projectToGround that does all the magic with curvatures.

So to summarize, it is (much) more complex than a simple velocity projection.