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; } }
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(); }
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()); } }
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; }
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; } } } }
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); }
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); } }
/// <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); }
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; }
public ParachuteInfo(VesselState vesselState) { this.vesselState = vesselState; update(vesselState.speedSurface, vesselState.atmosphericDensity); }
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; } } }
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"); } }
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); } }
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(); }
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; } } }
private void OnGameSceneLoadRequested(GameScenes scene) { Log.Info("EventObserver:: OnGameSceneLoadRequested: "+scene+" current="+HighLogic.LoadedScene); this.previousVesselState = null; }
private void OnLandedVesselMove(Vessel vessel) { Log.Detail("EventObserver:: OnLandedVesselMove " + vessel.GetName()); if (vessel.isActiveVessel) { VesselState vesselState = new VesselState(vessel); CheckAchievementsForVessel(vesselState.MovedOnSurface()); } }
public static VesselState CreateFlagPlantedFromVessel(Vessel vessel) { VesselState state = new VesselState(vessel); state.HasFlagPlanted = true; return state; }
public static VesselState CreateLaunchFromVessel(Vessel vessel) { VesselState state = new VesselState(vessel); state.IsLaunch = true; return state; }
public VesselState MovedOnSurface() { VesselState state = new VesselState(this); state.movedOnSurface = true; return state; }
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"); }
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); }
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 }); }
/// <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; }
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 -"); }
private void OnGameSceneLoadRequested(GameScenes scene) { Log.Info("EventObserver:: OnGameSceneLoadRequested: " + scene + " current=" + HighLogic.LoadedScene); this.previousVesselState = null; }
// 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(); }
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"); }
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; }
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); } } }
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; } }
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); }
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()); } }
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; } } }
public VesselState Docked() { VesselState state = new VesselState(this); state.Situation = Vessel.Situations.DOCKED; return state; }
public VesselState FlagPlanted() { VesselState state = new VesselState(this); state.HasFlagPlanted = true; return state; }
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; }