예제 #1
0
 public static double TimeToReachAP(VesselState vesselstate, double StartSpeed, double TargetAPTime)
 {
     // gravityForce isn't force, it's acceleration
     double targetSpeed = vesselstate.gravityForce.magnitude * TargetAPTime;
     double timeToSpeed = (targetSpeed - StartSpeed) / vesselstate.maxVertAccel;
     return timeToSpeed;
 }
        private IEnumerable <bool> ComputeTrajectoryIncrement()
        {
            // create new VesselState from vessel
            VesselState state = new VesselState(AttachedVessel);

            // iterate over patches until MaxPatchCount is reached
            for (int patchIdx = 0; patchIdx < Settings.MaxPatchCount; ++patchIdx)
            {
                // stop if we don't have a vessel state
                if (state == null)
                {
                    break;
                }

                // If we spent more time in this calculation than allowed, pause until the next frame
                if (Util.ElapsedMilliseconds(increment_time) > MAX_INCREMENT_TIME)
                {
                    yield return(false);
                }

                // if we have a patched conics solver, check for maneuver nodes
                if (null != AttachedVessel.patchedConicSolver)
                {
                    // search through maneuver nodes of the vessel
                    List <ManeuverNode> maneuverNodes = AttachedVessel.patchedConicSolver.maneuverNodes;
                    foreach (ManeuverNode node in maneuverNodes)
                    {
                        // if the maneuver node time corresponds to the end time of the last patch
                        if (node.UT == state.Time)
                        {
                            // add the velocity change of the burn to the velocity of the last patch
                            state.Velocity += node.GetBurnVector(CreateOrbitFromState(state));
                            break;
                        }
                    }
                }

                // Add one patch, then pause execution after every patch
                foreach (bool unused in AddPatch(state))
                {
                    yield return(false);
                }

                state = AddPatch_outState;
            }
        }
예제 #3
0
 void Start()
 {
     mucore.init();
     vesselState = new VesselState();
     attitude = new AttitudeController(this);
     stage = new StageController(this);
     attitude.OnStart();
     RenderingManager.AddToPostDrawQueue(3, new Callback(drawGUI));//start the GUI
     helpWindowPos = new Rect(windowPos.x+windowPos.width, windowPos.y, 0, 0);
     stagestats = new StageStats();
     stagestats.editorBody = vessel.mainBody;
     stagestats.OnModuleEnabled();
     stagestats.OnFixedUpdate();
     stagestats.RequestUpdate(this);
     stagestats.OnFixedUpdate();
     CreateButtonIcon();
 }
예제 #4
0
        void Start()
        {
            instance = this;
            Log("Starting");
            try
            {
                mucore.init();
                vesselState = new VesselState();
                attitude    = new AttitudeController(this);
                stage       = new StageController(this);
                attitude.OnStart();
                stagestats            = new StageStats(stage);
                stagestats.editorBody = getVessel.mainBody;
                stagestats.OnModuleEnabled();
                stagestats.OnFixedUpdate();
                stagestats.RequestUpdate(this);
                stagestats.OnFixedUpdate();
                CreateButtonIcon();
                LaunchName = new string(getVessel.vesselName.ToCharArray());
                LaunchBody = getVessel.mainBody;
                launchdb   = new LaunchDB(this);
                launchdb.Load();

                mainWindow      = new Window.MainWindow(this, 6378070);
                flightMapWindow = new Window.FlightMapWindow(this, 548302);
                statsWindow     = new Window.StatsWindow(this, 6378070 + 4);
                double h = 80f;
                if (FlightGlobals.ActiveVessel.mainBody.atmosphere)
                {
                    h = Math.Max(h, FlightGlobals.ActiveVessel.mainBody.atmosphereDepth + 10000f);
                    DestinationHeight = new EditableValue(h, locked: true) / 1000;
                }

                delayUT = double.NaN;

                GameEvents.onShowUI.Add(ShowGUI);
                GameEvents.onHideUI.Add(HideGUI);

                LoadKeybind();
            }
            catch (Exception ex)
            {
                Log(ex.ToString());
            }
        }
예제 #5
0
 public VesselState(VesselState state)
 {
     this.Origin         = state.Origin;
     this.MainBody       = state.MainBody;
     this.IsLanded       = state.IsLanded;
     this.IsLaunch       = state.IsLaunch;
     this.IsEVA          = state.IsEVA;
     this.HasFlagPlanted = state.HasFlagPlanted;
     this.Situation      = state.Situation;
     this.InOrbit        = state.InOrbit;
     this.ApA            = state.ApA;
     this.ApR            = state.ApR;
     this.PeA            = state.PeA;
     this.PeR            = state.PeR;
     this.atmDensity     = state.atmDensity;
     this.MissionTime    = state.MissionTime;
     this.MissionTime    = state.LaunchTime;
     this.movedOnSurface = state.movedOnSurface;
     this.altitude       = state.altitude;
     this.IsInAtmosphere = state.IsInAtmosphere;
 }
예제 #6
0
 public VesselState(VesselState state)
 {
    this.Origin = state.Origin;
    this.MainBody = state.MainBody;
    this.IsLanded = state.IsLanded;
    this.IsLaunch = state.IsLaunch;
    this.IsEVA = state.IsEVA;
    this.HasFlagPlanted = state.HasFlagPlanted;
    this.Situation = state.Situation;
    this.InOrbit = state.InOrbit;
    this.ApA = state.ApA;
    this.ApR = state.ApR;
    this.PeA = state.PeA;
    this.PeR = state.PeR;
    this.atmDensity = state.atmDensity;
    this.MissionTime = state.MissionTime;
    this.MissionTime = state.LaunchTime;
    this.movedOnSurface = state.movedOnSurface;
    this.altitude = state.altitude;
    this.IsInAtmosphere = state.IsInAtmosphere;
 }
예제 #7
0
            private void OnVesselSituationChange(GameEvents.HostedFromToAction <Vessel, Vessel.Situations> e)
            {
                Vessel vessel = e.host;

                //
                if (vessel == null)
                {
                    Log.Warning("vessel situation change without a valid vessel detected");
                    return;
                }
                Log.Info("vessel situation change for " + vessel.name + ", situation changed from " + e.from + " to " + e.to);
                //
                // check for a (first) launch
                CheckForLaunch(vessel, e.from, e.to);

                if (vessel.isActiveVessel)
                {
                    if (vessel.situation != Vessel.Situations.LANDED)
                    {
                        ResetLandedVesselHasMovedFlag();
                    }
                    //
                    Log.Info("situation change for active vessel");
                    CheckAchievementsForVessel(vessel);
                }
                else
                {
                    if (vessel != null && vessel.IsFlag() && vessel.situation == Vessel.Situations.LANDED)
                    {
                        Log.Info("situation change for flag");
                        Vessel active = FlightGlobals.ActiveVessel;
                        if (active != null && active.isEVA)
                        {
                            VesselState vesselState = VesselState.CreateFlagPlantedFromVessel(active);
                            CheckAchievementsForVessel(vesselState);
                            return;
                        }
                    }
                }
            }
예제 #8
0
            private void OnCrewOnEva(GameEvents.FromToAction <Part, Part> action)
            {
                Log.Detail("EventObserver:: crew on EVA");
                if (action.from == null || action.from.vessel == null)
                {
                    return;
                }
                if (action.to == null || action.to.vessel == null)
                {
                    return;
                }
                Vessel vessel = action.from.vessel;
                Vessel crew   = action.to.vessel;

                // record EVA
                foreach (ProtoCrewMember member in crew.GetVesselCrew())
                {
                    recorder.RecordEva(member, vessel);
                }
                // the previous vessel shoud be previous
                this.previousVesselState = new VesselState(vessel);
                // current vessel is crew
                CheckAchievementsForVessel(crew);
            }
예제 #9
0
            private void OnPartCouple(GameEvents.FromToAction <Part, Part> action)
            {
                // check for docking manouvers
                //
                // we are just checking flights
                if (!HighLogic.LoadedSceneIsFlight)
                {
                    return;
                }
                Part from = action.from;
                Part to   = action.to;

                Log.Detail("part couple event");
                // eva wont count as docking
                if (from == null || from.vessel == null || from.vessel.isEVA)
                {
                    return;
                }
                Log.Detail("from vessel " + from.vessel);
                if (to == null || to.vessel == null || to.vessel.isEVA)
                {
                    return;
                }
                Log.Detail("to vessel " + to.vessel);
                Vessel vessel = action.from.vessel.isActiveVessel?action.from.vessel:action.to.vessel;

                if (vessel != null && vessel.isActiveVessel)
                {
                    Log.Info("docking vessel " + vessel.name);
                    VesselState vesselState = new VesselState(vessel);
                    // check achievements, but mark vessel as docked
                    CheckAchievementsForVessel(vesselState.Docked());
                    // record docking maneuver
                    recorder.RecordDocking(vessel);
                }
            }
예제 #10
0
        /// <summary>
        /// Update vessel
        /// </summary>
        /// <param name="currentTime"></param>
        internal virtual void Update(double currentTime)
        {
            if (vessel == null)
            {
                return;
            }
            if (vessel.isActiveVessel)
            {
                if (active)
                {
                    ScreenMessages.PostScreenMessage(Localizer.Format("#LOC_BV_AutopilotActive"), 10f).color = Color.red;
                }
                return;
            }

            if (!active || vessel.loaded)
            {
                return;
            }

            State = VesselState.Idle;

            Save(currentTime);
        }
예제 #11
0
 private Orbit createOrbitFromState(VesselState state)
 {
     var orbit = new Orbit();
     orbit.UpdateFromStateVectors(Util.SwapYZ(state.position), Util.SwapYZ(state.velocity), state.referenceBody, state.time);
     return orbit;
 }
예제 #12
0
 public ParachuteInfo(VesselState vesselState)
 {
     this.vesselState = vesselState;
     update(vesselState.speedSurface, vesselState.atmosphericDensity);
 }
