Exemple #1
0
        /// <summary>
        /// Recalculate CurrentTransition value.
        /// </summary>
        public void CalculateTransitionState()
        {
            var refs = GetBodiesReferences();

            if (refs.Attractor != null)
            {
                Vector3d startPoint       = GetPositionAtGivenTime(refs.Origin, (StartTimeOffset), refs.OriginAttractorsChain);
                Vector3d endPoint         = GetPositionAtGivenTime(refs.Target, (StartTimeOffset + TargetDuration), refs.TargetAttractorsChain);
                var      attractorPosHalf = refs.Attractor.position;
                var      attractorPos     = new Vector3d(attractorPosHalf.x, attractorPosHalf.y, attractorPosHalf.z);
                var      hyperbola        = new HyperbolaData(new Vector3d(startPoint.x, startPoint.y, startPoint.z), endPoint, attractorPos);
                var      trajectory       = CalcTransitionTrajectory(
                    startPoint,
                    endPoint,
                    attractorPos,
                    hyperbola,
                    this.TargetDuration,
                    isReverseOrbit: this.IsTransitionPassBehindAttractor,
                    attrMass: refs.AttractorMass,
                    g: refs.GConst,
                    precision: this.TargetDurationPrecision,
                    semiMajorAxisUpperLimit: this.MaxTransitionSemiMajorAxis);

                if (trajectory.orbit != null && trajectory.orbit.IsValidOrbit && trajectory.Duration > 0 && !double.IsInfinity(trajectory.Duration))
                {
                    var velocityDiff = CalculateVelocityDifference(refs.Origin, refs.Target, trajectory.orbit, StartTimeOffset, trajectory.Duration, trajectory.EccAnomStart, trajectory.EccAnomEnd);
                    var totalDeltaV  = 0.0;
                    for (int i = 0; i < velocityDiff.Count; i++)
                    {
                        totalDeltaV += velocityDiff[i].magnitude;
                    }

                    _currentTransition = new TransitionOrbitData()
                    {
                        Attractor          = refs.Attractor,
                        Duration           = (float)trajectory.Duration,
                        EccAnomalyStart    = (float)trajectory.EccAnomStart,
                        EccAnomalyEnd      = (float)trajectory.EccAnomEnd,
                        ImpulseDifferences = velocityDiff,
                        Orbit       = trajectory.orbit,
                        TotalDeltaV = (float)totalDeltaV,
                    };
                }
                else
                {
                    _currentTransition = null;
                }
            }
            else
            {
                _currentTransition = null;
            }
        }
        /// <summary>
        /// Caclulate transition tranjectory from point p0 to point p1 around attractor f0.
        /// </summary>
        /// <param name="p0">First position vector.</param>
        /// <param name="p1">Second position vector.</param>
        /// <param name="f0">Attractor position vector.</param>
        /// <param name="hyperbola">Trajectory hyperbola.</param>
        /// <param name="targetDuration">Preferred duration.</param>
        /// <param name="isReverseOrbit">Is transfer orbit plane flipped.</param>
        /// <param name="attrMass">Attractor mass.</param>
        /// <param name="g">Gravity constant.</param>
        /// <param name="precision">Calculation precision.</param>
        /// <param name="semiMajorAxisUpperLimit">Transition ellipse semi major axis limit.</param>
        /// <returns>Calculated trajectory data.</returns>
        /// <remarks>
        /// Main task of this component is to find elliptic trajectory between any 2 points, orbiting around single attractor.
        /// The core problem can be described in more formal form:
        /// let's take a body A and body B; this two bodies rotating around attractor F. The Goal is to find an orbit around attractor F, which passes through vector A and B.
        /// This problem is equivalent to to problem of finding of an ellipse when only one focus and two points on ellipse are given,
        /// which is known as Lambert's problem (but only with one allowed revolution around attractor). The answer to this problem actually is quite simple. In simplified form the solution can be described in these steps:
        /// 1. Build a hyperbola, using given points A and B as focuses of hyperbola, and attractor F as point on one branch of hyperbola.
        /// 2. Place a new point F2 anywhere on branch opposite to main branch of hyperbola ( where main branch is branch, on which F is located)
        /// 3. Build an ellipse using 3 points: F as first focus, new point F2 as second focus, A or B as one point on ellipse.
        /// 4. As result, created ellipse will always exactly correspond to transition orbit, and it always will pass through A and B.
        /// Eccentricity of ellipse is dependent on where was placed point F2, which is convenient as it can be tweaked for better orbit parameters.
        ///
        /// Each step of this solution is pretty strait forward. With help of hyperbola, given data of 1 focus and 2 points is converting to 2 focuses and 1 point.
        ///
        /// With known ellipse parameters it is easy to construct orbit data of transition, and additionally it is possible to calculate mean anomaly of departure and arrival.
        /// Difference between these mean anomalies multiplied by mean motion gives exact duration of transition.
        /// Because time of transition is depending from second focus point F2 (which is parametric), it is possible to set some specific transition time value
        /// and then adjust F2 to match target time as close as possible.
        /// </remarks>
        public static TrajectoryData CalcTransitionTrajectory(Vector3d p0, Vector3d p1, Vector3d f0, HyperbolaData hyperbola, double targetDuration, bool isReverseOrbit, double attrMass, double g, double precision, double semiMajorAxisUpperLimit)
        {
            TrajectoryData result                = new TrajectoryData();
            double         hyperbolaValue        = 0.0;
            int            lastDeltaSign         = 0;
            int            changedDeltaSignCount = 0;
            float          delta              = 0.8f;
            double         lastDuration       = 0;
            int            tmp                = 0;
            Vector3d       ellipseSecondFocus = new Vector3d();

            // Calculate transition multiple times until optimal transition duration not found.
            // Usually steps count is not larger than a hundred, so 1000 iterations limit is for fail checking.
            while (true && tmp < 1e3)
            {
                tmp++;
                bool isBranch0 = (p0 - f0).magnitude < (p1 - f0).magnitude;
                ellipseSecondFocus = hyperbola.GetSamplePointOnBranch(hyperbolicCoordinate: hyperbolaValue, isMainBranch: isBranch0);
                var transitionOrbitEllipse = new EllipseData(focus0: f0, focus1: ellipseSecondFocus, p0: p0);
                if (KeplerOrbitUtils.DotProduct(transitionOrbitEllipse.Normal, hyperbola.Normal) <= 0)
                {
                    transitionOrbitEllipse.AxisSecondary *= -1;
                }
                if (transitionOrbitEllipse.A > semiMajorAxisUpperLimit)
                {
                    break;
                }
                if (isReverseOrbit)
                {
                    transitionOrbitEllipse.AxisSecondary = -transitionOrbitEllipse.AxisSecondary;
                }
                result.orbit = new KeplerOrbitData(
                    eccentricity: transitionOrbitEllipse.Eccentricity,
                    semiMajorAxis: transitionOrbitEllipse.AxisMain * transitionOrbitEllipse.A,
                    semiMinorAxis: transitionOrbitEllipse.AxisSecondary * transitionOrbitEllipse.B,
                    meanAnomalyDeg: 0,
                    attractorMass: attrMass,
                    gConst: g);
                result.EccAnomStart = transitionOrbitEllipse.GetEccentricAnomalyForPoint(p0);
                result.EccAnomEnd   = transitionOrbitEllipse.GetEccentricAnomalyForPoint(p1);

                if (result.EccAnomStart > result.EccAnomEnd)
                {
                    result.EccAnomEnd += KeplerOrbitUtils.PI_2;
                }

                var meanAnomStart = KeplerOrbitUtils.ConvertEccentricToMeanAnomaly(result.EccAnomStart, eccentricity: result.orbit.Eccentricity);
                var meanAnomEnd   = KeplerOrbitUtils.ConvertEccentricToMeanAnomaly(result.EccAnomEnd, eccentricity: result.orbit.Eccentricity);
                var meanAnomDiff  = meanAnomEnd - meanAnomStart;
                result.Duration = meanAnomEnd <= meanAnomStart ? 0.0 : meanAnomDiff / result.orbit.MeanMotion;
                var diff = result.Duration - targetDuration;
                int sign = diff >= 0 ? -1 : 1;
                if (KeplerOrbitUtils.Abs(diff) < precision)
                {
                    break;
                }
                if (sign != lastDeltaSign)
                {
                    lastDeltaSign = sign;
                    changedDeltaSignCount++;
                }
                if (changedDeltaSignCount >= 2)
                {
                    delta *= 0.5f;
                }
                int conicShapeAligmentSign = KeplerOrbitUtils.DotProduct(transitionOrbitEllipse.Normal, hyperbola.Normal) >= 0 ? 1 : -1;
                hyperbolaValue += delta * sign * conicShapeAligmentSign;
                var stepDurationDiff = result.Duration - lastDuration;
                if (KeplerOrbitUtils.Abs(stepDurationDiff) < precision)
                {
                    break;
                }
                lastDuration = result.Duration;
            }
            return(result);
        }