Local Aircraft Orientation to ECEF

Given the:

  • position of an aircraft in degrees and altitude above the ellipsoid (lat, lng, alt) (acquired from the GPS position of the aircraft)
  • the orientation of the aircraft in degrees (yaw, pitch roll) where yaw E (-180, 180), pitch E (-90, 90) and roll E (-180, 180).

Where:

  • Yaw means rotation from north with positive angles to the east and negative angles to the west.
  • Pitch means angles from the horizon with positive being above the horizon and negative being below the horizon.
  • Roll means rotation about the direction of travel of the aircraft with positive roll being clockwise and negative roll being counterclockwise.

How do I compute the orientation of the aircraft in the ECEF frame?

What I’ve tried is below

// Build a Quaternion that represents the rotation between absolute and
// local-surface frames of reference
const transform4 = Cesium.Transforms.northEastDownToFixedFrame(position)
const transform3 = Cesium.Matrix4.getMatrix3(transform4, new Cesium.Matrix3())
const transformQ = Cesium.Quaternion.fromRotationMatrix(transform3)
Cesium.Quaternion.normalize(transformQ, transformQ)    
// Convert the h/p/r values to a (local-reference) Quaternion
const localHpr = Cesium.HeadingPitchRoll.fromDegrees(heading, pitch, roll)
const localQ = Cesium.Quaternion.fromHeadingPitchRoll(localHpr, new Cesium.Quaternion())
Cesium.Quaternion.normalize(localQ, localQ)    
// Result is local times transform
const ret = new Cesium.Quaternion()
Cesium.Quaternion.multiply(transformQ, localQ, ret)
Cesium.Quaternion.normalize(ret, ret)

but this does not give the correct orientation when visualized as a frustum. Frustum visualization code below

const frustumGeometry = new Cesium.FrustumOutlineGeometry({
frustum: frustum,
origin: origin,
orientation: orientation,
})

const frustumOutlineGeometryInstance = new Cesium.GeometryInstance({
geometry: frustumGeometry,
attributes: {
  color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED),
},
})

const fp = viewer.scene.primitives.add(
  new Cesium.Primitive({
    geometryInstances: frustumOutlineGeometryInstance,
    appearance: new Cesium.PerInstanceColorAppearance({
      closed: true,
      flat: true,
    }),
  })
)

where origin is the ECEF Cesium.Cartesian3 and orientation is the quaternion ret.