예제 #13
0
        private IEnumerable<bool> AddPatch(VesselState startingState, DescentProfile profile)
        {
            if (null == vessel_.patchedConicSolver)
            {
                UnityEngine.Debug.LogWarning("Trajectories: AddPatch() attempted when patchedConicsSolver is null; Skipping.");
                yield break;
            }

            CelestialBody body = startingState.referenceBody;

            var patch = new Patch();
            patch.startingState = startingState;
            patch.isAtmospheric = false;
            patch.spaceOrbit = startingState.stockPatch ?? createOrbitFromState(startingState);
            patch.endTime = patch.startingState.time + patch.spaceOrbit.period;

            // the flight plan does not always contain the first patches (before the first maneuver node), so we populate it with the current orbit and associated encounters etc.
            var flightPlan = new List<Orbit>();
            for (var orbit = vessel_.orbit; orbit != null && orbit.activePatch; orbit = orbit.nextPatch)
            {
                if (vessel_.patchedConicSolver.flightPlan.Contains(orbit))
                    break;
                flightPlan.Add(orbit);
            }

            foreach (var orbit in vessel_.patchedConicSolver.flightPlan)
            {
                flightPlan.Add(orbit);
            }

            Orbit nextStockPatch = null;
            if (startingState.stockPatch != null)
            {
                int planIdx = flightPlan.IndexOf(startingState.stockPatch);
                if (planIdx >= 0 && planIdx < flightPlan.Count - 1)
                {
                    nextStockPatch = flightPlan[planIdx + 1];
                }
            }

            if (nextStockPatch != null)
            {
                patch.endTime = nextStockPatch.StartUT;
            }

            double maxAtmosphereAltitude = RealMaxAtmosphereAltitude(body);

            double minAltitude = patch.spaceOrbit.PeA;
            if (patch.endTime < startingState.time + patch.spaceOrbit.timeToPe)
            {
                minAltitude = patch.spaceOrbit.getRelativePositionAtUT(patch.endTime).magnitude;
            }
            if (minAltitude < maxAtmosphereAltitude)
            {
                double entryTime;
                if (startingState.position.magnitude <= body.Radius + maxAtmosphereAltitude)
                {
                    // whole orbit is inside the atmosphere
                    entryTime = startingState.time;
                }
                else
                {
                    // binary search of entry time in atmosphere
                    // I guess an analytic solution could be found, but I'm too lazy to search it
                    double from = startingState.time;
                    double to = from + patch.spaceOrbit.timeToPe;

                    int loopCount = 0;
                    while (to - from > 0.1)
                    {
                        ++loopCount;
                        if (loopCount > 1000)
                        {
                            UnityEngine.Debug.Log("WARNING: infinite loop? (Trajectories.Trajectory.AddPatch, atmosphere limit search)");
                            ++errorCount_;
                            break;
                        }
                        double middle = (from + to) * 0.5;
                        if (patch.spaceOrbit.getRelativePositionAtUT(middle).magnitude < body.Radius + maxAtmosphereAltitude)
                        {
                            to = middle;
                        }
                        else
                        {
                            from = middle;
                        }
                    }

                    entryTime = to;
                }

                if (entryTime > startingState.time + 0.1)
                {
                    // add the space patch before atmospheric entry
                    patch.endTime = entryTime;

                    if (body.atmosphere)
                    {
                        patchesBackBuffer_.Add(patch);
                        AddPatch_outState = new VesselState
                        {
                            position = Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(entryTime)),
                            referenceBody = body,
                            time = entryTime,
                            velocity = Util.SwapYZ(patch.spaceOrbit.getOrbitalVelocityAtUT(entryTime))
                        };
                        yield break;
                    }
                    else
                    {
                        // the body has no atmosphere, so what we actually computed is the impact on the body surface
                        // now, go back in time until the impact point is above the ground to take ground height in account
                        // we assume the ground is horizontal around the impact position
                        double groundAltitude = GetGroundAltitude(body, calculateRotatedPosition(body, Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(entryTime)), entryTime)) + body.Radius;

                        double iterationSize = 1.0;
                        while (entryTime > startingState.time + iterationSize && patch.spaceOrbit.getRelativePositionAtUT(entryTime).magnitude < groundAltitude)
                            entryTime -= iterationSize;

                        patch.endTime = entryTime;
                        patch.rawImpactPosition = Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(entryTime));
                        patch.impactPosition = calculateRotatedPosition(body, patch.rawImpactPosition.Value, entryTime);
                        patch.impactVelocity = Util.SwapYZ(patch.spaceOrbit.getOrbitalVelocityAtUT(entryTime));
                        patchesBackBuffer_.Add(patch);
                        AddPatch_outState = null;
                        yield break;
                    }
                }
                else
                {
                    if (patch.startingState.referenceBody != vessel_.mainBody)
                    {
                        // in current aerodynamic prediction code, we can't handle predictions for another body, so we stop here
                        AddPatch_outState = null;
                        yield break;
                    }

                    // simulate atmospheric flight (drag and lift), until landing (more likely to be a crash as we don't predict user piloting) or atmosphere exit (typically for an aerobraking maneuver)
                    // the simulation assumes a constant angle of attack

                    patch.isAtmospheric = true;
                    patch.startingState.stockPatch = null;

                    double dt = 0.1; // lower dt would be more accurate, but a tradeoff has to be found between performances and accuracy

                    int maxIterations = (int)(30.0 * 60.0 / dt); // some shallow entries can result in very long flight, for performances reasons, we limit the prediction duration

                    int chunkSize = 128;
                    double trajectoryInterval = 10.0; // time between two consecutive stored positions (more intermediate positions are computed for better accuracy), also used for ground collision checks
                    var buffer = new List<Point[]>();
                    buffer.Add(new Point[chunkSize]);
                    int nextPosIdx = 0;

                    Vector3d pos = Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(entryTime));
                    Vector3d vel = Util.SwapYZ(patch.spaceOrbit.getOrbitalVelocityAtUT(entryTime));
                    Vector3d prevPos = pos - vel * dt;
                    //Util.PostSingleScreenMessage("initial vel", "initial vel = " + vel);
                    double currentTime = entryTime;
                    double lastPositionStoredUT = 0;
                    Vector3d lastPositionStored = new Vector3d();
                    bool hitGround = false;
                    int iteration = 0;
                    int incrementIterations = 0;
                    int minIterationsPerIncrement = maxIterations / Settings.fetch.MaxFramesPerPatch;
                    while (true)
                    {
                        ++iteration;
                        ++incrementIterations;

                        if (incrementIterations > minIterationsPerIncrement && incrementTime_.ElapsedMilliseconds > MaxIncrementTime)
                        {
                            yield return false;
                            incrementIterations = 0;
                        }

                        double R = pos.magnitude;
                        double altitude = R - body.Radius;
                        double atmosphereCoeff = altitude / maxAtmosphereAltitude;
                        if (hitGround || atmosphereCoeff <= 0.0 || atmosphereCoeff >= 1.0 || iteration == maxIterations || currentTime > patch.endTime)
                        {
                            if (hitGround || atmosphereCoeff <= 0.0)
                            {
                                patch.rawImpactPosition = pos;
                                patch.impactPosition = calculateRotatedPosition(body, patch.rawImpactPosition.Value, currentTime);
                                patch.impactVelocity = vel;
                            }

                            patch.endTime = Math.Min(currentTime, patch.endTime);

                            int totalCount = (buffer.Count - 1) * chunkSize + nextPosIdx;
                            patch.atmosphericTrajectory = new Point[totalCount];
                            int outIdx = 0;
                            foreach (var chunk in buffer)
                            {
                                foreach (var p in chunk)
                                {
                                    if (outIdx == totalCount)
                                        break;
                                    patch.atmosphericTrajectory[outIdx++] = p;
                                }
                            }

                            if (iteration == maxIterations)
                            {
                                ScreenMessages.PostScreenMessage("WARNING: trajectory prediction stopped, too many iterations");
                                patchesBackBuffer_.Add(patch);
                                AddPatch_outState = null;
                                yield break;
                            }
                            else if (atmosphereCoeff <= 0.0 || hitGround)
                            {
                                patchesBackBuffer_.Add(patch);
                                AddPatch_outState = null;
                                yield break;
                            }
                            else
                            {
                                patchesBackBuffer_.Add(patch);
                                AddPatch_outState = new VesselState
                                {
                                    position = pos,
                                    velocity = vel,
                                    referenceBody = body,
                                    time = patch.endTime
                                };
                                yield break;
                            }
                        }

                        Vector3d gravityAccel = pos * (-body.gravParameter / (R * R * R));

                        //Util.PostSingleScreenMessage("prediction vel", "prediction vel = " + vel);
                        Vector3d airVelocity = vel - body.getRFrmVel(body.position + pos);
                        double angleOfAttack = profile.GetAngleOfAttack(body, pos, airVelocity);
                        Vector3d aerodynamicForce = aerodynamicModel_.GetForces(body, pos, airVelocity, angleOfAttack);
                        Vector3d acceleration = gravityAccel + aerodynamicForce / aerodynamicModel_.mass;

                        // acceleration in the vessel reference frame is acceleration - gravityAccel
                        maxAccelBackBuffer_ = Math.Max((float) (aerodynamicForce.magnitude / aerodynamicModel_.mass), maxAccelBackBuffer_);

                        //vel += acceleration * dt;
                        //pos += vel * dt;

                        // Verlet integration (more precise than using the velocity)
                        Vector3d ppos = prevPos;
                        prevPos = pos;
                        pos = pos + pos - ppos + acceleration * (dt * dt);
                        vel = (pos - prevPos) / dt;

                        currentTime += dt;

                        double interval = altitude < 10000.0 ? trajectoryInterval * 0.1 : trajectoryInterval;
                        if (currentTime >= lastPositionStoredUT + interval)
                        {
                            double groundAltitude = GetGroundAltitude(body, calculateRotatedPosition(body, pos, currentTime));
                            if (lastPositionStoredUT > 0)
                            {
                                // check terrain collision, to detect impact on mountains etc.
                                Vector3 rayOrigin = lastPositionStored;
                                Vector3 rayEnd = pos;
                                double absGroundAltitude = groundAltitude + body.Radius;
                                if (absGroundAltitude > rayEnd.magnitude)
                                {
                                    hitGround = true;
                                    float coeff = Math.Max(0.01f, (float)((absGroundAltitude - rayOrigin.magnitude) / (rayEnd.magnitude - rayOrigin.magnitude)));
                                    pos = rayEnd * coeff + rayOrigin * (1.0f - coeff);
                                    currentTime = currentTime * coeff + lastPositionStoredUT * (1.0f - coeff);
                                }
                            }

                            lastPositionStoredUT = currentTime;
                            if (nextPosIdx == chunkSize)
                            {
                                buffer.Add(new Point[chunkSize]);
                                nextPosIdx = 0;
                            }
                            Vector3d nextPos = pos;
                            if (Settings.fetch.BodyFixedMode)
                            {
                                nextPos = calculateRotatedPosition(body, nextPos, currentTime);
                            }
                            buffer.Last()[nextPosIdx].aerodynamicForce = aerodynamicForce;
                            buffer.Last()[nextPosIdx].orbitalVelocity = vel;
                            buffer.Last()[nextPosIdx].groundAltitude = (float)groundAltitude;
                            buffer.Last()[nextPosIdx].time = currentTime;
                            buffer.Last()[nextPosIdx++].pos = nextPos;
                            lastPositionStored = pos;
                        }
                    }
                }
            }
            else
            {
                // no atmospheric entry, just add the space orbit
                patchesBackBuffer_.Add(patch);
                if (nextStockPatch != null)
                {
                    AddPatch_outState = new VesselState
                    {
                        position = Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(patch.endTime)),
                        velocity = Util.SwapYZ(patch.spaceOrbit.getOrbitalVelocityAtUT(patch.endTime)),
                        referenceBody = nextStockPatch == null ? body : nextStockPatch.referenceBody,
                        time = patch.endTime,
                        stockPatch = nextStockPatch
                    };
                    yield break;
                }
                else
                {
                    AddPatch_outState = null;
                    yield break;
                }
            }
        }
