示例#1
0
        private static void AdjustManeuver(BurnModel burn, Vector3d direction, double fraction = 1.0)
        {
            const double DELTA_V_INCREMENT_LARGE = 0.5,
                         DELTA_V_INCREMENT_SMALL = 0.01;

            if (burn != null && FlightGlobals.ActiveVessel != null)
            {
                ManeuverNode n = burn?.node;
                if (n != null)
                {
                    if (GameSettings.MODIFIER_KEY.GetKey())
                    {
                        n.DeltaV += DELTA_V_INCREMENT_SMALL * fraction * direction;
                    }
                    else
                    {
                        n.DeltaV += DELTA_V_INCREMENT_LARGE * fraction * direction;
                    }
                    if (n.attachedGizmo != null)
                    {
                        n.attachedGizmo.DeltaV = n.DeltaV;
                        try {
                            n.OnGizmoUpdated(n.DeltaV, burn.atTime ?? 0);
                        } catch (Exception ex) {
                            DbgExc("Problem updating gizmo", ex);
                        }
                    }
                    n.solver.UpdateFlightPlan();
                }
            }
        }
示例#2
0
        /// <summary>
        /// Calculate the time and delta V of the burn needed to transfer.
        /// </summary>
        public void CalculateEjectionBurn()
        {
            if (origin != null)
            {
                ejectionBurn = GenerateEjectionBurn(origin.GetOrbit());
            }
            else
            {
                ejectionBurn = null;
            }

            if (planeChangeBurn != null && ejectionBurn != null)
            {
                if (planeChangeBurn.atTime < ejectionBurn.atTime)
                {
                    DbgFmt("Resetting plane change burn because it's too early now");
                    planeChangeBurn = null;
                }
            }
        }
示例#3
0
        /// <summary>
        /// Calculate the time and delta V of the burn needed to change planes.
        /// </summary>
        public void CalculatePlaneChangeBurn()
        {
            if (FlightGlobals.ActiveVessel?.patchedConicSolver?.maneuverNodes != null &&
                transferDestination != null &&
                transferParent != null &&
                destination.GetOrbit().eccentricity < 1)
            {
                bool ejectionAlreadyActive = false;

                if (FlightGlobals.ActiveVessel.patchedConicSolver.maneuverNodes.Count > 0)
                {
                    if (Settings.Instance.DeleteExistingManeuvers)
                    {
                        ClearManeuverNodes();
                    }
                    else if (FlightGlobals.ActiveVessel.patchedConicSolver.maneuverNodes.Count == 1 &&
                             ejectionBurn.node != null)
                    {
                        ejectionAlreadyActive = true;
                    }
                    else
                    {
                        // At least one unrelated maneuver is active, and we're not allowed to delete them.
                        // Can't activate ejection burn for calculation.
                        return;
                    }
                }

                if (ejectionBurn != null)
                {
                    ManeuverNode eNode;
                    if (ejectionAlreadyActive)
                    {
                        eNode = ejectionBurn.node;
                    }
                    else
                    {
                        DbgFmt("Temporarily activating ejection burn to {0}", destination.GetName());
                        eNode = ejectionBurn.ToActiveManeuver();
                        DbgFmt("Activated ejection burn to {0}", destination.GetName());
                    }

                    if (eNode != null)
                    {
                        if (eNode.nextPatch == null)
                        {
                            DbgFmt("This node goes nowhere.");
                        }

                        // Find the orbit patch that intersects the target orbit
                        for (Orbit o = eNode.nextPatch; o != null; o = NextPatch(o))
                        {
                            // Skip the patches that are in the wrong SoI
                            if (o.referenceBody == transferParent)
                            {
                                DbgFmt("Identified matching reference body for {0}", transferParent.GetName());

                                // Find the AN or DN
                                bool   ascendingNode;
                                double planeTime = TimeOfPlaneChange(o, transferDestination.GetOrbit(), ejectionBurn.atTime ?? 0, out ascendingNode);

                                DbgFmt("Pinpointed plane change for {0}", transferParent.GetName());

                                if (planeTime > 0 && planeTime > ejectionBurn.atTime)
                                {
                                    double magnitude = PlaneChangeDeltaV(o, transferDestination.GetOrbit(), planeTime, ascendingNode);
                                    // Don't bother to create tiny maneuver nodes
                                    if (Math.Abs(magnitude) > 0.05)
                                    {
                                        // Add a maneuver node to change planes
                                        planeChangeBurn = new BurnModel(planeTime, 0,
                                                                        magnitude);
                                        DbgFmt("Transmitted correction burn for {0}: {1}", transferDestination.GetName(), magnitude);
                                    }
                                    else
                                    {
                                        planeChangeBurn = null;
                                        DbgFmt("No plane change needed for {0}", transferDestination.GetName());
                                    }

                                    // Stop looping through orbit patches since we found what we want
                                    break;
                                }
                                else
                                {
                                    DbgFmt("Plane change burn would be before the ejection burn, skipping");
                                }
                            }
                            else
                            {
                                DbgFmt("Skipping a patch with the wrong parent body");
                            }
                        }
                    }
                    else
                    {
                        DbgFmt("Ejection burn existed but generated a null node");
                    }

                    if (!ejectionAlreadyActive)
                    {
                        // Clean up the node since we're just doing calculations, not intending to set things up for the user
                        ejectionBurn.RemoveNode();
                    }
                    DbgFmt("Released completed transfer to {0}", destination.GetName());
                }
                else
                {
                    DbgFmt("Ejection burn is missing somehow");
                }
            }
            else
            {
                DbgFmt("Can't do a plane change without a vessel");
            }
        }
