Example #1
0
        //Estimate the delta-V of the correction burn that would be required to put us on
        //course for the target
        public Vector3d ComputeCourseCorrection(bool allowPrograde)
        {
            //actualLandingPosition is the predicted actual landing position
            Vector3d actualLandingPosition = RotatedLandingSite - mainBody.position;

            //orbitLandingPosition is the point where our current orbit intersects the planet
            double   endRadius            = mainBody.Radius + DecelerationEndAltitude() - 100;
            Vector3d orbitLandingPosition = orbit.SwappedRelativePositionAtUT(orbit.NextTimeOfRadius(vesselState.time, endRadius));

            //convertOrbitToActual is a rotation that rotates orbitLandingPosition on actualLandingPosition
            Quaternion convertOrbitToActual = Quaternion.FromToRotation(orbitLandingPosition, actualLandingPosition);

            //Consider the effect small changes in the velocity in each of these three directions
            Vector3d[] perturbationDirections = { vesselState.velocityVesselSurfaceUnit, vesselState.radialPlusSurface, vesselState.normalPlusSurface };

            //Compute the effect burns in these directions would
            //have on the landing position, where we approximate the landing position as the place
            //the perturbed orbit would intersect the planet.
            Vector3d[] deltas = new Vector3d[3];
            for (int i = 0; i < 3; i++)
            {
                double perturbationDeltaV = 1;                                                                                      //warning: hard experience shows that setting this too low leads to bewildering bugs due to finite precision of Orbit functions
                Orbit  perturbedOrbit     = orbit.PerturbedOrbit(vesselState.time, perturbationDeltaV * perturbationDirections[i]); //compute the perturbed orbit
                double perturbedLandingTime;
                if (perturbedOrbit.PeR < endRadius)
                {
                    perturbedLandingTime = perturbedOrbit.NextTimeOfRadius(vesselState.time, endRadius);
                }
                else
                {
                    perturbedLandingTime = perturbedOrbit.NextPeriapsisTime(vesselState.time);
                }
                Vector3d perturbedLandingPosition = perturbedOrbit.SwappedRelativePositionAtUT(perturbedLandingTime); //find where it hits the planet
                Vector3d landingDelta             = perturbedLandingPosition - orbitLandingPosition;                  //find the difference between that and the original orbit's intersection point
                landingDelta = convertOrbitToActual * landingDelta;                                                   //rotate that difference vector so that we can now think of it as starting at the actual landing position
                landingDelta = Vector3d.Exclude(actualLandingPosition, landingDelta);                                 //project the difference vector onto the plane tangent to the actual landing position
                deltas[i]    = landingDelta / perturbationDeltaV;                                                     //normalize by the delta-V considered, so that deltas now has units of meters per (meter/second) [i.e., seconds]
            }

            //Now deltas stores the predicted offsets in landing position produced by each of the three perturbations.
            //We now figure out the offset we actually want

            //First we compute the target landing position. We have to convert the latitude and longitude of the target
            //into a position. We can't just get the current position of those coordinates, because the planet will
            //rotate during the descent, so we have to account for that.
            Vector3d   desiredLandingPosition         = mainBody.GetRelSurfacePosition(core.target.targetLatitude, core.target.targetLongitude, 0);
            float      bodyRotationAngleDuringDescent = (float)(360 * (prediction.endUT - vesselState.time) / mainBody.rotationPeriod);
            Quaternion bodyRotationDuringFall         = Quaternion.AngleAxis(bodyRotationAngleDuringDescent, mainBody.angularVelocity.normalized);

            desiredLandingPosition = bodyRotationDuringFall * desiredLandingPosition;

            Vector3d desiredDelta = desiredLandingPosition - actualLandingPosition;

            desiredDelta = Vector3d.Exclude(actualLandingPosition, desiredDelta);

            //Now desiredDelta gives the desired change in our actual landing position (projected onto a plane
            //tangent to the actual landing position).

            Vector3d downrangeDirection;
            Vector3d downrangeDelta;

            if (allowPrograde)
            {
                //Construct the linear combination of the prograde and radial+ perturbations
                //that produces the largest effect on the landing position. The Math.Sign is to
                //detect and handle the case where radial+ burns actually bring the landing sign closer
                //(e.g. when we are traveling close to straight up)
                downrangeDirection = (deltas[0].magnitude * perturbationDirections[0]
                                      + Math.Sign(Vector3d.Dot(deltas[0], deltas[1])) * deltas[1].magnitude * perturbationDirections[1]).normalized;

                downrangeDelta = Vector3d.Dot(downrangeDirection, perturbationDirections[0]) * deltas[0]
                                 + Vector3d.Dot(downrangeDirection, perturbationDirections[1]) * deltas[1];
            }
            else
            {
                //If we aren't allowed to burn prograde, downrange component of the landing
                //position has to be controlled by radial+/- burns:
                downrangeDirection = perturbationDirections[1];
                downrangeDelta     = deltas[1];
            }

            //Now solve a 2x2 system of linear equations to determine the linear combination
            //of perturbationDirection01 and normal+ that will give the desired offset in the
            //predicted landing position.
            Matrix2x2 A = new Matrix2x2(
                downrangeDelta.sqrMagnitude, Vector3d.Dot(downrangeDelta, deltas[2]),
                Vector3d.Dot(downrangeDelta, deltas[2]), deltas[2].sqrMagnitude
                );

            Vector2d b = new Vector2d(Vector3d.Dot(desiredDelta, downrangeDelta), Vector3d.Dot(desiredDelta, deltas[2]));

            Vector2d coeffs = A.inverse() * b;

            Vector3d courseCorrection = coeffs.x * downrangeDirection + coeffs.y * perturbationDirections[2];

            return(courseCorrection);
        }
        // Estimate the delta-V of the correction burn that would be required to put us on
        // course for the target
        public Vector3d ComputeCourseCorrection(bool allowPrograde)
        {
            // actualLandingPosition is the predicted actual landing position
            Vector3d actualLandingPosition = RotatedLandingSite - mainBody.position;

            // orbitLandingPosition is the point where our current orbit intersects the planet
            double endRadius = mainBody.Radius + DecelerationEndAltitude() - 100;

            // Seems we are already landed ?
            if (endRadius > orbit.ApR || vessel.LandedOrSplashed)
                StopLanding();

            Vector3d orbitLandingPosition;
            if (orbit.PeR < endRadius)
                orbitLandingPosition = orbit.SwappedRelativePositionAtUT(orbit.NextTimeOfRadius(vesselState.time, endRadius));
            else
                orbitLandingPosition = orbit.SwappedRelativePositionAtUT(orbit.NextPeriapsisTime(vesselState.time));

            // convertOrbitToActual is a rotation that rotates orbitLandingPosition on actualLandingPosition
            Quaternion convertOrbitToActual = Quaternion.FromToRotation(orbitLandingPosition, actualLandingPosition);

            // Consider the effect small changes in the velocity in each of these three directions
            Vector3d[] perturbationDirections = { vesselState.surfaceVelocity.normalized, vesselState.radialPlusSurface, vesselState.normalPlusSurface };

            // Compute the effect burns in these directions would
            // have on the landing position, where we approximate the landing position as the place
            // the perturbed orbit would intersect the planet.
            Vector3d[] deltas = new Vector3d[3];
            for (int i = 0; i < 3; i++)
            {
                const double perturbationDeltaV = 1; //warning: hard experience shows that setting this too low leads to bewildering bugs due to finite precision of Orbit functions
                Orbit perturbedOrbit = orbit.PerturbedOrbit(vesselState.time, perturbationDeltaV * perturbationDirections[i]); //compute the perturbed orbit
                double perturbedLandingTime;
                if (perturbedOrbit.PeR < endRadius) perturbedLandingTime = perturbedOrbit.NextTimeOfRadius(vesselState.time, endRadius);
                else perturbedLandingTime = perturbedOrbit.NextPeriapsisTime(vesselState.time);
                Vector3d perturbedLandingPosition = perturbedOrbit.SwappedRelativePositionAtUT(perturbedLandingTime); //find where it hits the planet
                Vector3d landingDelta = perturbedLandingPosition - orbitLandingPosition; //find the difference between that and the original orbit's intersection point
                landingDelta = convertOrbitToActual * landingDelta; //rotate that difference vector so that we can now think of it as starting at the actual landing position
                landingDelta = Vector3d.Exclude(actualLandingPosition, landingDelta); //project the difference vector onto the plane tangent to the actual landing position
                deltas[i] = landingDelta / perturbationDeltaV; //normalize by the delta-V considered, so that deltas now has units of meters per (meter/second) [i.e., seconds]
            }

            // Now deltas stores the predicted offsets in landing position produced by each of the three perturbations. 
            // We now figure out the offset we actually want

            // First we compute the target landing position. We have to convert the latitude and longitude of the target
            // into a position. We can't just get the current position of those coordinates, because the planet will
            // rotate during the descent, so we have to account for that.
            Vector3d desiredLandingPosition = mainBody.GetRelSurfacePosition(core.target.targetLatitude, core.target.targetLongitude, 0);
            float bodyRotationAngleDuringDescent = (float)(360 * (prediction.endUT - vesselState.time) / mainBody.rotationPeriod);
            Quaternion bodyRotationDuringFall = Quaternion.AngleAxis(bodyRotationAngleDuringDescent, mainBody.angularVelocity.normalized);
            desiredLandingPosition = bodyRotationDuringFall * desiredLandingPosition;

            Vector3d desiredDelta = desiredLandingPosition - actualLandingPosition;
            desiredDelta = Vector3d.Exclude(actualLandingPosition, desiredDelta);

            // Now desiredDelta gives the desired change in our actual landing position (projected onto a plane
            // tangent to the actual landing position).

            Vector3d downrangeDirection;
            Vector3d downrangeDelta;
            if (allowPrograde)
            {
                // Construct the linear combination of the prograde and radial+ perturbations 
                // that produces the largest effect on the landing position. The Math.Sign is to
                // detect and handle the case where radial+ burns actually bring the landing sign closer
                // (e.g. when we are traveling close to straight up)
                downrangeDirection = (deltas[0].magnitude * perturbationDirections[0]
                    + Math.Sign(Vector3d.Dot(deltas[0], deltas[1])) * deltas[1].magnitude * perturbationDirections[1]).normalized;

                downrangeDelta = Vector3d.Dot(downrangeDirection, perturbationDirections[0]) * deltas[0]
                    + Vector3d.Dot(downrangeDirection, perturbationDirections[1]) * deltas[1];
            }
            else
            {
                // If we aren't allowed to burn prograde, downrange component of the landing
                // position has to be controlled by radial+/- burns:
                downrangeDirection = perturbationDirections[1];
                downrangeDelta = deltas[1];
            }

            // Now solve a 2x2 system of linear equations to determine the linear combination
            // of perturbationDirection01 and normal+ that will give the desired offset in the
            // predicted landing position.
            Matrix2x2 A = new Matrix2x2(
                downrangeDelta.sqrMagnitude, Vector3d.Dot(downrangeDelta, deltas[2]),
                Vector3d.Dot(downrangeDelta, deltas[2]), deltas[2].sqrMagnitude
            );

            Vector2d b = new Vector2d(Vector3d.Dot(desiredDelta, downrangeDelta), Vector3d.Dot(desiredDelta, deltas[2]));

            Vector2d coeffs = A.inverse() * b;

            Vector3d courseCorrection = coeffs.x * downrangeDirection + coeffs.y * perturbationDirections[2];

            return courseCorrection;
        }
        ///////////////////////////////////////
        // REENTRY SIMULATION /////////////////
        ///////////////////////////////////////
        //figure out what we wish our velocity were right now if we want to land at the user's target
        //we do this by first doing a pair of hypothetical simulations, which we have extra velocity in
        //two different orthogonal directions. By observing how this affects the landing site we compute the change in our velocity
        //necessary to produce the desired change in our predicted landing location
        void computeVelocityCorrection()
        {
            double offsetMagnitude = -5.0;

            //simulate a trajectory where we have extra velocity in the A direction
            LandingPrediction resultA = simulateReentryRK4(simulationTimeStep * 10.0, offsetMagnitude * velAUnit);
            //if simulation doesn't hit the ground, we should burn retrograde until it does. set a goal that will cause us to deorbit
            if (resultA.outcome != LandingPrediction.Outcome.LANDED)
            {
                if (landStep == LandStep.COURSE_CORRECTIONS && resultA.outcome == LandingPrediction.Outcome.NO_REENTRY)
                {
                    gaveUpOnCourseCorrections = true;
                    turnOffSteering();
                    core.controlRelease(this);
                    print("A: gaveup, end condition = " + resultA.outcome);
                }
                velocityCorrection = -10 * vesselState.velocityVesselSurfaceUnit;
                return;
            }

            //simulate a trajectory where we have extra velocity in the B direction
            LandingPrediction resultB = simulateReentryRK4(simulationTimeStep * 10.0, offsetMagnitude * velBUnit);
            if (resultB.outcome != LandingPrediction.Outcome.LANDED)
            {
                if (landStep == LandStep.COURSE_CORRECTIONS && resultB.outcome == LandingPrediction.Outcome.NO_REENTRY)
                {
                    gaveUpOnCourseCorrections = true;
                    turnOffSteering();
                    core.controlRelease(this);
                    print("B: gaveup, end condition = " + resultB.outcome);
                }
                velocityCorrection = -10 * vesselState.velocityVesselSurfaceUnit;
                return;
            }

            //simulate a trajectory with our current velocity
            LandingPrediction resultCurrent = simulateReentryRK4(simulationTimeStep * 10.0, Vector3d.zero);
            if (resultCurrent.outcome != LandingPrediction.Outcome.LANDED)
            {
                if (landStep == LandStep.COURSE_CORRECTIONS && resultCurrent.outcome == LandingPrediction.Outcome.NO_REENTRY)
                {
                    gaveUpOnCourseCorrections = true;
                    turnOffSteering();
                    core.controlRelease(this);
                    print("C: gaveup, end condition = " + resultCurrent.outcome);
                }
                velocityCorrection = -10 * vesselState.velocityVesselSurfaceUnit;
                return;
            }

            Vector2d sepA = latLonSeparation(resultCurrent.landingLatitude, resultCurrent.landingLongitude,
                                             resultA.landingLatitude, resultA.landingLongitude) / offsetMagnitude;

            Vector2d sepB = latLonSeparation(resultCurrent.landingLatitude, resultCurrent.landingLongitude,
                                 resultB.landingLatitude, resultB.landingLongitude) / offsetMagnitude;

            //construct a matrix that, when multiplied by a velocity offset vector2, gives the resulting offset in the landing position
            Matrix2x2 partialsMatrix = new Matrix2x2(sepA.x, sepB.x, sepA.y, sepB.y);

            //invert this to get a matrix that tells how to change our velocity to produce a
            //give change in our landing site:
            Matrix2x2 inversePartialsMatrix = partialsMatrix.inverse();

            Vector2d sepDesired = latLonSeparation(prediction.landingLatitude, prediction.landingLongitude, targetLatitude, targetLongitude);

            Vector2d correctionMagnitudes = inversePartialsMatrix.times(sepDesired);

            velocityCorrection = correctionMagnitudes.x * velAUnit + correctionMagnitudes.y * velBUnit;
            velocityCorrectionSet = true;
        }