예제 #14
0
 private void CheckAchievementsForVessel(VesselState previous, VesselState current, EventReport report, bool hasToBeFirst)
 {
    Log.Detail("CheckAchievementsForVessel, first=" + hasToBeFirst + ", with report=" + (report!=null));
    if(current!=null)
    {
       foreach (Ribbon ribbon in RibbonPool.Instance())
       {
          try
          {
             CheckRibbonForVessel(ribbon, previous, current, report, hasToBeFirst);
          }
          catch(Exception e)
          {
             Log.Error("exception caught in vessel check: "+e.Message + "("+e.GetType()+")");
          }
       }
       if (Log.IsLogable(Log.LEVEL.TRACE)) Log.Trace("all ribbons checked");
    }
    else
    {
       Log.Warning("no current vessel state; no achievements checked");
    }
 }
예제 #15
0
 private void OnPartCouple(GameEvents.FromToAction<Part, Part> action)
 {
    // check for docking manouvers
    //
    // we are just checking flights
    if (!HighLogic.LoadedSceneIsFlight) return;
    Part from = action.from;
    Part to = action.to;
    Log.Detail("part couple event");
    // eva wont count as docking
    if (from == null || from.vessel == null || from.vessel.isEVA) return;
    Log.Detail("from vessel " + from.vessel);
    if (to == null || to.vessel == null || to.vessel.isEVA) return;
    Log.Detail("to vessel " + to.vessel);
    Vessel vessel = action.from.vessel.isActiveVessel?action.from.vessel:action.to.vessel;
    if (vessel != null && vessel.isActiveVessel)
    {
       Log.Info("docking vessel "+vessel.name);
       VesselState vesselState = new VesselState(vessel);
       // check achievements, but mark vessel as docked
       CheckAchievementsForVessel(vesselState.Docked());
       // record docking maneuver
       recorder.RecordDocking(vessel);
    }
 }
예제 #16
0
    private void CheckVessel(Vessel vessel, VesselState state)
    {
        bool changed = false;

        var rcsBalancer = VesselExtensions.GetMasterMechJeb(vessel).rcsbal;

        if (vessel.parts.Count != lastPartCount)
        {
            lastPartCount = vessel.parts.Count;
            changed       = true;
        }

        // Make sure all thrusters are still enabled, because if they're not,
        // our calculations will be wrong.
        for (int i = 0; i < thrusters.Count; i++)
        {
            if (!thrusters[i].partModule.isEnabled)
            {
                changed = true;
                break;
            }
        }

        // Likewise, make sure any previously-disabled RCS modules are still
        // disabled.
        foreach (var pm in lastDisabled)
        {
            if (pm.isEnabled)
            {
                changed = true;
                break;
            }
        }

        // See if the CoM has moved too much.
        Rigidbody rootPartBody = vessel.rootPart.rigidbody;

        if (rootPartBody != null)
        {
            // But how much is "too much"? Well, it probably has something to do
            // with the ship's moment of inertia (MoI). Let's say the distance
            // 'd' that the CoM is allowed to shift without a reset is:
            //
            //      d = moi * x + c
            //
            // where 'moi' is the magnitude of the ship's moment of inertia and
            // 'x' and 'c' are tuning parameters to be determined.
            //
            // Using a few actual KSP ships, I burned RCS fuel (or moved fuel
            // from one tank to another) to see how far the CoM could shift
            // before the the rotation error on translation became annoying.
            // I came up with roughly:
            //
            //      d         moi
            //      0.005    2.34
            //      0.04    11.90
            //      0.07    19.96
            //
            // I then halved each 'd' value, because we'd like to address this
            // problem -before- it becomes annoying. Least-squares linear
            // regression on the (moi, d/2) pairs gives the following (with
            // adjusted R^2 = 0.999966):
            //
            //      moi = 542.268 d + 1.00654
            //      d = (moi - 1) / 542
            //
            // So the numbers below have some basis in reality. =)

            // Assume MoI magnitude is always >=2.34, since that's all I tested.
            comErrorThreshold = (Math.Max(state.MoI.magnitude, 2.34) - 1) / 542;

            Vector3 comState   = state.CoM;
            Vector3 rootPos    = state.rootPartPos;
            Vector3 com        = WorldToVessel(vessel, comState - rootPos);
            double  thisComErr = (lastCoM - com).magnitude;
            maxComError     = Math.Max(maxComError, thisComErr);
            _comError.value = thisComErr;
            if (_comError > comErrorThreshold)
            {
                lastCoM = com;
                changed = true;
            }
        }

        if (!changed)
        {
            return;
        }

        // Something about the vessel has changed. We need to reset everything.

        lastDisabled.Clear();

        // ModuleRCS has no originalThrusterPower attribute, so we have
        // to explicitly reset it.
        ResetThrusterForces();

        // Rebuild the list of thrusters.
        var ts = new List <RCSSolver.Thruster>();

        foreach (Part p in vessel.parts)
        {
            foreach (ModuleRCS pm in p.Modules.OfType <ModuleRCS>())
            {
                if (!pm.isEnabled)
                {
                    // Keep track of this module so we'll know if it's enabled.
                    lastDisabled.Add(pm);
                }
                else if (p.Rigidbody != null && !pm.isJustForShow)
                {
                    Vector3 pos = VesselRelativePos(state.CoM, vessel, p);

                    // Create a single RCSSolver.Thruster for this part. This
                    // requires some assumptions about how the game's RCS code will
                    // drive the individual thrusters (which we can't control).

                    Vector3[]  thrustDirs   = new Vector3[pm.thrusterTransforms.Count];
                    Quaternion rotationQuat = Quaternion.Inverse(vessel.GetTransform().rotation);
                    for (int i = 0; i < pm.thrusterTransforms.Count; i++)
                    {
                        thrustDirs[i] = (rotationQuat * -pm.thrusterTransforms[i].up).normalized;
                    }

                    ts.Add(new RCSSolver.Thruster(pos, thrustDirs, p, pm));
                }
            }
        }
        callerThrusters.Clear();
        originalThrottles = new double[ts.Count];
        zeroThrottles     = new double[ts.Count];
        for (int i = 0; i < ts.Count; i++)
        {
            originalThrottles[i] = ts[i].originalForce;
            zeroThrottles[i]     = 0;
            callerThrusters.Add(ts[i]);
        }

        thrusters = ts;
        ClearResults();
    }
