/// <summary>
        /// Finds a point on the glide slope intercept where the angle between
        /// vessel and the point is lateralInterceptAngle degrees.
        /// </summary>
        /// <param name="finalApproachVector"></param>
        /// <param name="lateralAngleOfFinalApproachVector"></param>
        /// <returns></returns>
        private Vector3d FindVectorToGlideslopeIntercept(Vector3d finalApproachVector, double lateralAngleOfFinalApproachVector)
        {
            // Determine the three angles of the triangle, one of which is
            // the lateral angle of final approach vector, and the other is
            // 180 - lateral intercept angle.
            double theta = 180 - lateralInterceptAngle;
            double omega = 180 - lateralAngleOfFinalApproachVector - theta;

            // We know the lateral distance to the final approach point, we
            // want to find a point on the glide slope which we can
            // intercept at a given angle.
            double dist = (LateralDistance(vesselState.CoM, finalApproachVector) * Math.Sin(UtilMath.Deg2Rad * omega)) / Math.Sin(UtilMath.Deg2Rad * theta);

            // If this is a bad intercept, proceed to IAP.
            if (dist < lateralDistanceFromTouchdownToFinalApproach)
            {
                approachState = AutolandApproachState.IAP;
                return(runway.GetPointOnGlideslope(glideslope, GetAutolandLateralDistanceFromTouchdownToFinalApproach() - runway.touchdownPoint));
            }

            return(runway.GetPointOnGlideslope(glideslope, dist + lateralDistanceFromTouchdownToFinalApproach));
        }
        /// <summary>
        /// Computes and returns the target vector for approach and autoland.
        /// </summary>
        /// <returns></returns>
        public Vector3d GetAutolandTargetVector()
        {
            // positions of the start and end of the runway
            Vector3d runwayStart = runway.GetVectorToTouchdown();
            Vector3d runwayEnd   = runway.End();

            // get the initial and final approach vectors
            Vector3d initialApproachVector = runway.GetPointOnGlideslope(glideslope, GetAutolandLateralDistanceFromTouchdownToFinalApproach() - runway.touchdownPoint);
            Vector3d finalApproachVector   = runway.GetPointOnGlideslope(glideslope, lateralDistanceFromTouchdownToFinalApproach - runway.touchdownPoint);

            // determine whether the vessel is within the approach cone or not
            Vector3d finalApproachVectorProjectedOnGroundPlane   = finalApproachVector.ProjectOnPlane(runway.Up());
            Vector3d initialApproachVectorProjectedOnGroundPlane = initialApproachVector.ProjectOnPlane(runway.Up());
            Vector3d runwayDirectionVectorProjectedOnGroundPlane = (runwayEnd - runwayStart).ProjectOnPlane(runway.Up());

            double lateralAngleOfFinalApproachVector   = Vector3d.Angle(finalApproachVectorProjectedOnGroundPlane, runwayDirectionVectorProjectedOnGroundPlane);
            double lateralAngleOfInitialApproachVector = Vector3d.Angle(initialApproachVectorProjectedOnGroundPlane, runwayDirectionVectorProjectedOnGroundPlane);

            if (approachState == AutolandApproachState.START)
            {
                if (lateralAngleOfFinalApproachVector < lateralApproachConeAngle)
                {
                    // We are within the approach cone, we can skip IAP and
                    // instead start intercepting the glideslope.
                    approachState = AutolandApproachState.GLIDESLOPEINTERCEPT;
                    return(FindVectorToGlideslopeIntercept(finalApproachVector, lateralAngleOfFinalApproachVector));
                }

                approachState = AutolandApproachState.IAP;
                return(initialApproachVector);
            }
            else if (approachState == AutolandApproachState.IAP)
            {
                if (lateralAngleOfInitialApproachVector > 180 - lateralApproachConeAngle)
                {
                    // We are within the "bad" cone. We have to go all the way
                    // to IAP without cutting corners.
                    return(initialApproachVector);
                }

                if (lateralAngleOfFinalApproachVector < lateralApproachConeAngle)
                {
                    // We are in the approach cone, start glideslope intercept.
                    approachState = AutolandApproachState.GLIDESLOPEINTERCEPT;
                    return(FindVectorToGlideslopeIntercept(finalApproachVector, lateralAngleOfFinalApproachVector));
                }

                return(initialApproachVector);
            }
            else if (approachState == AutolandApproachState.GLIDESLOPEINTERCEPT)
            {
                Vector3d vectorToGlideslopeIntercept = FindVectorToGlideslopeIntercept(finalApproachVector, lateralAngleOfFinalApproachVector);

                // Determine whether we should start turning towards FAP.
                double estimatedTimeToTurn       = lateralInterceptAngle / GetAutolandMaxRateOfTurn();
                double timeToGlideslopeIntercept = LateralDistance(vesselState.CoM, vectorToGlideslopeIntercept) / vesselState.speedSurfaceHorizontal;

                if (estimatedTimeToTurn >= timeToGlideslopeIntercept)
                {
                    approachState = AutolandApproachState.FAP;
                    return(finalApproachVector);
                }

                // Otherwise, continue flying towards the glideslope intercept.
                return(vectorToGlideslopeIntercept);
            }
            else if (approachState == AutolandApproachState.FAP)
            {
                if (lateralAngleOfFinalApproachVector > lateralInterceptAngle)
                {
                    // Cancel final approach, go back to initial approach.
                    approachState = AutolandApproachState.IAP;
                    return(initialApproachVector);
                }

                double timeToFAP = LateralDistance(vesselState.CoM, finalApproachVector) / vesselState.speedSurfaceHorizontal;

                if (GetAutolandAlignmentError(finalApproachVector) < 3.0 && timeToFAP < secondsThresholdToNextWaypoint)
                {
                    approachState = AutolandApproachState.TOUCHDOWN;
                    return(runway.GetVectorToTouchdown());
                }

                return(finalApproachVector);
            }
            else if (approachState == AutolandApproachState.TOUCHDOWN)
            {
                double timeToTouchdown = LateralDistance(vesselState.CoM, runway.GetVectorToTouchdown()) / vesselState.speedSurfaceHorizontal;

                if (vesselState.altitudeTrue < startFlareAtAltitude + 10)
                {
                    approachState = AutolandApproachState.WAITINGFORFLARE;
                    return(runway.End());
                }

                return(runway.GetVectorToTouchdown());
            }
            else if (approachState == AutolandApproachState.WAITINGFORFLARE)
            {
                if (vesselState.altitudeTrue < startFlareAtAltitude)
                {
                    approachState = AutolandApproachState.FLARE;
                    flareStartAoA = vesselState.AoA;
                }

                return(runway.End());
            }
            else if (approachState == AutolandApproachState.FLARE)
            {
                if (vessel.Landed)
                {
                    touchdownMomentAoA   = vesselState.AoA;
                    touchdownMomentSpeed = vesselState.speedSurfaceHorizontal;
                    approachState        = AutolandApproachState.ROLLOUT;
                }

                return(runway.End());
            }
            else if (approachState == AutolandApproachState.ROLLOUT)
            {
                if (vesselState.speedSurface < 1.0)
                {
                    AutopilotOff();
                }

                return(runway.End());
            }

            Debug.Assert(false);
            return(runway.Start());
        }