示例#4
0
        private BurnModel GenerateEjectionBurnFromOrbit(Orbit currentOrbit, bool fakeOrbit = false)
        {
            DbgFmt("Looking for a route from {0} to {1}, via {2}", TheName(origin), TheName(destination), TheName(currentOrbit.referenceBody));

            if (currentOrbit == null)
            {
                DbgFmt("Skipping transfer from null starting orbit.");
                // Sanity check just in case something unexpected happens.
                return(null);
            }
            else if (destination == null)
            {
                DbgFmt("Skipping transfer to null destination.");
                // Sanity check just in case something unexpected happens.
                return(null);
            }
            else if (destination.GetOrbit().eccentricity > 1)
            {
                DbgFmt("{0} is on an escape trajectory; bailing", TheName(destination));
                return(null);
            }
            else if (currentOrbit.eccentricity > 1.0)
            {
                return(PlotCaptureBurn(currentOrbit));
            }
            else
            {
                BurnModel b = FindIntermediateDestination(currentOrbit.referenceBody, currentOrbit);
                if (b != null)
                {
                    // If that function generated a burn, that means this destination
                    // is a "return to parent" scenario.
                    // Otherwise we have to continue calculating.
                    DbgFmt("Got return to parent burn");
                    return(b);
                }

                // The above function sets this if we're in the transfer patch.
                if (transferDestination != null)
                {
                    // Base case - calculate a simple Hohmann transfer

                    retrogradeTransfer = (currentOrbit.GetRelativeInclination(transferDestination.GetOrbit()) > 90f);
                    if (!retrogradeTransfer)
                    {
                        return(PlotTransferBurn(currentOrbit));
                    }
                    else
                    {
                        return(PlotRetrogradeTransferBurn(currentOrbit));
                    }
                }
                else
                {
                    // Recursive case - get an orbit from the parent body and adjust it for ejection from here
                    DbgFmt("Direct route to {0} not found, recursing through parent {1}", TheName(destination), TheName(currentOrbit.referenceBody));

                    return(PlotEjectionBurn(currentOrbit, fakeOrbit));
                }
            }
        }