예제 #17
0
        private IEnumerable <bool> AddPatch(VesselState startingState, DescentProfile profile)
        {
            if (null == vessel_.patchedConicSolver)
            {
                UnityEngine.Debug.LogWarning("Trajectories: AddPatch() attempted when patchedConicsSolver is null; Skipping.");
                yield break;
            }

            CelestialBody body = startingState.referenceBody;

            var patch = new Patch();

            patch.startingState = startingState;
            patch.isAtmospheric = false;
            patch.spaceOrbit    = startingState.stockPatch ?? createOrbitFromState(startingState);
            patch.endTime       = patch.startingState.time + patch.spaceOrbit.period;

            // the flight plan does not always contain the first patches (before the first maneuver node), so we populate it with the current orbit and associated encounters etc.
            var flightPlan = new List <Orbit>();

            for (var orbit = vessel_.orbit; orbit != null && orbit.activePatch; orbit = orbit.nextPatch)
            {
                if (vessel_.patchedConicSolver.flightPlan.Contains(orbit))
                {
                    break;
                }
                flightPlan.Add(orbit);
            }

            foreach (var orbit in vessel_.patchedConicSolver.flightPlan)
            {
                flightPlan.Add(orbit);
            }


            Orbit nextStockPatch = null;

            if (startingState.stockPatch != null)
            {
                int planIdx = flightPlan.IndexOf(startingState.stockPatch);
                if (planIdx >= 0 && planIdx < flightPlan.Count - 1)
                {
                    nextStockPatch = flightPlan[planIdx + 1];
                }
            }

            if (nextStockPatch != null)
            {
                patch.endTime = nextStockPatch.StartUT;
            }

            double maxAtmosphereAltitude = RealMaxAtmosphereAltitude(body);

            double minAltitude = patch.spaceOrbit.PeA;

            if (patch.endTime < startingState.time + patch.spaceOrbit.timeToPe)
            {
                minAltitude = patch.spaceOrbit.getRelativePositionAtUT(patch.endTime).magnitude;
            }
            if (minAltitude < maxAtmosphereAltitude)
            {
                double entryTime;
                if (startingState.position.magnitude <= body.Radius + maxAtmosphereAltitude)
                {
                    // whole orbit is inside the atmosphere
                    entryTime = startingState.time;
                }
                else
                {
                    // binary search of entry time in atmosphere
                    // I guess an analytic solution could be found, but I'm too lazy to search it
                    double from = startingState.time;
                    double to   = from + patch.spaceOrbit.timeToPe;

                    int loopCount = 0;
                    while (to - from > 0.1)
                    {
                        ++loopCount;
                        if (loopCount > 1000)
                        {
                            UnityEngine.Debug.Log("WARNING: infinite loop? (Trajectories.Trajectory.AddPatch, atmosphere limit search)");
                            ++errorCount_;
                            break;
                        }
                        double middle = (from + to) * 0.5;
                        if (patch.spaceOrbit.getRelativePositionAtUT(middle).magnitude < body.Radius + maxAtmosphereAltitude)
                        {
                            to = middle;
                        }
                        else
                        {
                            from = middle;
                        }
                    }

                    entryTime = to;
                }

                if (entryTime > startingState.time + 0.1)
                {
                    // add the space patch before atmospheric entry
                    patch.endTime = entryTime;

                    if (body.atmosphere)
                    {
                        patchesBackBuffer_.Add(patch);
                        AddPatch_outState = new VesselState
                        {
                            position      = Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(entryTime)),
                            referenceBody = body,
                            time          = entryTime,
                            velocity      = Util.SwapYZ(patch.spaceOrbit.getOrbitalVelocityAtUT(entryTime))
                        };
                        yield break;
                    }
                    else
                    {
                        // the body has no atmosphere, so what we actually computed is the impact on the body surface
                        // now, go back in time until the impact point is above the ground to take ground height in account
                        // we assume the ground is horizontal around the impact position
                        double groundAltitude = GetGroundAltitude(body, calculateRotatedPosition(body, Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(entryTime)), entryTime)) + body.Radius;

                        double iterationSize = 1.0;
                        while (entryTime > startingState.time + iterationSize && patch.spaceOrbit.getRelativePositionAtUT(entryTime).magnitude < groundAltitude)
                        {
                            entryTime -= iterationSize;
                        }

                        patch.endTime           = entryTime;
                        patch.rawImpactPosition = Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(entryTime));
                        patch.impactPosition    = calculateRotatedPosition(body, patch.rawImpactPosition.Value, entryTime);
                        patch.impactVelocity    = Util.SwapYZ(patch.spaceOrbit.getOrbitalVelocityAtUT(entryTime));
                        patchesBackBuffer_.Add(patch);
                        AddPatch_outState = null;
                        yield break;
                    }
                }
                else
                {
                    if (patch.startingState.referenceBody != vessel_.mainBody)
                    {
                        // in current aerodynamic prediction code, we can't handle predictions for another body, so we stop here
                        AddPatch_outState = null;
                        yield break;
                    }

                    // simulate atmospheric flight (drag and lift), until landing (more likely to be a crash as we don't predict user piloting) or atmosphere exit (typically for an aerobraking maneuver)
                    // the simulation assumes a constant angle of attack

                    patch.isAtmospheric            = true;
                    patch.startingState.stockPatch = null;

                    double dt = 0.1;                             // lower dt would be more accurate, but a tradeoff has to be found between performances and accuracy

                    int maxIterations = (int)(30.0 * 60.0 / dt); // some shallow entries can result in very long flight, for performances reasons, we limit the prediction duration

                    int    chunkSize          = 128;
                    double trajectoryInterval = 10.0; // time between two consecutive stored positions (more intermediate positions are computed for better accuracy), also used for ground collision checks
                    var    buffer             = new List <Point[]>();
                    buffer.Add(new Point[chunkSize]);
                    int nextPosIdx = 0;

                    Vector3d pos     = Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(entryTime));
                    Vector3d vel     = Util.SwapYZ(patch.spaceOrbit.getOrbitalVelocityAtUT(entryTime));
                    Vector3d prevPos = pos - vel * dt;
                    //Util.PostSingleScreenMessage("initial vel", "initial vel = " + vel);
                    double   currentTime               = entryTime;
                    double   lastPositionStoredUT      = 0;
                    Vector3d lastPositionStored        = new Vector3d();
                    bool     hitGround                 = false;
                    int      iteration                 = 0;
                    int      incrementIterations       = 0;
                    int      minIterationsPerIncrement = maxIterations / Settings.fetch.MaxFramesPerPatch;
                    while (true)
                    {
                        ++iteration;
                        ++incrementIterations;

                        if (incrementIterations > minIterationsPerIncrement && incrementTime_.ElapsedMilliseconds > MaxIncrementTime)
                        {
                            yield return(false);

                            incrementIterations = 0;
                        }

                        double R               = pos.magnitude;
                        double altitude        = R - body.Radius;
                        double atmosphereCoeff = altitude / maxAtmosphereAltitude;
                        if (hitGround || atmosphereCoeff <= 0.0 || atmosphereCoeff >= 1.0 || iteration == maxIterations || currentTime > patch.endTime)
                        {
                            if (hitGround || atmosphereCoeff <= 0.0)
                            {
                                patch.rawImpactPosition = pos;
                                patch.impactPosition    = calculateRotatedPosition(body, patch.rawImpactPosition.Value, currentTime);
                                patch.impactVelocity    = vel;
                            }

                            patch.endTime = Math.Min(currentTime, patch.endTime);

                            int totalCount = (buffer.Count - 1) * chunkSize + nextPosIdx;
                            patch.atmosphericTrajectory = new Point[totalCount];
                            int outIdx = 0;
                            foreach (var chunk in buffer)
                            {
                                foreach (var p in chunk)
                                {
                                    if (outIdx == totalCount)
                                    {
                                        break;
                                    }
                                    patch.atmosphericTrajectory[outIdx++] = p;
                                }
                            }

                            if (iteration == maxIterations)
                            {
                                ScreenMessages.PostScreenMessage("WARNING: trajectory prediction stopped, too many iterations");
                                patchesBackBuffer_.Add(patch);
                                AddPatch_outState = null;
                                yield break;
                            }
                            else if (atmosphereCoeff <= 0.0 || hitGround)
                            {
                                patchesBackBuffer_.Add(patch);
                                AddPatch_outState = null;
                                yield break;
                            }
                            else
                            {
                                patchesBackBuffer_.Add(patch);
                                AddPatch_outState = new VesselState
                                {
                                    position      = pos,
                                    velocity      = vel,
                                    referenceBody = body,
                                    time          = patch.endTime
                                };
                                yield break;
                            }
                        }

                        Vector3d gravityAccel = pos * (-body.gravParameter / (R * R * R));

                        //Util.PostSingleScreenMessage("prediction vel", "prediction vel = " + vel);
                        Vector3d airVelocity      = vel - body.getRFrmVel(body.position + pos);
                        double   angleOfAttack    = profile.GetAngleOfAttack(body, pos, airVelocity);
                        Vector3d aerodynamicForce = aerodynamicModel_.GetForces(body, pos, airVelocity, angleOfAttack);
                        Vector3d acceleration     = gravityAccel + aerodynamicForce / aerodynamicModel_.mass;

                        // acceleration in the vessel reference frame is acceleration - gravityAccel
                        maxAccelBackBuffer_ = Math.Max((float)(aerodynamicForce.magnitude / aerodynamicModel_.mass), maxAccelBackBuffer_);


                        //vel += acceleration * dt;
                        //pos += vel * dt;

                        // Verlet integration (more precise than using the velocity)
                        Vector3d ppos = prevPos;
                        prevPos = pos;
                        pos     = pos + pos - ppos + acceleration * (dt * dt);
                        vel     = (pos - prevPos) / dt;

                        currentTime += dt;

                        double interval = altitude < 10000.0 ? trajectoryInterval * 0.1 : trajectoryInterval;
                        if (currentTime >= lastPositionStoredUT + interval)
                        {
                            double groundAltitude = GetGroundAltitude(body, calculateRotatedPosition(body, pos, currentTime));
                            if (lastPositionStoredUT > 0)
                            {
                                // check terrain collision, to detect impact on mountains etc.
                                Vector3 rayOrigin         = lastPositionStored;
                                Vector3 rayEnd            = pos;
                                double  absGroundAltitude = groundAltitude + body.Radius;
                                if (absGroundAltitude > rayEnd.magnitude)
                                {
                                    hitGround = true;
                                    float coeff = Math.Max(0.01f, (float)((absGroundAltitude - rayOrigin.magnitude) / (rayEnd.magnitude - rayOrigin.magnitude)));
                                    pos         = rayEnd * coeff + rayOrigin * (1.0f - coeff);
                                    currentTime = currentTime * coeff + lastPositionStoredUT * (1.0f - coeff);
                                }
                            }

                            lastPositionStoredUT = currentTime;
                            if (nextPosIdx == chunkSize)
                            {
                                buffer.Add(new Point[chunkSize]);
                                nextPosIdx = 0;
                            }
                            Vector3d nextPos = pos;
                            if (Settings.fetch.BodyFixedMode)
                            {
                                nextPos = calculateRotatedPosition(body, nextPos, currentTime);
                            }
                            buffer.Last()[nextPosIdx].aerodynamicForce = aerodynamicForce;
                            buffer.Last()[nextPosIdx].orbitalVelocity  = vel;
                            buffer.Last()[nextPosIdx].groundAltitude   = (float)groundAltitude;
                            buffer.Last()[nextPosIdx].time             = currentTime;
                            buffer.Last()[nextPosIdx++].pos            = nextPos;
                            lastPositionStored = pos;
                        }
                    }
                }
            }
            else
            {
                // no atmospheric entry, just add the space orbit
                patchesBackBuffer_.Add(patch);
                if (nextStockPatch != null)
                {
                    AddPatch_outState = new VesselState
                    {
                        position      = Util.SwapYZ(patch.spaceOrbit.getRelativePositionAtUT(patch.endTime)),
                        velocity      = Util.SwapYZ(patch.spaceOrbit.getOrbitalVelocityAtUT(patch.endTime)),
                        referenceBody = nextStockPatch == null ? body : nextStockPatch.referenceBody,
                        time          = patch.endTime,
                        stockPatch    = nextStockPatch
                    };
                    yield break;
                }
                else
                {
                    AddPatch_outState = null;
                    yield break;
                }
            }
        }
예제 #18
0
 private void OnGameSceneLoadRequested(GameScenes scene)
 {
    Log.Info("EventObserver:: OnGameSceneLoadRequested: "+scene+" current="+HighLogic.LoadedScene);
    this.previousVesselState = null;
 }
예제 #19
0
 private void OnLandedVesselMove(Vessel vessel)
 {
    Log.Detail("EventObserver:: OnLandedVesselMove " + vessel.GetName());
    if (vessel.isActiveVessel)
    {
       VesselState vesselState = new VesselState(vessel);
       CheckAchievementsForVessel(vesselState.MovedOnSurface());
    }
 }
예제 #20
0
 public static VesselState CreateFlagPlantedFromVessel(Vessel vessel)
 {
    VesselState state = new VesselState(vessel);
    state.HasFlagPlanted = true;
    return state;
 }
예제 #21
0
 public static VesselState CreateLaunchFromVessel(Vessel vessel)
 {
    VesselState state = new VesselState(vessel);
    state.IsLaunch = true;
    return state;
 }
예제 #22
0
 public VesselState MovedOnSurface()
 {
    VesselState state = new VesselState(this);
    state.movedOnSurface = true;
    return state;
 }
예제 #23
0
 private void CheckAchievementsForVessel(VesselState currentVesselState, EventReport report = null)
 {
    Log.Detail("EventObserver:: checkArchivements for vessel state");
    Stopwatch sw = new Stopwatch();
    sw.Start();
    //
    CheckAchievementsForVessel(previousVesselState, currentVesselState, report);
    //
    sw.Stop();
    this.previousVesselState = currentVesselState;
    Log.Detail("EventObserver:: checkArchivements done in "+sw.ElapsedMilliseconds+" ms");
 }
예제 #24
0
 private void CheckAchievementsForVessel(VesselState previous, VesselState current, EventReport report)
 {
    // first check all first achievements
    CheckAchievementsForVessel(previous, current, report, true);
    // now check the rest
    CheckAchievementsForVessel(previous, current, report, false);
 }
예제 #25
0
        public static List <object> Iterate(Vehicle vehicle, Target target, VesselState state, VesselState previous)
        {
            var gamma = target.Angle;
            var iy    = target.Normal;
            var rdval = target.Radius;
            var vdval = target.Velocity;

            var t = state.Time;
            var m = state.Mass.Value;
            var r = state.Radius;
            var v = state.Velocity;

            var cser  = previous.Cse;
            var rbias = previous.Rbias;
            var rd    = previous.Rd;
            var rgrav = previous.Rgrav;
            var tp    = previous.Time;
            var vprev = previous.Velocity;
            var vgo   = previous.Vgo;

            var n  = vehicle.Stages.Count;
            var SM = new List <double>();
            var aL = new List <double>();
            var md = new List <double>();
            var ve = new List <double>();
            var fT = new List <double>();
            var aT = new List <double>();
            var tu = new List <double>();
            var tb = new List <double>();

            for (int i = 0; i < n; i++)
            {
                Stage stg  = vehicle.Stages[i];
                var   pack = stg.GetThrust();
                SM.Add(stg.Mode);
                aL.Add(stg.GLim * Constants.G0);
                fT.Add(pack[0]);
                md.Add(pack[1]);
                ve.Add(pack[2] * Constants.G0);
                aT.Add(fT[i] / stg.MassTotal);
                tu.Add(ve[i] / aT[i]);
                tb.Add(stg.MaxT);
            }


            var dt       = t - tp;
            var dvsensed = Vector3d.Subtract(v, vprev);

            vgo   = Vector3d.Subtract(vgo, dvsensed);
            tb[0] = tb[0] - previous.Tb;

            if (SM[0] == 1)
            {
                aT[0] = fT[0] / m;
            }
            else if (SM[0] == 2)
            {
                aT[0] = aL[0];
            }

            tu[0] = ve[0] / aT[0];
            var L  = 0d;
            var Li = new List <double>();

            for (int i = 0; i < n - 1; i++)
            {
                if (SM[i] == 1)
                {
                    Li.Add(ve[i] * Math.Log(tu[i] / (tu[i] - tb[i])));
                }
                else if (SM[i] == 2)
                {
                    Li.Add(aL[i] * tb[i]);
                }
                else
                {
                    Li.Add(0);
                }

                L = Li[i];

                if (L > vgo.Magnitude())
                {
                    var veh = vehicle;
                    veh.Stages.RemoveAt(veh.Stages.Count - 1);

                    return(UPFG.Iterate(veh, target, state, previous));
                }
            }

            Li.Add(vgo.Magnitude() - L);

            var tgoi = new List <double>();

            for (int i = 0; i < n; i++)
            {
                if (SM[i] == 1)
                {
                    tb[i] = tu[i] * (1 - Math.Pow(Math.E, (-Li[i] / ve[i])));
                }
                else if (SM[i] == 2)
                {
                    tb[i] = Li[i] / aL[i];
                }

                if (i == 0)
                {
                    tgoi.Add(tb[i]);
                }
                else
                {
                    tgoi.Add(tgoi[i - 1] + tb[i]);
                }
            }

            var L1  = Li[0];
            var tgo = tgoi[n - 1];

            L = 0d;
            var J = 0d;
            var S = 0d;
            var Q = 0d;
            var H = 0d;
            var P = 0d;

            var Ji    = new List <double>();
            var Si    = new List <double>();
            var Qi    = new List <double>();
            var Pi    = new List <double>();
            var tgoi1 = 0d;

            for (int i = 0; i < n; i++)
            {
                if (i > 0)
                {
                    tgoi1 = tgoi[i - 1];
                }

                if (SM[i] == 1)
                {
                    Ji.Add(tu[i] * Li[i] - ve[i] * tb[i]);
                    Si.Add(-Ji[i] + tb[i] * Li[i]);
                    Qi.Add(Si[i] * (tu[i] + tgoi1) - 0.5 * ve[i] * Math.Pow(tb[i], 2));
                    Pi.Add(Qi[i] * (tu[i] + tgoi1) - 0.5 * ve[i] * Math.Pow(tb[i], 2) * (tb[i] / 3 + tgoi1));
                }
                else if (SM[i] == 2)
                {
                    Ji.Add(0.5 * Li[i] * tb[i]);
                    Si.Add(Ji[i]);
                    Qi.Add(Si[i] * (tb[i] / 3 + tgoi1));
                    Pi.Add((1 / 6) * Si[i] * (Math.Pow(tgoi[i], 2) + 2 * tgoi[i] * tgoi1 + 3 * Math.Pow(tgoi1, 2)));
                }

                Ji[i] = Ji[i] + Li[i] * tgoi1;
                Si[i] = Si[i] + L * tb[i];
                Qi[i] = Qi[i] + J * tb[i];
                Pi[i] = Pi[i] + H * tb[i];

                L += Li[i];
                J += Ji[i];
                S += Si[i];
                Q += Qi[i];
                P += Pi[i];
                H  = J * tgoi[i] - Q;
            }

            var lambda = Vector3d.Normalize(vgo);

            if (previous.Tgo > 0)
            {
                rgrav = Vector3d.Multiply(rgrav, Math.Pow((tgo / previous.Tgo), 2));
            }

            var rgo = Vector3d.Subtract(
                rd,
                Vector3d.Add(
                    Vector3d.Multiply(
                        Vector3d.Add(r, v),
                        tgo
                        ),
                    rgrav
                    )
                );


            var iz    = Vector3d.Normalize(Vector3d.Cross(rd, iy));
            var rgoxy = Vector3d.Dot(Vector3d.Subtract(rgo, Vector3d.Dot(iz, rgo)), iz);
            var rgoz  = Vector3d.Divide(Vector3d.Subtract(Vector3d.Multiply(lambda, rgoxy), S),
                                        Vector3d.Dot(lambda, iz));

            rgo = Vector3d.Add(Vector3d.Add(rgoz, rgoxy), Vector3d.Add(iz, rbias));

            var lambdade  = Q - S * J / L;
            var lambdadot = Vector3d.Divide(Vector3d.Subtract(rgo, Vector3d.Multiply(lambda, S)), lambdade);
            var iF_       = Vector3d.Subtract(lambda, Vector3d.Multiply(lambdadot, J / L));

            iF_ = Vector3d.Normalize(iF_);
            var phi     = Math.Acos(Vector3d.Dot(iF_, lambda) / (iF_.Magnitude() * lambda.Magnitude()));
            var phidot  = -phi * L / J;
            var vthrust = Vector3d.Multiply(lambda,
                                            L - 0.5 * L * Math.Pow(phi, 2) - J * phi * phidot - 0.5 * Math.Pow(phidot, 2));
            var rthrst = S - 0.5 * S * Math.Pow(phi, 2) - Q * phi * phidot - 0.5 * P * Math.Pow(phidot, 2);

            var rthrust = Vector3d.Subtract(
                Vector3d.Multiply(lambda, rthrst),
                Vector3d.Multiply(
                    Vector3d.Normalize(lambda),
                    (S * phi + Q * phidot)
                    )
                );

            var vbias = Vector3d.Subtract(vgo, vthrust);
            var rbs   = Vector3d.Subtract(rgo, rthrst);

            var _up   = Vector3d.Normalize(r);
            var _east = Vector3d.Normalize(Vector3d.Cross(
                                               new Vector3d(0, 0, 1),
                                               _up
                                               ));
            var pitch   = Math.Acos(Vector3d.Dot(iF_, _up) / (iF_.Magnitude() * _up.Magnitude()));
            var inplane = Vector3d.Subtract(
                iF_,
                Vector3d.Multiply(_up,
                                  Vector3d.Dot(iF_, _up) / Vector3d.Dot(_up, _up)
                                  ));

            var yaw     = Math.Acos(Vector3d.Dot(inplane, _east) / (inplane.Magnitude() * _east.Magnitude()));
            var tangent = Vector3d.Cross(_up, _east);

            if (Vector3d.Dot(inplane, tangent) < 0)
            {
                yaw = -yaw;
            }

            var rc1 = Vector3d.Subtract(Vector3d.Subtract(
                                            r,
                                            Vector3d.Multiply(rthrust, 0.1)
                                            ),
                                        Vector3d.Multiply(
                                            vthrust,
                                            tgo / 30
                                            ));

            var vc1 = Vector3d.Subtract(Vector3d.Add(
                                            Vector3d.Divide(Vector3d.Multiply(rthrust, 1.2), tgo),
                                            v
                                            ),
                                        Vector3d.Multiply(vthrust, 0.1));

            var pck = CSER.CSE(rc1, vc1, tgo);

            cser = (Dictionary <string, object>)pck[2];
            var rgrv = Vector3d.Subtract(
                (Vector3d)pck[0],
                Vector3d.Subtract(
                    rc1,
                    Vector3d.Multiply(vc1, tgo)
                    )
                );

            var vgrv = Vector3d.Subtract(
                (Vector3d)pck[1],
                vc1
                );

            var rp = Vector3d.Add(
                r,
                Vector3d.Add(
                    Vector3d.Multiply(v, tgo),
                    Vector3d.Add(rgrv, rthrust)
                    )
                );

            rp = Vector3d.Subtract(
                rp,
                Vector3d.Multiply(
                    iy,
                    Vector3d.Dot(rp, iy)
                    )
                );

            rd = Vector3d.Multiply(
                Vector3d.Normalize(rp),
                rdval
                );

            var ix = Vector3d.Normalize(rd);

            iz = Vector3d.Cross(ix, iy);

            var vv1 = new Vector3d(ix.X, iy.X, iz.Z);
            var vv2 = new Vector3d(ix.Y, iy.Y, iz.Y);
            var vv3 = new Vector3d(ix.Z, iy.Z, iz.Z);
            var vop = new Vector3d(Math.Sin(gamma), 0, Math.Cos(gamma));
            var vd  = new Vector3d(
                Vector3d.Dot(vv1, vop),
                Vector3d.Dot(vv2, vop),
                Vector3d.Dot(vv3, vop)
                );

            vgo = Vector3d.Add(Vector3d.Subtract(
                                   vd,
                                   Vector3d.Subtract(
                                       vgrv,
                                       v
                                       )
                                   ),
                               vbias
                               );

            // var current = new VesselState(cser);
            var current  = new VesselState(t, null, null, v, cser, rbias, rd, rgrv, vgo, previous.Tb + dt, tgo);
            var guidance = new Guidance(iF_, pitch, yaw, 0, 0, tgo);

            return(new List <object>
            {
                current,
                guidance,
                dt
            });
        }
예제 #26
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="v"></param>
        /// <param name="module"></param>
        internal BVController(Vessel v, ConfigNode module)
        {
            vessel   = v;
            BVModule = module;
            displayedSystemCheckResults = new List <DisplayedSystemCheckResult[]>();

            // Load values from config if it isn't the first run of the mod (we are reseting vessel on the first run)
            if (!Configuration.FirstRun)
            {
                active            = bool.Parse(BVModule.GetValue("active") != null ? BVModule.GetValue("active") : "false");
                shutdown          = bool.Parse(BVModule.GetValue("shutdown") != null ? BVModule.GetValue("shutdown") : "false");
                arrived           = bool.Parse(BVModule.GetValue("arrived") != null ? BVModule.GetValue("arrived") : "false");
                targetLatitude    = double.Parse(BVModule.GetValue("targetLatitude") != null ? BVModule.GetValue("targetLatitude") : "0");
                targetLongitude   = double.Parse(BVModule.GetValue("targetLongitude") != null ? BVModule.GetValue("targetLongitude") : "0");
                distanceToTarget  = double.Parse(BVModule.GetValue("distanceToTarget") != null ? BVModule.GetValue("distanceToTarget") : "0");
                distanceTravelled = double.Parse(BVModule.GetValue("distanceTravelled") != null ? BVModule.GetValue("distanceTravelled") : "0");
                if (BVModule.GetValue("pathEncoded") != null)
                {
                    path = PathUtils.DecodePath(BVModule.GetValue("pathEncoded"));
                }

                if (BVModule.GetValue("rotationVector") != null)
                {
                    switch (BVModule.GetValue("rotationVector"))
                    {
                    case "0":
                        rotationVector = Vector3d.up;
                        break;

                    case "1":
                        rotationVector = Vector3d.down;
                        break;

                    case "2":
                        rotationVector = Vector3d.forward;
                        break;

                    case "3":
                        rotationVector = Vector3d.back;
                        break;

                    case "4":
                        rotationVector = Vector3d.right;
                        break;

                    case "5":
                        rotationVector = Vector3d.left;
                        break;

                    default:
                        rotationVector = Vector3d.back;
                        break;
                    }
                }
                else
                {
                    rotationVector = Vector3d.back;
                }
            }

            State = VesselState.Idle;
            if (shutdown)
            {
                State = VesselState.ControllerDisabled;
            }

            lastTimeUpdated     = 0;
            mainStarIndex       = 0; // In the most cases The Sun
            electricPower_Solar = 0;
            electricPower_Other = 0;
            requiredPower       = 0;
        }