示例#5
0
        private BurnModel PlotLaunchToEjection()
        {
            DbgFmt("Launching to ejection");

            double        now           = Planetarium.GetUniversalTime();
            bool          haveVessel    = (origin.GetVessel() != null);
            CelestialBody body          = origin as CelestialBody ?? origin.GetOrbit().referenceBody;
            double        targetRadius  = GoodLowOrbitRadius(body);
            bool          atHome        = (body == FlightGlobals.GetHomeBody());
            bool          haveLongitude = haveVessel || atHome;

            if (!body.rotates)
            {
                // A non-rotating body means we never get any closer to the
                // point where we want to escape. No calculation possible.
                // (This also lets us sidestep a divide by zero risk.)
                return(null);
            }

            // This will give us a burn with the right delta V from low orbit.
            // The time will be now plus the time it takes to get from the absolute
            // reference direction to the burn at the orbital speed of fakeOrbit.
            Orbit fakeOrbit = new Orbit(0, 0, targetRadius, 0, 0, 0, 0, body);
            // This variable prevents infinite recursion between a body and fake orbits around it.
            BurnModel ejection = GenerateEjectionBurnFromOrbit(fakeOrbit, true);

            DbgFmt("Ejection time null: {0}", (ejection.atTime == null));

            if (haveLongitude && ejection.atTime != null)
            {
                DbgFmt("Using real longitude and ejection time");
                double startingLongitude =
                    haveVessel ? origin.GetVessel().longitude
                                        : atHome ? SpaceCenter.Instance.Longitude
                                        : 0;

                // Now we figure out where the vessel (or KSC) will be at the time of that burn.
                double currentPhaseAngle = AbsolutePhaseAngle(
                    body,
                    ejection.atTime ?? now,
                    startingLongitude
                    );

                // This will tell us approximately where our ship should be to launch
                double targetAbsolutePhaseAngle = AbsolutePhaseAngle(fakeOrbit, ejection.atTime ?? now);

                // This tells us how fast the body rotates.
                // Note that we already have our divide-by-zero guard above.
                double phaseAnglePerSecond = Tau / body.rotationPeriod;

                // Now we adjust the original burn time to account for the planet rotating
                // into position for us.
                double burnTime = (ejection.atTime ?? now) + clamp(targetAbsolutePhaseAngle - currentPhaseAngle) / phaseAnglePerSecond;

                // Finally, generate the real burn if it seems OK.
                if (burnTime < now)
                {
                    return(null);
                }
                else
                {
                    return(new BurnModel(
                               burnTime,
                               ejection.prograde + DeltaVToOrbit(body)
                               ));
                }
            }
            else
            {
                DbgFmt("Longitude or ejection time missing, using outer burn time");
                // If we're at a body with no launch pad, we can't time the maneuver,
                // so just use what we got from the outer burn.
                return(new BurnModel(
                           ejection.atTime,
                           ejection.prograde + DeltaVToOrbit(body)
                           ));
            }
        }
示例#6
0
        private BurnModel PlotEjectionBurn(Orbit currentOrbit, bool fakeOrbit = false)
        {
            double    now       = Planetarium.GetUniversalTime();
            BurnModel outerBurn = GenerateEjectionBurnFromOrbit(ParentOrbit(currentOrbit));

            if (outerBurn != null)
            {
                DbgFmt("Got route from {0}, calculating ejection", TheName(currentOrbit.referenceBody));

                double angleOffset = outerBurn.prograde < 0
                                        ? 0
                                        : -Math.PI;

                // The angle, position, and time are interdependent.
                // So we seed them with parameters from the outer burn, then
                // cross-seed them with each other this many times.
                // Cross your fingers and hope this converges to the right answer.
                const int iterations = 6;

                if (fakeOrbit && outerBurn.atTime == null)
                {
                    // We have absolutely no basis for a time, so don't fake it.
                    return(new BurnModel(
                               null,
                               BurnToEscape(
                                   currentOrbit.referenceBody,
                                   currentOrbit.ApR,
                                   outerBurn.totalDeltaV
                                   )
                               ));
                }
                else
                {
                    // Either the current orbit is real, or the outer burn constrains us,
                    // so we can use those times.
                    double burnTime = outerBurn.atTime ?? now;

                    try {
                        for (int i = 0; i < iterations; ++i)
                        {
                            double ejectionAngle = EjectionAngle(
                                currentOrbit.referenceBody,
                                RadiusAtTime(currentOrbit, burnTime),
                                outerBurn.totalDeltaV);
                            burnTime = TimeAtAngleFromMidnight(
                                currentOrbit.referenceBody.orbit,
                                currentOrbit,
                                burnTime,
                                ejectionAngle + angleOffset);
                        }
                    } catch (Exception ex) {
                        DbgExc("Problem with ejection calc", ex);
                    }

                    return(new BurnModel(
                               burnTime,
                               BurnToEscape(
                                   currentOrbit.referenceBody,
                                   currentOrbit,
                                   outerBurn.totalDeltaV,
                                   burnTime
                                   )
                               ));
                }
            }
            else
            {
                DbgFmt("No outer burn found");
                return(null);
            }
        }