예제 #27
0
 public static void LogAchievement(Achievement achievement, VesselState previous, VesselState current)
 {
     Debug.Log("FF: ribbon achievement (vessel state):");
     LogAchievement(achievement);
     Debug.Log("+ previous vessel state:");
     LogVesselState(previous);
     Debug.Log("+ current vessel state:");
     LogVesselState(current);
     Debug.Log("+ - end -");
 }
예제 #28
0
 private void OnGameSceneLoadRequested(GameScenes scene)
 {
     Log.Info("EventObserver:: OnGameSceneLoadRequested: " + scene + " current=" + HighLogic.LoadedScene);
     this.previousVesselState = null;
 }
예제 #29
0
    // The list of throttles is ordered under the assumption that you iterate
    // over the vessel as follows:
    //      foreach part in vessel.parts:
    //          foreach rcsModule in part.Modules.OfType<ModuleRCS>:
    //              ...
    // Note that rotation balancing is not supported at the moment.
    public void GetThrottles(Vessel vessel, VesselState state, Vector3 direction,
                             out double[] throttles, out List <RCSSolver.Thruster> thrustersOut)
    {
        thrustersOut = callerThrusters;

        Vector3 rotation = Vector3.zero;

        var rcsBalancer = VesselExtensions.GetMasterMechJeb(vessel).rcsbal;

        // Update vessel info if needed.
        CheckVessel(vessel, state);

        Vector3      dir = direction.normalized;
        RCSSolverKey key = new RCSSolverKey(ref dir, rotation);

        if (thrusters.Count == 0)
        {
            throttles = double0;
        }
        else if (direction == Vector3.zero)
        {
            throttles = originalThrottles;
        }
        else if (results.TryGetValue(key, out throttles))
        {
            cacheHits++;
        }
        else
        {
            // This task hasn't been calculated. We'll handle that here.
            // Meanwhile, TryGetValue() will have set 'throttles' to null, but
            // we'll make it a 0-element array instead to avoid null checks.
            cacheMisses++;
            throttles = double0;

            if (pending.Contains(key))
            {
                // We've submitted this key before, so we need to check the
                // results queue.
                while (resultsQueue.Count > 0)
                {
                    SolverResult sr = (SolverResult)resultsQueue.Dequeue();
                    results[sr.key] = sr.throttles;
                    pending.Remove(sr.key);
                    if (sr.key == key)
                    {
                        throttles = sr.throttles;
                    }
                }
            }
            else
            {
                // This task was neither calculated nor pending, so we've never
                // submitted it. Do so!
                pending.Add(key);
                tasks.Enqueue(new SolverTask(key, dir, rotation));
                workEvent.Set();
            }
        }

        // Return a copy of the array to make sure ours isn't modified.
        throttles = (double[])throttles.Clone();
    }
예제 #30
0
         private void OnVesselChange(Vessel vessel)
         {
            if(vessel==null)
            {
               Log.Warning("vessel change without a valid vessel detected");
               return;
            }

            // we have to detect a closed orbit again...
            this.orbitClosed = false;

            ResetObserver();

            ResetLandedVesselHasMovedFlag();
            //
            Log.Info("EventObserver:: OnVesselChange " + vessel.GetName());
            if (!vessel.isActiveVessel) return;
            //
            this.previousVesselState = null;
            CheckAchievementsForVessel(vessel);
            //
            Log.Detail("vessel change finished");
         }
예제 #31
0
        public static double SuicideBurnCountdown(Orbit orbit, VesselState vesselState, Vessel vessel)
        {
            if (vesselState.mainBody == null) return 0;
            if (orbit.PeA > 0) return Double.PositiveInfinity;

            double angleFromHorizontal = 90 - Vector3d.Angle(-vessel.srf_velocity, vesselState.up);
            angleFromHorizontal = MuUtils.Clamp(angleFromHorizontal, 0, 90);
            double sine = Math.Sin(angleFromHorizontal * Math.PI / 180);
            double g = vesselState.localg;
            double T = vesselState.limitedMaxThrustAccel;

            double effectiveDecel = 0.5 * (-2 * g * sine + Math.Sqrt((2 * g * sine) * (2 * g * sine) + 4 * (T * T - g * g)));
            double decelTime = vesselState.speedSurface / effectiveDecel;

            Vector3d estimatedLandingSite = vesselState.CoM + 0.5 * decelTime * vessel.srf_velocity;
            double terrainRadius = vesselState.mainBody.Radius + vesselState.mainBody.TerrainAltitude(estimatedLandingSite);
            double impactTime = 0;
            try
            {
                impactTime = orbit.NextTimeOfRadius(vesselState.time, terrainRadius);
            }
            catch (ArgumentException)
            {
                return 0;
            }
            return impactTime - decelTime / 2 - vesselState.time;
        }
예제 #32
0
 private void CheckRibbonForVessel(Ribbon ribbon, VesselState previous, VesselState current, EventReport report, bool hasToBeFirst)
 {
    if (Log.IsLogable(Log.LEVEL.TRACE)) Log.Trace("checking ribbon " + ribbon.GetName());
    Achievement achievement = ribbon.GetAchievement();
    if (achievement.HasToBeFirst() == hasToBeFirst)
    {
       Vessel vessel = current.Origin;
       // check situation changes
       if (Log.IsLogable(Log.LEVEL.TRACE)) Log.Trace("checking change in situation");
       if (achievement.Check(previous, current))
       {
          recorder.Record(ribbon, vessel);
       }
       // check events
       if (Log.IsLogable(Log.LEVEL.TRACE)) Log.Trace("checking report");
       if (report != null && achievement.Check(report))
       {
          recorder.Record(ribbon, vessel);
       }
    }
 }
예제 #33
0
        private VesselState AddPatch(VesselState startingState)
        {
            CelestialBody body = startingState.referenceBody;

            var patch = new Patch();
            patch.startingState = startingState;           
            patch.isAtmospheric = false;
            patch.spaceOrbit = new Orbit();
            patch.spaceOrbit.UpdateFromStateVectors(startingState.position, startingState.velocity, body, startingState.time);
            patch.endTime = patch.startingState.time + patch.spaceOrbit.period;

            // TODO: predict encounters and use maneuver nodes
            // easy to do for encounters and maneuver nodes before the first atmospheric entry (just follow the KSP flight plan)
            // more difficult to do after aerobraking or other custom trajectory modifications (need to implement independent encounter algorithm? snap future maneuver nodes to the modified trajectory?)

            if (patch.spaceOrbit.PeA < body.maxAtmosphereAltitude)
            {
                double entryTime;
                if (startingState.position.magnitude <= body.Radius + body.maxAtmosphereAltitude)
                {
                    // whole orbit is inside the atmosphere
                    entryTime = startingState.time;
                }
                else
                {
                    // binary search of entry time in atmosphere
                    // I guess an analytic solution could be found, but I'm too lazy to search it
                    double from = startingState.time;
                    double to = from + patch.spaceOrbit.timeToPe;

                    while (to - from > 5.0)
                    {
                        double middle = (from + to) * 0.5;
                        if (patch.spaceOrbit.getRelativePositionAtUT(middle).magnitude < body.Radius + body.maxAtmosphereAltitude)
                        {
                            to = middle;
                        }
                        else
                        {
                            from = middle;
                        }
                    }

                    entryTime = to;
                }

                if (entryTime > startingState.time + 10.0)
                {
                    // add the space patch before atmospheric entry
                    patch.endTime = entryTime;

                    if (body.atmosphere)
                    {
                        patches_.Add(patch);
                        return new VesselState
                        {
                            position = patch.spaceOrbit.getRelativePositionAtUT(entryTime),
                            referenceBody = body,
                            time = entryTime,
                            velocity = patch.spaceOrbit.getOrbitalVelocityAtUT(entryTime)
                        };
                    }
                    else
                    {
                        // the body has no atmosphere, so what we actually computed is the impact on the body surface
                        // now, go back in time until the impact point is above the ground to take ground height in account
                        // we assume the ground is horizontal around the impact position
                        double groundAltitude = GetGroundAltitude(body, calculateRotatedPosition(body, patch.spaceOrbit.getRelativePositionAtUT(entryTime), entryTime)) + body.Radius;

                        double iterationSize = 1.0;
                        while (entryTime > startingState.time + iterationSize && patch.spaceOrbit.getRelativePositionAtUT(entryTime).magnitude < groundAltitude)
                            entryTime -= iterationSize;

                        patch.endTime = entryTime;
                        patch.impactPosition = calculateRotatedPosition(body, patch.spaceOrbit.getRelativePositionAtUT(entryTime), entryTime);
                        patch.impactVelocity = patch.spaceOrbit.getOrbitalVelocityAtUT(entryTime);
                        patches_.Add(patch);
                        return null;
                    }
                }
                else
                {
                    // simulate atmospheric flight (drag and lift), until landing (more likely to be a crash as we don't predict user piloting) or atmosphere exit (typically for an aerobraking maneuver)
                    // the simulation assumes a constant angle of attack

                    patch.isAtmospheric = true;

                    double G = 6.674E-11;
                    double dt = 0.15; // lower dt would be more accurate, but a tradeoff has to be found between performances and accuracy

                    int maxIterations = (int)(30.0 * 60.0 / dt); // some shallow entries can result in very long flight, for performances reasons, we limit the prediction duration

                    int chunkSize = 128;
                    double trajectoryInterval = 5.0; // time between two consecutive stored positions (more intermediate positions are computed for better accuracy)
                    var buffer = new List<Vector3[]>();
                    buffer.Add(new Vector3[chunkSize]);
                    int nextPosIdx = 0;
                    
                    Vector3d pos = patch.spaceOrbit.getRelativePositionAtUT(entryTime);
                    Vector3d vel = patch.spaceOrbit.getOrbitalVelocityAtUT(entryTime);
                    //Util.PostSingleScreenMessage("initial vel", "initial vel = " + vel);
                    double currentTime = entryTime;
                    double lastPositionStored = 0;
                    int iteration = 0;
                    while (true)
                    {
                        ++iteration;

                        double R = pos.magnitude;
                        double altitude = R - body.Radius;
                        double atmosphereCoeff = altitude / body.maxAtmosphereAltitude;
                        if (atmosphereCoeff <= 0.0 || atmosphereCoeff >= 1.0 || iteration == maxIterations)
                        {
                            if (atmosphereCoeff <= 0.0)
                            {
                                //rewind trajectory a bit to get actual intersection with the ground (we assume the ground is horizontal around the impact position)
                                double groundAltitude = GetGroundAltitude(body, calculateRotatedPosition(body, pos, currentTime)) + body.Radius;
                                if (nextPosIdx == 0 && buffer.Count > 1)
                                {
                                    nextPosIdx = chunkSize;
                                    buffer.RemoveAt(buffer.Count - 1);
                                }
                                while (pos.magnitude <= groundAltitude)
                                {
                                    --nextPosIdx;
                                    currentTime -= dt;
                                    if (nextPosIdx == 0)
                                    {
                                        if (buffer.Count == 1)
                                            break;
                                        nextPosIdx = chunkSize;
                                        buffer.RemoveAt(buffer.Count - 1);
                                    }
                                    pos = buffer.Last()[nextPosIdx - 1];
                                }

                                if (Settings.fetch.BodyFixedMode) {
                                    //if we do fixed-mode calculations, pos is already rotated
                                    patch.impactPosition = pos;
                                } else {
                                    patch.impactPosition = calculateRotatedPosition(body, pos, currentTime);
                                }
                                patch.impactVelocity = vel;
                            }

                            patch.endTime = currentTime;

                            int totalCount = (buffer.Count - 1) * chunkSize + nextPosIdx;
                            patch.atmosphericTrajectory = new Vector3[totalCount];
                            int outIdx = 0;
                            foreach (var chunk in buffer)
                            {
                                foreach (var p in chunk)
                                {
                                    if (outIdx == totalCount)
                                        break;
                                    patch.atmosphericTrajectory[outIdx++] = p;
                                }
                            }

                            if (iteration == maxIterations)
                            {
                                ScreenMessages.PostScreenMessage("WARNING: trajectory prediction stopped, too many iterations");
                                patches_.Add(patch);
                                return null;
                            }
                            else if (atmosphereCoeff <= 0.0)
                            {
                                patches_.Add(patch);
                                return null;
                            }
                            else
                            {
                                patches_.Add(patch);
                                return new VesselState
                                {
                                    position = pos,
                                    velocity = vel,
                                    referenceBody = body,
                                    time = currentTime
                                };
                            }
                        }

                        Vector3d gravityAccel = pos * (-G * body.Mass / (R * R * R));
                        vel += gravityAccel * dt;
                        //Util.PostSingleScreenMessage("prediction vel", "prediction vel = " + vel);
                        Vector3d airVelocity = vel - body.getRFrmVel(body.position + pos);
                        double angleOfAttack = DescentProfile.fetch.GetAngleOfAttack(body, pos, airVelocity);
                        vel += aerodynamicModel_.computeForces(body, pos, airVelocity, angleOfAttack, dt) * (dt / aerodynamicModel_.mass);
                        pos += vel * dt;

                        currentTime += dt;

                        double interval = altitude < 15000.0 ? trajectoryInterval * 0.1 : trajectoryInterval;
                        if (currentTime >= lastPositionStored + interval)
                        {
                            lastPositionStored = currentTime;
                            if (nextPosIdx == chunkSize)
                            {
                                buffer.Add(new Vector3[chunkSize]);
                                nextPosIdx = 0;
                            }
                            Vector3d nextPos = pos;
                            if (Settings.fetch.BodyFixedMode) {
                                nextPos = calculateRotatedPosition(body, nextPos, currentTime);
                            }
                            buffer.Last()[nextPosIdx++] = nextPos;
                        }
                    }
                }
            }
            else
            {
                // no atmospheric entry, just add the space orbit
                patches_.Add(patch);
                return null;
            }
        }
예제 #34
0
 private void OnCrewOnEva(GameEvents.FromToAction<Part, Part> action)
 {
    Log.Detail("EventObserver:: crew on EVA");
    if (action.from == null || action.from.vessel == null) return;
    if (action.to == null || action.to.vessel == null) return;
    Vessel vessel = action.from.vessel;
    Vessel crew = action.to.vessel;
    // record EVA
    foreach(ProtoCrewMember member in crew.GetVesselCrew())
    {
       recorder.RecordEva(member, vessel);
    }
    // the previous vessel shoud be previous
    this.previousVesselState = new VesselState(vessel);
    // current vessel is crew
    CheckAchievementsForVessel(crew);
 }
예제 #35
0
 void Start()
 {
     Log("Starting");
     try
     {
         mucore.init();
         vesselState = new VesselState();
         attitude = new AttitudeController(this);
         stage = new StageController(this);
         attitude.OnStart();
         stagestats = new StageStats();
         stagestats.editorBody = getVessel.mainBody;
         stagestats.OnModuleEnabled();
         stagestats.OnFixedUpdate();
         stagestats.RequestUpdate(this);
         stagestats.OnFixedUpdate();
         CreateButtonIcon();
         LaunchName = new string(getVessel.vesselName.ToCharArray());
         LaunchBody = getVessel.mainBody;
         launchdb = new LaunchDB(this);
         launchdb.Load();
         mainWindow = new Window.MainWindow(this, 6378070);
         flightMapWindow = new Window.FlightMapWindow(this, 548302);
     }
     catch (Exception ex)
     {
         Log(ex.ToString());
     }
 }
예제 #36
0
 public VesselState NonEva()
 {
    VesselState state = new VesselState(this);
    state.IsEVA = false;
    return state;
 }
        private IEnumerable <bool> AddPatch(VesselState startingState)
        {
            if (null == AttachedVessel.patchedConicSolver)
            {
                AttachedVessel.AttachPatchedConicsSolver();
            }

            CelestialBody body = startingState.ReferenceBody;

            Patch patch = new Patch
            {
                StartingState = startingState,
                IsAtmospheric = false,
                SpaceOrbit    = startingState.StockPatch ?? CreateOrbitFromState(startingState)
            };

            patch.EndTime = patch.StartingState.Time + patch.SpaceOrbit.period;

            // the flight plan does not always contain the first patches (before the first maneuver node),
            // so we populate it with the current orbit and associated encounters etc.
            List <Orbit> flightPlan = new List <Orbit>();

            for (Orbit orbit = AttachedVessel.orbit; orbit != null && orbit.activePatch; orbit = orbit.nextPatch)
            {
                if (AttachedVessel.patchedConicSolver.flightPlan.Contains(orbit))
                {
                    break;
                }
                flightPlan.Add(orbit);
            }

            foreach (Orbit orbit in AttachedVessel.patchedConicSolver.flightPlan)
            {
                flightPlan.Add(orbit);
            }


            Orbit nextPatch = null;

            if (startingState.StockPatch == null)
            {
                nextPatch = patch.SpaceOrbit.nextPatch;
            }
            else
            {
                int planIdx = flightPlan.IndexOf(startingState.StockPatch);
                if (planIdx >= 0 && planIdx < flightPlan.Count - 1)
                {
                    nextPatch = flightPlan[planIdx + 1];
                }
            }

            if (nextPatch != null)
            {
                patch.EndTime = nextPatch.StartUT;
            }

            double maxAtmosphereAltitude = RealMaxAtmosphereAltitude(body);

            if (!body.atmosphere)
            {
                maxAtmosphereAltitude = body.pqsController.mapMaxHeight;
            }

            double minAltitude = patch.SpaceOrbit.PeA;

            if (patch.SpaceOrbit.timeToPe < 0 || patch.EndTime < startingState.Time + patch.SpaceOrbit.timeToPe)
            {
                minAltitude = Math.Min(
                    patch.SpaceOrbit.getRelativePositionAtUT(patch.EndTime).magnitude,
                    patch.SpaceOrbit.getRelativePositionAtUT(patch.StartingState.Time + 1.0).magnitude
                    ) - body.Radius;
            }

            if (minAltitude < maxAtmosphereAltitude)
            {
                double entryTime;
                if (startingState.Position.magnitude <= body.Radius + maxAtmosphereAltitude)
                {
                    // whole orbit is inside the atmosphere
                    entryTime = startingState.Time;
                }
                else
                {
                    entryTime = FindOrbitBodyIntersection(
                        patch.SpaceOrbit,
                        startingState.Time, startingState.Time + patch.SpaceOrbit.timeToPe,
                        body.Radius + maxAtmosphereAltitude);
                }

                if (entryTime > startingState.Time + 0.1 || !body.atmosphere)
                {
                    if (body.atmosphere)
                    {
                        // add the space patch before atmospheric entry

                        patch.EndTime = entryTime;
                        patchesBackBuffer_.Add(patch);
                        AddPatch_outState = new VesselState
                        {
                            Position      = patch.SpaceOrbit.getRelativePositionAtUT(entryTime).SwapYZ(),
                            ReferenceBody = body,
                            Time          = entryTime,
                            Velocity      = patch.SpaceOrbit.getOrbitalVelocityAtUT(entryTime).SwapYZ()
                        };
                    }
                    else
                    {
                        // the body has no atmosphere, so what we actually computed is the entry
                        // inside the "ground sphere" (defined by the maximal ground altitude)
                        // now we iterate until the inner ground sphere (minimal altitude), and
                        // check if we hit the ground along the way
                        double groundRangeExit = FindOrbitBodyIntersection(
                            patch.SpaceOrbit,
                            startingState.Time, startingState.Time + patch.SpaceOrbit.timeToPe,
                            body.Radius - maxAtmosphereAltitude);

                        if (groundRangeExit <= entryTime)
                        {
                            groundRangeExit = startingState.Time + patch.SpaceOrbit.timeToPe;
                        }

                        double iterationSize = (groundRangeExit - entryTime) / 100.0;
                        double t;
                        bool   groundImpact = false;
                        for (t = entryTime; t < groundRangeExit; t += iterationSize)
                        {
                            Vector3d pos            = patch.SpaceOrbit.getRelativePositionAtUT(t);
                            double   groundAltitude = GetGroundAltitude(body, CalculateRotatedPosition(body, pos.SwapYZ(), t))
                                                      + body.Radius;
                            if (pos.magnitude < groundAltitude)
                            {
                                t           -= iterationSize;
                                groundImpact = true;
                                break;
                            }
                        }

                        if (groundImpact)
                        {
                            patch.EndTime           = t;
                            patch.RawImpactPosition = patch.SpaceOrbit.getRelativePositionAtUT(t).SwapYZ();
                            patch.ImpactPosition    = CalculateRotatedPosition(body, patch.RawImpactPosition.Value, t);
                            patch.ImpactVelocity    = patch.SpaceOrbit.getOrbitalVelocityAtUT(t).SwapYZ();
                            patchesBackBuffer_.Add(patch);
                            AddPatch_outState = null;
                        }
                        else
                        {
                            // no impact, just add the space orbit
                            patchesBackBuffer_.Add(patch);
                            if (nextPatch != null)
                            {
                                AddPatch_outState = new VesselState
                                {
                                    Position      = patch.SpaceOrbit.getRelativePositionAtUT(patch.EndTime).SwapYZ(),
                                    Velocity      = patch.SpaceOrbit.getOrbitalVelocityAtUT(patch.EndTime).SwapYZ(),
                                    ReferenceBody = nextPatch.referenceBody,
                                    Time          = patch.EndTime,
                                    StockPatch    = nextPatch
                                };
                            }
                            else
                            {
                                AddPatch_outState = null;
                            }
                        }
                    }
                }
                else
                {
                    if (patch.StartingState.ReferenceBody != AttachedVessel.mainBody)
                    {
                        // currently, we can't handle predictions for another body, so we stop
                        AddPatch_outState = null;
                        yield break;
                    }

                    // simulate atmospheric flight (drag and lift), until impact or atmosphere exit
                    // (typically for an aerobraking maneuver) assuming a constant angle of attack
                    patch.IsAtmospheric            = true;
                    patch.StartingState.StockPatch = null;

                    // lower dt would be more accurate, but a trade-off has to be found between performances and accuracy
                    double dt = Settings.IntegrationStepSize;

                    // some shallow entries can result in very long flight. For performances reasons,
                    // we limit the prediction duration
                    int maxIterations = (int)(60.0 * 60.0 / dt);

                    int chunkSize = 128;

                    // time between two consecutive stored positions (more intermediate positions are computed for better accuracy),
                    // also used for ground collision checks
                    double trajectoryInterval = 10.0;

                    List <Point[]> buffer = new List <Point[]>
                    {
                        new Point[chunkSize]
                    };
                    int nextPosIdx = 0;

                    SimulationState state;
                    state.position = patch.SpaceOrbit.getRelativePositionAtUT(entryTime).SwapYZ();
                    state.velocity = patch.SpaceOrbit.getOrbitalVelocityAtUT(entryTime).SwapYZ();

                    // Initialize a patch with zero acceleration
                    Vector3d currentAccel = new Vector3d(0.0, 0.0, 0.0);


                    double   currentTime               = entryTime;
                    double   lastPositionStoredUT      = 0;
                    Vector3d lastPositionStored        = new Vector3d();
                    bool     hitGround                 = false;
                    int      iteration                 = 0;
                    int      incrementIterations       = 0;
                    int      minIterationsPerIncrement = maxIterations / Settings.MaxFramesPerPatch;

                    #region Acceleration Functor

                    // function that calculates the acceleration under current parameters
                    Vector3d AccelerationFunc(Vector3d position, Vector3d velocity)
                    {
                        Profiler.Start("accelerationFunc inside");

                        // gravity acceleration
                        double   R_      = position.magnitude;
                        Vector3d accel_g = position * (-body.gravParameter / (R_ * R_ * R_));

                        // aero force
                        Vector3d vel_air = velocity - body.getRFrmVel(body.position + position);

                        double aoa = DescentProfile.GetAngleOfAttack(body, position, vel_air) ?? 0d;

                        Profiler.Start("GetForces");
                        Vector3d force_aero = aerodynamicModel_.GetForces(body, position, vel_air, aoa);

                        Profiler.Stop("GetForces");

                        Vector3d accel = accel_g + force_aero / aerodynamicModel_.Mass;

                        accel += API.HandleAccel(AttachedVessel, body, position, velocity, aoa);

                        Profiler.Stop("accelerationFunc inside");
                        return(accel);
                    }

                    #endregion


                    #region Integration Loop

                    while (true)
                    {
                        ++iteration;
                        ++incrementIterations;

                        if (incrementIterations > minIterationsPerIncrement && Util.ElapsedMilliseconds(increment_time) > MAX_INCREMENT_TIME)
                        {
                            yield return(false);

                            incrementIterations = 0;
                        }


                        double R               = state.position.magnitude;
                        double altitude        = R - body.Radius;
                        double atmosphereCoeff = altitude / maxAtmosphereAltitude;
                        if (hitGround ||
                            atmosphereCoeff <= 0.0 || atmosphereCoeff >= 1.0 ||
                            iteration == maxIterations || currentTime > patch.EndTime)
                        {
                            //Util.PostSingleScreenMessage("atmo force", "Atmospheric accumulated force: " + accumulatedForces.ToString("0.00"));

                            if (hitGround || atmosphereCoeff <= 0.0)
                            {
                                patch.RawImpactPosition = state.position;
                                patch.ImpactPosition    = CalculateRotatedPosition(body, patch.RawImpactPosition.Value, currentTime);
                                patch.ImpactVelocity    = state.velocity;
                            }

                            patch.EndTime = Math.Min(currentTime, patch.EndTime);

                            int totalCount = (buffer.Count - 1) * chunkSize + nextPosIdx;
                            patch.AtmosphericTrajectory = new Point[totalCount];
                            int outIdx = 0;
                            foreach (Point[] chunk in buffer)
                            {
                                foreach (Point p in chunk)
                                {
                                    if (outIdx == totalCount)
                                    {
                                        break;
                                    }
                                    patch.AtmosphericTrajectory[outIdx++] = p;
                                }
                            }

                            if (iteration == maxIterations)
                            {
                                ScreenMessages.PostScreenMessage("WARNING: trajectory prediction stopped, too many iterations");
                                patchesBackBuffer_.Add(patch);
                                AddPatch_outState = null;
                                yield break;
                            }

                            if (atmosphereCoeff <= 0.0 || hitGround)
                            {
                                patchesBackBuffer_.Add(patch);
                                AddPatch_outState = null;
                                yield break;
                            }

                            patchesBackBuffer_.Add(patch);
                            AddPatch_outState = new VesselState
                            {
                                Position      = state.position,
                                Velocity      = state.velocity,
                                ReferenceBody = body,
                                Time          = patch.EndTime
                            };
                            yield break;
                        }

                        Vector3d        lastAccel = currentAccel;
                        SimulationState lastState = state;

                        Profiler.Start("IntegrationStep");

                        // Verlet integration (more precise than using the velocity)
                        // state = VerletStep(state, accelerationFunc, dt);
                        state = RK4Step(state, AccelerationFunc, dt, out currentAccel);

                        currentTime += dt;

                        // KSP presumably uses Euler integration for position updates. Since RK4 is actually more precise than that,
                        // we try to reintroduce an approximation of the error.

                        // The local truncation error for euler integration is:
                        // LTE = 1/2 * h^2 * y''(t)
                        // https://en.wikipedia.org/wiki/Euler_method#Local_truncation_error
                        //
                        // For us,
                        // h is the time step of the outer simulation (KSP), which is the physics time step
                        // y''(t) is the difference of the velocity/acceleration divided by the physics time step
                        state.position += 0.5 * TimeWarp.fixedDeltaTime * currentAccel * dt;
                        state.velocity += 0.5 * TimeWarp.fixedDeltaTime * (currentAccel - lastAccel);

                        Profiler.Stop("IntegrationStep");

                        // calculate gravity and aerodynamic force
                        Vector3d gravityAccel     = lastState.position * (-body.gravParameter / (R * R * R));
                        Vector3d aerodynamicForce = (currentAccel - gravityAccel) / aerodynamicModel_.Mass;

                        // acceleration in the vessel reference frame is acceleration - gravityAccel
                        maxAccelBackBuffer_ = Math.Max(
                            (float)(aerodynamicForce.magnitude / aerodynamicModel_.Mass),
                            maxAccelBackBuffer_);

                        #region Impact Calculation

                        Profiler.Start("AddPatch#impact");

                        double interval = altitude < 10000.0 ? trajectoryInterval * 0.1 : trajectoryInterval;
                        if (currentTime >= lastPositionStoredUT + interval)
                        {
                            double groundAltitude = GetGroundAltitude(body, CalculateRotatedPosition(body, state.position, currentTime));
                            if (lastPositionStoredUT > 0)
                            {
                                // check terrain collision, to detect impact on mountains etc.
                                Vector3 rayOrigin         = lastPositionStored;
                                Vector3 rayEnd            = state.position;
                                double  absGroundAltitude = groundAltitude + body.Radius;
                                if (absGroundAltitude > rayEnd.magnitude)
                                {
                                    hitGround = true;
                                    float coeff = Math.Max(0.01f, (float)((absGroundAltitude - rayOrigin.magnitude)
                                                                          / (rayEnd.magnitude - rayOrigin.magnitude)));
                                    state.position = rayEnd * coeff + rayOrigin * (1.0f - coeff);
                                    currentTime    = currentTime * coeff + lastPositionStoredUT * (1.0f - coeff);
                                }
                            }

                            lastPositionStoredUT = currentTime;
                            if (nextPosIdx == chunkSize)
                            {
                                buffer.Add(new Point[chunkSize]);
                                nextPosIdx = 0;
                            }

                            Vector3d nextPos = state.position;
                            if (Settings.BodyFixedMode)
                            {
                                nextPos = CalculateRotatedPosition(body, nextPos, currentTime);
                            }

                            buffer.Last()[nextPosIdx].aerodynamicForce = aerodynamicForce;
                            buffer.Last()[nextPosIdx].orbitalVelocity  = state.velocity;
                            buffer.Last()[nextPosIdx].groundAltitude   = (float)groundAltitude;
                            buffer.Last()[nextPosIdx].time             = currentTime;
                            buffer.Last()[nextPosIdx++].pos            = nextPos;
                            lastPositionStored = state.position;
                        }

                        Profiler.Stop("AddPatch#impact");

                        #endregion
                    }

                    #endregion
                }
            }
            else
            {
                // no atmospheric entry, just add the space orbit
                patchesBackBuffer_.Add(patch);
                if (nextPatch != null)
                {
                    AddPatch_outState = new VesselState
                    {
                        Position      = patch.SpaceOrbit.getRelativePositionAtUT(patch.EndTime).SwapYZ(),
                        Velocity      = patch.SpaceOrbit.getOrbitalVelocityAtUT(patch.EndTime).SwapYZ(),
                        ReferenceBody = nextPatch.referenceBody,
                        Time          = patch.EndTime,
                        StockPatch    = nextPatch
                    };
                }
                else
                {
                    AddPatch_outState = null;
                }
            }
        }
예제 #38
0
 public VesselState Docked()
 {
    VesselState state = new VesselState(this);
    state.Situation = Vessel.Situations.DOCKED;
    return state;
 }
예제 #39
0
 public VesselState FlagPlanted()
 {
    VesselState state = new VesselState(this);
    state.HasFlagPlanted = true;
    return state;
 }
예제 #40
0
        private Orbit createOrbitFromState(VesselState state)
        {
            var orbit = new Orbit();
            orbit.UpdateFromStateVectors(Util.SwapYZ(state.position), Util.SwapYZ(state.velocity), state.referenceBody, state.time);
			var pars = new PatchedConics.SolverParameters();
			pars.FollowManeuvers = false;
			PatchedConics.CalculatePatch(orbit, new Orbit(), state.time, pars, null);
            return orbit;
        }