// Doesn't work, just learning to use github /// <summary> /// Find the Intersections of two orbits /// </summary> /// <returns> /// 0 for no intersections found, 1 for 1, 2 for 2 /// True anomaly of intersections in out intersect1 and intersect2 /// </returns> /// <param name='orbit'> /// Orbit. /// </param> /// <param name='tgtorbit'> /// Tgtorbit. /// </param> /// <param name='intersect1'> /// Intsect1. /// </param> /// <param name='intersect2'> /// Intsect2. /// </param> public static int FindIntersectSimple(this Orbit orbit, Orbit tgtorbit, out double intersect1, out double intersect2) { double anomaly = 0; intersect1 = 361; intersect2 = 361; int div = 50; double range = 360; double lowest = 1e10; int count = 0; while (lowest>100) { for (int i=0; i<div; i++) { double j; anomaly += (i / div) * range; j = Math.Abs (orbit.f (tgtorbit, anomaly)); if (i == 0) { lowest = j; intersect1 = anomaly; } if (j < lowest) { lowest = j; intersect1 = anomaly; } } range = range / 2; anomaly = intersect1 - range / 2; count++; if (count > 30) return 2; } return 0; }
/// <summary> /// Calculates position of the orbiting body for given time. It also updates <see cref="CurrentPosition"/> property. /// </summary> /// <param name="time">time from the creation of the start system (time 0)</param> /// <returns>Position as spherical coordinates</returns> public static SphericalCoordinates CalculatePosition(float time, Orbit orbitData) { time = Mathf.Repeat(time, orbitData.Period); //mean anomaly float n = 2f * Mathf.PI / orbitData.Period; float M = n * time; //eccentric anomaly float deltaMax = Mathf.Pow(10, anomalyPrecision * -1); float E = M; float error = E - orbitData.Eccentricity * Mathf.Sin(E) - M; int i = 0; while (Mathf.Abs(error) > deltaMax && i < 5) { E = E - error / (1f - orbitData.Eccentricity * Mathf.Cos(E)); error = E - orbitData.Eccentricity * Mathf.Sin(E) - M; i++; } //true anomaly var S = Mathf.Sin(E); var C = Mathf.Cos(E); var fak = Mathf.Sqrt(1f - orbitData.Eccentricity * orbitData.Eccentricity); float phi = Mathf.Atan2(fak * S, C - orbitData.Eccentricity); //distance // float r = MeanDistance * (1 - Eccentricity * Eccentricity) / (1 + Eccentricity * Mathf.Cos(phi)); float r = orbitData.MeanDistance * (1 - orbitData.Eccentricity * Mathf.Cos(E)); phi = phi + orbitData.PeriapsisArgumentRadians; float elevation = orbitData.InclinationRadians * Mathf.Cos(phi); return new SphericalCoordinates(r, phi, elevation); }
public static ManeuverNode PlaceManeuverNode(this Vessel vessel, Orbit patch, Vector3d dV, double UT) { //placing a maneuver node with bad dV values can really mess up the game, so try to protect against that //and log an exception if we get a bad dV vector: for (int i = 0; i < 3; i++) { if (double.IsNaN(dV[i]) || double.IsInfinity(dV[i])) { throw new Exception("VesselExtensions.PlaceManeuverNode: bad dV: " + dV); } } if (double.IsNaN(UT) || double.IsInfinity(UT)) { throw new Exception("VesselExtensions.PlaceManeuverNode: bad UT: " + UT); } //It seems that sometimes the game can freak out if you place a maneuver node in the past, so this //protects against that. UT = Math.Max(UT, Planetarium.GetUniversalTime()); //convert a dV in world coordinates into the coordinate system of the maneuver node, //which uses (x, y, z) = (radial+, normal-, prograde) Vector3d nodeDV = patch.DeltaVToManeuverNodeCoordinates(UT, dV); ManeuverNode mn = vessel.patchedConicSolver.AddManeuverNode(UT); mn.OnGizmoUpdated(nodeDV, UT); return mn; }
public override ManeuverParameters MakeNodeImpl(Orbit o, double universalTime, MechJebModuleTargetController target) { double UT = timeSelector.ComputeManeuverTime(o, universalTime, target); var dV = OrbitalManeuverCalculator.DeltaVToResonantOrbit(o, UT, (double)resonanceNumerator.val / resonanceDenominator.val); return new ManeuverParameters(dV, UT); }
/// <summary> /// Orbit foo, this finds the nodes of two orbits /// </summary> /// <returns> /// The true anomaly of the ascending node(descing node is 180degrees off) /// </returns> /// <param name='orbit'> /// Orbit. /// </param> /// <param name='tgtorbit'> /// Target Orbit /// </param> public static double FindAN(Orbit orbit, Orbit tgtorbit) { double rad = Math.PI/180; double Lan1 = orbit.LAN; double inc1 = orbit.inclination; double Lan2 = tgtorbit.LAN; double inc2 = tgtorbit.inclination; //see braeunig.us/space... cross product of two orbital planes gives the node location var a = new Vector3d(Math.Sin(inc1*rad)*Math.Cos(Lan1*rad), Math.Sin(inc1*rad)*Math.Sin(Lan1*rad), Math.Cos(inc1*rad)); var b = new Vector3d(Math.Sin(inc2*rad)*Math.Cos(Lan2*rad), Math.Sin(inc2*rad)*Math.Sin(Lan2*rad), Math.Cos(inc2*rad)); var c = new Vector3d(0, 0, 0); c = Vector3d.Cross(a, b); var coord = new double[] {0, 0}; coord = LatLonofVector(c); //get the coordinates lat/lon of the ascending node double lat = coord[0]; double lon = coord[1]; //go look at the diagrams at braeunig.us/space double α = lon - Lan1; //its all greek to me if (α < 0) α += 360; double λ = Math.Atan(Math.Tan(α*rad)/Math.Cos(inc1*rad))/rad; double x = 180 + (λ - orbit.argumentOfPeriapsis); if (x > 360) return 360 - x; else return x; }
private static void DrawOrbit(Orbit o, CelestialBody referenceBody, Matrix4x4 screenTransform, int numSegments) { if (!o.activePatch) { return; } double startTA; double endTA; double now = Planetarium.GetUniversalTime(); if (o.patchEndTransition != Orbit.PatchTransitionType.FINAL) { startTA = o.TrueAnomalyAtUT(o.StartUT); endTA = o.TrueAnomalyAtUT(o.EndUT); if (endTA < startTA) { endTA += 2.0 * Math.PI; } } else { startTA = o.GetUTforTrueAnomaly(0.0, now); endTA = startTA + 2.0 * Math.PI; } double dTheta = (endTA - startTA) / (double)numSegments; double theta = startTA; double timeAtTA = o.GetUTforTrueAnomaly(theta, now); Vector3 lastVertex = screenTransform.MultiplyPoint3x4(o.getRelativePositionFromTrueAnomaly(theta).xzy + (o.referenceBody.getTruePositionAtUT(timeAtTA)) - (referenceBody.getTruePositionAtUT(timeAtTA))); for (int i = 0; i < numSegments; ++i) { GL.Vertex3(lastVertex.x, lastVertex.y, 0.0f); theta += dTheta; timeAtTA = o.GetUTforTrueAnomaly(theta, now); Vector3 newVertex = screenTransform.MultiplyPoint3x4(o.getRelativePositionFromTrueAnomaly(theta).xzy + (o.referenceBody.getTruePositionAtUT(timeAtTA)) - (referenceBody.getTruePositionAtUT(timeAtTA))); GL.Vertex3(newVertex.x, newVertex.y, 0.0f); lastVertex = newVertex; } }
public static Vector3d DeltaVAndTimeForCheapestCourseCorrection(Orbit o, double UT, Orbit target, CelestialBody targetBody, double finalPeR, out double burnUT) { Vector3d collisionDV = DeltaVAndTimeForCheapestCourseCorrection(o, UT, target, out burnUT); Orbit collisionOrbit = o.PerturbedOrbit(burnUT, collisionDV); double collisionUT = collisionOrbit.NextClosestApproachTime(target, burnUT); Vector3d collisionPosition = target.SwappedAbsolutePositionAtUT(collisionUT); Vector3d collisionRelVel = collisionOrbit.SwappedOrbitalVelocityAtUT(collisionUT) - target.SwappedOrbitalVelocityAtUT(collisionUT); double soiEnterUT = collisionUT - targetBody.sphereOfInfluence / collisionRelVel.magnitude; Vector3d soiEnterRelVel = collisionOrbit.SwappedOrbitalVelocityAtUT(soiEnterUT) - target.SwappedOrbitalVelocityAtUT(soiEnterUT); double E = 0.5 * soiEnterRelVel.sqrMagnitude - targetBody.gravParameter / targetBody.sphereOfInfluence; //total orbital energy on SoI enter double finalPeSpeed = Math.Sqrt(2 * (E + targetBody.gravParameter / finalPeR)); //conservation of energy gives the orbital speed at finalPeR. double desiredImpactParameter = finalPeR * finalPeSpeed / soiEnterRelVel.magnitude; //conservation of angular momentum gives the required impact parameter Vector3d displacementDir = Vector3d.Cross(collisionRelVel, o.SwappedOrbitNormal()).normalized; Vector3d interceptTarget = collisionPosition + desiredImpactParameter * displacementDir; Vector3d velAfterBurn; Vector3d arrivalVel; LambertSolver.Solve(o.SwappedRelativePositionAtUT(burnUT), interceptTarget - o.referenceBody.position, collisionUT - burnUT, o.referenceBody, true, out velAfterBurn, out arrivalVel); Vector3d deltaV = velAfterBurn - o.SwappedOrbitalVelocityAtUT(burnUT); return deltaV; }
public OrbitInfo(Orbitable orb, SharedObjects sharedObj) { orbit = orb.Orbit; shared = sharedObj; name = orb.GetName(); InitializeSuffixes(); }
public OrbitInfo( Orbit orb, SharedObjects sharedObj ) { shared = sharedObj; orbit = orb; name = "<unnamed>"; InitializeSuffixes(); }
public override ManeuverParameters MakeNodeImpl(Orbit o, double UT, MechJebModuleTargetController target) { if (!target.NormalTargetExists) throw new OperationException("must select a target for the course correction."); Orbit correctionPatch = o; while (correctionPatch != null) { if (correctionPatch.referenceBody == target.TargetOrbit.referenceBody) { o = correctionPatch; UT = correctionPatch.StartUT; break; } correctionPatch = target.core.vessel.GetNextPatch(correctionPatch); } if (correctionPatch == null || correctionPatch.referenceBody != target.TargetOrbit.referenceBody) throw new OperationException("target for course correction must be in the same sphere of influence"); if (o.NextClosestApproachTime(target.TargetOrbit, UT) < UT + 1 || o.NextClosestApproachDistance(target.TargetOrbit, UT) > target.TargetOrbit.semiMajorAxis * 0.2) { errorMessage = "Warning: orbit before course correction doesn't seem to approach target very closely. Planned course correction may be extreme. Recommend plotting an approximate intercept orbit and then plotting a course correction."; } CelestialBody targetBody = target.Target as CelestialBody; Vector3d dV = targetBody != null ? OrbitalManeuverCalculator.DeltaVAndTimeForCheapestCourseCorrection(o, UT, target.TargetOrbit, targetBody, targetBody.Radius + courseCorrectFinalPeA, out UT): OrbitalManeuverCalculator.DeltaVAndTimeForCheapestCourseCorrection(o, UT, target.TargetOrbit, interceptDistance, out UT); return new ManeuverParameters(dV, UT); }
/// <summary> /// Find the point on the orbit that includes the initial time where the ejection angle is closest to the suplied one /// </summary> /// <param name="oObject">Orbit of the vessel/body</param> /// <param name="timeInitial">The UT you want to search around for the angle - will search 1/2 an orbit back and 1/2 forward</param> /// <param name="numDivisions">Higher thisnumber the more precise the answer - and the longer it will take</param> /// <param name="closestAngle">The output of the closest angle the method could find</param> /// <param name="targetAngle">The ejection angle we are looking for</param> /// <returns></returns> internal static double timeOfEjectionAngle(Orbit oObject, double timeInitial, double targetAngle, double numDivisions, out double closestAngle) { double timeStart = timeInitial - oObject.period/2; double periodtoscan = oObject.period; double closestAngleTime = timeStart; double closestAngleValue = Double.MaxValue; double minTime = timeStart; double maxTime = timeStart + periodtoscan; //work out iterations for precision - we only really need to within a second - so how many iterations do we actually need //Each iteration gets us 1/10th of the period to scan for (int iter = 0; iter < 8; iter++) { double dt = (maxTime - minTime) / numDivisions; for (int i = 0; i < numDivisions; i++) { double t = minTime + i * dt; double angle = oObject.getEjectionAngleAtUT(t); if (Math.Abs(angle - targetAngle) < closestAngleValue) { closestAngleValue = Math.Abs(angle - targetAngle); closestAngleTime = t; } } minTime = (closestAngleTime - dt).Clamp(timeStart, timeStart + periodtoscan); maxTime = (closestAngleTime + dt).Clamp(timeStart, timeStart + periodtoscan); } closestAngle = closestAngleValue + targetAngle; return closestAngleTime; }
protected TransferCalculator( Orbit o, Orbit target, double min_departure_time, double max_departure_time, double min_transfer_time, double max_transfer_time, int width, int height) { originOrbit = o; destinationOrbit = target; origin = new Orbit(); origin.UpdateFromOrbitAtUT(o, min_departure_time, o.referenceBody); destination = new Orbit(); destination.UpdateFromOrbitAtUT(target, min_departure_time, target.referenceBody); maxDurationSamples = height; dateSamples = width; nextDateIndex = dateSamples; this.minDepartureTime = min_departure_time; this.maxDepartureTime = max_departure_time; this.minTransferTime = min_transfer_time; this.maxTransferTime = max_transfer_time; computed = new ManeuverParameters[dateSamples, maxDurationSamples]; pendingJobs = 0; #if DEBUG log = new string[dateSamples, maxDurationSamples]; #endif }
static void WarpShip(Vessel vessel, Orbit newOrbit) { if (newOrbit.getRelativePositionAtUT (Planetarium.GetUniversalTime ()).magnitude > newOrbit.referenceBody.sphereOfInfluence) throw new ArgumentException ("Destination position was above the sphere of influence"); vessel.Landed = false; vessel.Splashed = false; vessel.landedAt = string.Empty; var parts = vessel.parts; if (parts != null) { var clamps = parts.Where (p => p.Modules != null && p.Modules.OfType<LaunchClamp> ().Any ()).ToList (); foreach (var clamp in clamps) clamp.Die (); } try { OrbitPhysicsManager.HoldVesselUnpack (60); } catch (NullReferenceException) { } foreach (var v in (FlightGlobals.fetch == null ? (IEnumerable<Vessel>)new[] { vessel } : FlightGlobals.Vessels).Where(v => !v.packed)) v.GoOnRails (); HardsetOrbit (vessel.orbit, newOrbit); vessel.orbitDriver.pos = vessel.orbit.pos.xzy; vessel.orbitDriver.vel = vessel.orbit.vel; }
/// <summary> /// Initializes the script. /// </summary> void Start() { // Store the central object original position originalCentralBodyPosition = CentralObject.transform.position; // Calculate the up vector if not entered if (Up == Vector3.zero) Up = CentralObject.transform.up; orbits = new Orbit[OrbitingObjects.Length]; // for each orbiting object, calculate the normal vector to its orbital plane for (int i=0; i<OrbitingObjects.Length; i++) { // Get the normalized direction vector between the two objects Vector3 direction = (OrbitingObjects[i].orbitingObject.transform.position - CentralObject.transform.position).normalized; // Calculate a perpendicular vector to the orbital plane Vector3 normal = Vector3.Cross (direction, Up); normal = Vector3.Cross (normal, direction); // Add a new Orbit containing all the information needed for the orbit to the array of orbits orbits[i] = new Orbit (OrbitingObjects[i].orbitingObject, OrbitingObjects[i].revolutionsPerMinute, normal); } }
public VesselData(VesselData vd) { name = vd.name; id = vd.id; craftURL = vd.craftURL; craftPart = vd.craftPart; flagURL = vd.flagURL; vesselType = vd.vesselType; body = vd.body; orbit = vd.orbit; latitude = vd.latitude; longitude = vd.longitude; altitude = vd.altitude; height = vd.height; orbiting = vd.orbiting; owned = vd.owned; pqsCity = vd.pqsCity; pqsOffset = vd.pqsOffset; heading = vd.heading; pitch = vd.pitch; roll = vd.roll; foreach (CrewData cd in vd.crew) { crew.Add(new CrewData(cd)); } }
public void EllipticSector() { var o = new Orbit { SemiMajorAxis = 2, SemiMinorAxis = 1, TransformAngle = 0 }; AssertAreClose(o.S(Math.PI), 0); AssertAreClose(o.S(0), Math.PI); AssertAreClose(o.S(1, -Math.PI), 2 * Math.PI); }
public override ManeuverParameters MakeNodeImpl(Orbit o, double universalTime, MechJebModuleTargetController target) { double UT = timeSelector.ComputeManeuverTime(o, universalTime, target); if (!target.NormalTargetExists) { throw new OperationException("must select a target to match planes with."); } else if (o.referenceBody != target.TargetOrbit.referenceBody) { throw new OperationException("can only match planes with an object in the same sphere of influence."); } else if (timeSelector.timeReference == TimeReference.REL_ASCENDING) { if (!o.AscendingNodeExists(target.TargetOrbit)) { throw new OperationException("ascending node with target doesn't exist."); } } else { if (!o.DescendingNodeExists(target.TargetOrbit)) { throw new OperationException("descending node with target doesn't exist."); } } Vector3d dV = (timeSelector.timeReference == TimeReference.REL_ASCENDING) ? OrbitalManeuverCalculator.DeltaVAndTimeToMatchPlanesAscending(o, target.TargetOrbit, UT, out UT): OrbitalManeuverCalculator.DeltaVAndTimeToMatchPlanesDescending(o, target.TargetOrbit, UT, out UT); return new ManeuverParameters(dV, UT); }
double tauP; //normalized parabolic transfer time #endregion Fields #region Constructors /// <summary> /// Initializes a new instance of the <see cref="ThrottleControlledAvionics.LambertSolver"/> class. /// </summary> /// <param name="orb">Starting orbit.</param> /// <param name="destination">Destination radius-vector.</param> /// <param name="UT">Starting UT.</param> public LambertSolver(Orbit orb, Vector3d destination, double UT) { orbit = orb; body = orbit.referenceBody; StartUT = UT; mu = body.gravParameter; r1 = orb.getRelativePositionAtUT(UT); var h = Vector3d.Cross(r1, destination); if(h.sqrMagnitude < 0.01) h = orb.GetOrbitNormal(); c = destination-r1; cm = c.magnitude; var r1m = r1.magnitude; var r2m = destination.magnitude; var rrm = r1m+r2m; m = rrm+cm; n = rrm-cm; m3 = m*m*m; var transfer_angle = Vector3d.Angle(r1, destination)*Mathf.Deg2Rad; if(h.z < 0) transfer_angle = Utils.TwoPI-transfer_angle; sigma = Math.Sqrt(n/m); if(transfer_angle > Math.PI) sigma = -sigma; sigma2 = sigma*sigma; sigma3 = sigma2*sigma; sigma5 = sigma2*sigma3; tauP = 2/3.0*(1-sigma3); tauME = Math.Acos(sigma)+sigma*Math.Sqrt(1-sigma2); }
public override ManeuverParameters MakeNodeImpl(Orbit o, double universalTime, MechJebModuleTargetController target) { double UT = timeSelector.ComputeManeuverTime(o, universalTime, target); var dV = OrbitalManeuverCalculator.DeltaVToShiftNodeLongitude(o, UT, target.targetLongitude); return new ManeuverParameters(dV, UT); }
//Computes the time until the phase angle between the launchpad and the target equals the given angle. //The convention used is that phase angle is the angle measured starting at the target and going east until //you get to the launchpad. //The time returned will not be exactly accurate unless the target is in an exactly circular orbit. However, //the time returned will go to exactly zero when the desired phase angle is reached. public static double TimeToPhaseAngle(double phaseAngle, CelestialBody launchBody, double launchLongitude, Orbit target) { double launchpadAngularRate = 360 / launchBody.rotationPeriod; double targetAngularRate = 360.0 / target.period; if (Vector3d.Dot(target.SwappedOrbitNormal(), launchBody.angularVelocity) < 0) targetAngularRate *= -1; //retrograde target Vector3d currentLaunchpadDirection = launchBody.GetSurfaceNVector(0, launchLongitude); Vector3d currentTargetDirection = target.SwappedRelativePositionAtUT(Planetarium.GetUniversalTime()); currentTargetDirection = Vector3d.Exclude(launchBody.angularVelocity, currentTargetDirection); double currentPhaseAngle = Math.Abs(Vector3d.Angle(currentLaunchpadDirection, currentTargetDirection)); if (Vector3d.Dot(Vector3d.Cross(currentTargetDirection, currentLaunchpadDirection), launchBody.angularVelocity) < 0) { currentPhaseAngle = 360 - currentPhaseAngle; } double phaseAngleRate = launchpadAngularRate - targetAngularRate; double phaseAngleDifference = MuUtils.ClampDegrees360(phaseAngle - currentPhaseAngle); if (phaseAngleRate < 0) { phaseAngleRate *= -1; phaseAngleDifference = 360 - phaseAngleDifference; } return phaseAngleDifference / phaseAngleRate; }
public OrbitInfo(Orbitable orb, SharedObjects sharedObj) : this() { orbit = orb.Orbit; Shared = sharedObj; name = orb.GetName(); }
public override ManeuverParameters MakeNodeImpl(Orbit o, double UT, MechJebModuleTargetController target) { if (!target.NormalTargetExists) { throw new OperationException("must select a target for the Hohmann transfer."); } else if (o.referenceBody != target.TargetOrbit.referenceBody) { throw new OperationException("target for Hohmann transfer must be in the same sphere of influence."); } else if (o.eccentricity > 1) { throw new OperationException("starting orbit for Hohmann transfer must not be hyperbolic."); } else if (target.TargetOrbit.eccentricity > 1) { throw new OperationException("target orbit for Hohmann transfer must not be hyperbolic."); } else if (o.RelativeInclination(target.TargetOrbit) > 30 && o.RelativeInclination(target.TargetOrbit) < 150) { errorMessage = "Warning: target's orbital plane is at a " + o.RelativeInclination(target.TargetOrbit).ToString("F0") + "º angle to starting orbit's plane (recommend at most 30º). Planned transfer may not intercept target properly."; } else if (o.eccentricity > 0.2) { errorMessage = "Warning: Recommend starting Hohmann transfers from a near-circular orbit (eccentricity < 0.2). Planned transfer is starting from an orbit with eccentricity " + o.eccentricity.ToString("F2") + " and so may not intercept target properly."; } Vector3d dV = OrbitalManeuverCalculator.DeltaVAndTimeForHohmannTransfer(o, target.TargetOrbit, UT, out UT); return new ManeuverParameters(dV, UT); }
public OrbitInfo( Orbit orb, SharedObjects sharedObj) : this() { Shared = sharedObj; orbit = orb; name = "<unnamed>"; }
public ReentrySimulation(Orbit _initialOrbit, double _UT, SimulatedVessel _vessel, SimCurves _simcurves, IDescentSpeedPolicy _descentSpeedPolicy, double _decelEndAltitudeASL, double _maxThrustAccel, double _parachuteSemiDeployMultiplier, double _probableLandingSiteASL, bool _multiplierHasError, double _dt, double _min_dt) { // Store all the input values as they were given input_initialOrbit = _initialOrbit; input_UT = _UT; vessel = _vessel; input_descentSpeedPolicy = _descentSpeedPolicy; input_decelEndAltitudeASL = _decelEndAltitudeASL; input_maxThrustAccel = _maxThrustAccel; input_parachuteSemiDeployMultiplier = _parachuteSemiDeployMultiplier; input_probableLandingSiteASL = _probableLandingSiteASL; input_multiplierHasError = _multiplierHasError; input_dt = _dt; // the vessel attitude relative to the surface vel. Fixed for now attitude = Quaternion.Euler(180,0,0); min_dt = _min_dt; max_dt = _dt; dt = max_dt; // Get a copy of the original orbit, to be more thread safe initialOrbit = new Orbit(); initialOrbit.UpdateFromOrbitAtUT(_initialOrbit, _UT, _initialOrbit.referenceBody); CelestialBody body = _initialOrbit.referenceBody; bodyHasAtmosphere = body.atmosphere; bodyRadius = body.Radius; gravParameter = body.gravParameter; this.parachuteSemiDeployMultiplier = _parachuteSemiDeployMultiplier; this.multiplierHasError = _multiplierHasError; bodyAngularVelocity = body.angularVelocity; this.descentSpeedPolicy = _descentSpeedPolicy; decelRadius = bodyRadius + _decelEndAltitudeASL; aerobrakedRadius = bodyRadius + body.RealMaxAtmosphereAltitude(); mainBody = body; this.maxThrustAccel = _maxThrustAccel; this.probableLandingSiteASL = _probableLandingSiteASL; this.probableLandingSiteRadius = _probableLandingSiteASL + bodyRadius; referenceFrame = ReferenceFrame.CreateAtCurrentTime(_initialOrbit.referenceBody); orbitReenters = OrbitReenters(_initialOrbit); if (orbitReenters) startUT = _UT; maxDragGees = 0; deltaVExpended = 0; trajectory = new List<AbsoluteVector>(); simCurves = _simcurves; once = true; }
// Use this for initialization void Start() { cameraScript = cameraObject.GetComponent<CameraMovement>(); kmCameraScript = kmCameraObject.GetComponent<CameraMovement>(); orbitScript = rotateObject.GetComponent<Orbit>(); timerText.text = timeLeft.ToString("0.00"); timerScreen.SetActive(false); }
public void CircularOrbitAtZeroHasZeroAnglesAndMeanDistance() { Orbit orbit = new Orbit { Period = 1, MeanDistance = 1, Eccentricity = 0, Inclination = 0, PeriapsisArgument = 0 }; var position = OrbitMechanics.CalculatePosition(0, orbit); Assert.AreEqual(0, position.Polar); Assert.AreEqual(0, position.Elevation); Assert.AreEqual(1, position.Radius); }
public void CircularOrbitAtThreeQyartersYearHas270PolarAngleAndMeanDistance() { Orbit orbit = new Orbit { Period = 1, MeanDistance = 1, Eccentricity = 0, Inclination = 0, PeriapsisArgument = 0 }; var position = OrbitMechanics.CalculatePosition(0.75f, orbit); Assert.AreEqual(Math.PI + Math.PI / 2, position.Polar, 0.00001); Assert.AreEqual(0, position.Elevation); Assert.AreEqual(1, position.Radius); }
public Orbit stockPatch { get; set; } // tells wether the patch starting from this state is superimposed on a stock KSP patch, or null if something makes it diverge (atmospheric entry for example) public VesselState(Vessel vessel) { referenceBody = vessel.orbit.referenceBody; time = Planetarium.GetUniversalTime(); position = vessel.GetWorldPos3D() - referenceBody.position; velocity = vessel.obt_velocity; stockPatch = vessel.orbit; }
public void CircularSector() { var o = new Orbit {SemiMajorAxis = 1, SemiMinorAxis = 1, TransformAngle = 0}; AssertAreClose(o.S(1, 0), Math.PI / 2); AssertAreClose(o.S(1, Math.PI), 0); AssertAreClose(o.S(1, -Math.PI/2), 3*Math.PI/4); AssertAreClose(o.S(1, -Math.PI), Math.PI); }
public override void DoParametersGUI(Orbit o, double universalTime, MechJebModuleTargetController target) { if (target.Target is CelestialBody) GuiUtils.SimpleTextBox("Approximate final periapsis", courseCorrectFinalPeA, "km"); else GuiUtils.SimpleTextBox("Closest approach distance", interceptDistance, "m"); GUILayout.Label("Schedule the burn to minimize the required ΔV."); }
public override void UpdateOrbit(Orbit current) { base.UpdateOrbit(current); update(false, false); }
public void UpdateOrbit(Orbit current, bool with_brake, bool brake_at_fly_above) { base.UpdateOrbit(current); update(with_brake, brake_at_fly_above); }
public LandingPath(VesselWrapper vsl, Orbit orb, double target_altitude, double startUT, double dt, double start_mass, double fuel = 0, double brake_vel = 0) { VSL = vsl; Orbit = orb; TargetAltitude = target_altitude; StartUT = startUT; UT0 = VSL.Physics.UT; if (Orbit.referenceBody.atmosphere || brake_vel > 0 && fuel > 0) { EndUT = startUT + orb.timeToPe; HavePoints = brake_vel > 0 && fuel > 0; var p = newP(StartUT); var m = start_mass; var s = Math.Max(VSL.Geometry.MinArea, VSL.Geometry.AreaWithBrakes); var ascending = p.Ascending; p.Duration = dt; while ((ascending || p.Altitude > TargetAltitude) && p.UT < EndUT) { if (!ascending && dt > 0.01) { var dAlt = p.Altitude - TargetAltitude; if (dAlt / p.SrfSpeed < dt) { dt = dAlt / p.SrfSpeed * 0.9; p.Duration = dt; } } var drag_dv = p.SpecificDrag * s / m * dt; var prev_alt = p.Altitude; Atmosphere |= drag_dv > 0; HavePoints |= Atmosphere; if (HavePoints) { if (Points.Count == 0) { FirstPointUT = p.UT; if (Atmosphere) { AtmoStartUT = p.UT; } } Points.Add(p); // VSL.Log("drag dV {}, m {}, fm {}, brake_vel {}, p {}", // drag_dv, m, fuel, brake_vel, p);//debug var r = p.pos.magnitude; if (brake_vel > 0 && fuel > 0) { //compute thrust direction var vV = Utils.ClampL(Vector3d.Dot(p.vel, p.pos / r), 1e-5); var b_dir = LandingTrajectoryAutopilot.CorrectedBrakeVelocity(VSL, p.vel, p.pos, p.DynamicPressure / 1000 / LandingTrajectoryAutopilot.C.MinDPressure, (p.Altitude - TargetAltitude) / vV).normalized; //compute thrust and mass flow float mflow; var thrust = VSL.Engines.ThrustAtAlt((float)p.SrfSpeed, (float)p.Altitude, out mflow); var Ve = thrust / mflow; var dm = mflow * dt; if (dm > fuel) { dm = fuel; } var bV = (double)VSL.Engines.DeltaV(Ve, (float)dm); if (bV > brake_vel) { bV = brake_vel; dm = EnginesProps.FuelNeeded((float)bV, Ve, (float)m); } p.vel -= b_dir * bV; brake_vel -= bV; // VSL.Log("vV {}, vB {}, thrust {}, mflow {}, Ve {}, dm {}\nb_dir {}\nvel {}\npos {}", // vV, bV, thrust, mflow, Ve, dm, b_dir, p.vel, p.pos);//debug //spend fuel fuel -= dm; m -= dm; } if (Atmosphere) { p.vel -= p.srf_vel / p.SrfSpeed * Math.Min(drag_dv, p.SrfSpeed); } p.vel -= p.pos * p.Body.gMagnitudeAtCenter / r / r / r * dt; p.pos += p.vel * dt; p.UT += dt; p.Update(); if (Atmosphere && AtmoStopUT < 0 && p.SrfSpeed < 10) { AtmoStopUT = p.UT; } } else { p.Update(p.UT + dt); } ascending &= p.Altitude > prev_alt; } if (HavePoints) { Points.Add(p); if (Atmosphere && AtmoStopUT < 0) { AtmoStopUT = p.UT; } } EndUT = p.UT; LastPoint = p; } else { EndUT = TrajectoryCalculator.NearestRadiusUT(Orbit, Orbit.referenceBody.Radius + TargetAltitude, StartUT); LastPoint = newP(EndUT); } }
/// Dublicate an orbit public static Orbit Clone(this Orbit orbit0) { return(new Orbit(orbit0.inclination, orbit0.eccentricity, orbit0.semiMajorAxis, orbit0.LAN, orbit0.argumentOfPeriapsis, orbit0.meanAnomalyAtEpoch, orbit0.epoch, orbit0.referenceBody)); }
public override void UpdateOrbit(Orbit current) { base.UpdateOrbit(current); TransferTime = -1; update(); }
internal static double timeOfClosestApproach(Orbit oOrig, Orbit oTgt, double timeStart, out double closestdistance) { return(timeOfClosestApproach(oOrig, oTgt, timeStart, oOrig.period, 20, out closestdistance)); }
internal static double timeOfTargetAltitude(Orbit oOrig, double timeStart, out double closestdistance, double targetDistance) { //return timeOfClosestApproach(a, b, time + ((orbit - 1) * a.period), (orbit * a.period), 20, out closestdistance); return(timeOfTargetAltitude(oOrig, timeStart, 20, out closestdistance, targetDistance)); }
public Vector3d GetOrbitVelocityAtSurface() { return(Orbit.getOrbitalVelocityAtUT(AtTargetUT)); }
protected override void Collide(Collision2D other) { Rigidbody2D rb = other.gameObject.GetComponent <Rigidbody2D>(); if (rb) { if (frozenObjects.Contains(rb)) { rb.isKinematic = false; rb.velocity = Vector2.zero; rb.angularVelocity = 0; rb.constraints = RigidbodyConstraints2D.None; frozenObjects.Remove(rb); } else { //scaled.TimeScale = 0; rb.isKinematic = true; rb.velocity = Vector2.zero; rb.angularVelocity = 0; rb.constraints = RigidbodyConstraints2D.FreezeAll; frozenObjects.Add(rb); GameObject.FindWithTag("Player").GetComponentInChildren <TimeGun>().StartUnFreeze(rb, freezeTime); } return; } Movingplatform platform = other.gameObject.GetComponent <Movingplatform>(); if (platform) { FrozenPlatform frozen = frozenPlatforms.FirstOrDefault(p => p.platform == platform); if (frozen.platform != null) { frozen.platform.TimeScale = frozen.unfrozenTimeScale; frozenPlatforms.Remove(frozen); } else { frozenPlatforms.Add(new FrozenPlatform { platform = platform, unfrozenTimeScale = platform.TimeScale }); platform.TimeScale = 0; GameObject.FindWithTag("Player").GetComponentInChildren <TimeGun>().StartUnFreeze(platform, freezeTime); } return; } Orbit o = other.gameObject.GetComponent <Orbit>(); if (o) { if (frozenOrbits.Contains(o)) { o.enabled = true; frozenOrbits.Remove(o); } else { frozenOrbits.Add(o); o.enabled = false; GameObject.FindWithTag("Player").GetComponentInChildren <TimeGun>().StartUnFreeze(o, freezeTime); } } }
public Planet(GameObject prefab, Orbit orbit) { this.prefab = prefab; this.orbit = orbit; }
private static Vector3d CalculateDragVector(Vessel vessel, VesselData vesselData, Vector3d orbitalVector, Orbit vesselOrbit) { var normalizedOrbitalVector = orbitalVector.normalized; var cosOrbitRaw = Vector3d.Dot(vessel.transform.up, normalizedOrbitalVector); var cosOrbitalDrag = Math.Abs(cosOrbitRaw); var squaredCosOrbitalDrag = cosOrbitalDrag * cosOrbitalDrag; var atmosphericGasKgPerSquareMeter = AtmosphericFloatCurves.GetAtmosphericGasDensityKgPerCubicMeter(vesselOrbit.referenceBody, vessel.altitude); var effectiveSpeedForDrag = Math.Max(0, orbitalVector.magnitude); var dragForcePerSquareMeter = atmosphericGasKgPerSquareMeter * 0.5 * effectiveSpeedForDrag * effectiveSpeedForDrag; var effectiveSurfaceArea = cosOrbitalDrag * vesselData.ModulePhotonSail.surfaceArea; // calculate normal Vector3d vesselNormal = vessel.transform.up; if (cosOrbitRaw < 0) { vesselNormal = -vesselNormal; } // calculate specular drag var specularDragCoefficient = squaredCosOrbitalDrag + 3 * squaredCosOrbitalDrag; var specularDragPerSquareMeter = specularDragCoefficient * dragForcePerSquareMeter * 0.1; var specularDragInNewton = specularDragPerSquareMeter * effectiveSurfaceArea; Vector3d specularDragForce = specularDragInNewton * vesselNormal; // calculate Diffuse Drag var diffuseDragCoefficient = 2 + squaredCosOrbitalDrag * 1.3; var diffuseDragPerSquareMeter = diffuseDragCoefficient * dragForcePerSquareMeter * 0.9; var diffuseDragInNewton = diffuseDragPerSquareMeter * effectiveSurfaceArea; Vector3d diffuseDragForceVector = diffuseDragInNewton * normalizedOrbitalVector * -1; Vector3d combinedDragDecelerationVector = vesselData.TotalVesselMassInKg > 0 ? (specularDragForce + diffuseDragForceVector) / vesselData.TotalVesselMassInKg : Vector3d.zero; return(combinedDragDecelerationVector); }
private static void SetOrbit(OrbitDriver currentlyEditing, Orbit orbit) { Log.Info("Setorbit"); currentlyEditing.DynamicSetOrbit(orbit); }
public static Vector3d Node2OrbitalDeltaV(ManeuverNode node, Orbit o = null) => Node2OrbitalDeltaV(o ?? node.patch, node.DeltaV, node.UT);
private void checkOrbit() { if (_manager.SelectedManeuverNode == null || _snapTab == null) { return; } if (_patch == null) { SetFields(); } double UT = Planetarium.GetUniversalTime(); //_snapTab.CurrentTime.OnTextUpdate.Invoke(string.Format("Maneuver Node #{0}: T {1}", _index + 1, KSPUtil.PrintTime(UT - _manager.SelectedManeuverNode.UT, 3, true))); _snapTab.ResetTime.OnTextUpdate.Invoke(string.Format("{0}", KSPUtil.PrintTime(_startUT - UT, 3, false))); if (_patch.eccentricity >= 1) { if (_manager.SelectedManeuverNode.attachedGizmo != null) { _manager.SelectedManeuverNode.attachedGizmo.orbitsAdded = 0; } _orbitsAdded = 0; _snapTab.NextOrbitButton.gameObject.SetActive(false); _snapTab.PreviousOrbitButton.gameObject.SetActive(false); _snapTab.ApoButton.gameObject.SetActive(false); if (_patch.timeToPe < 0) { _snapTab.PeriButton.gameObject.SetActive(false); } else if (_patch.PeR < 0) { _snapTab.PeriButton.gameObject.SetActive(false); } else if (_patch.UTsoi > 0 && _patch.timeToPe + _patch.StartUT > _patch.UTsoi) { _snapTab.PeriButton.gameObject.SetActive(false); } else { _snapTab.PeriButton.gameObject.SetActive(true); periUT = _patch.StartUT + _patch.timeToPe; } if ((_patch.patchEndTransition == Orbit.PatchTransitionType.ENCOUNTER || _patch.patchEndTransition == Orbit.PatchTransitionType.ESCAPE || _patch.patchEndTransition == Orbit.PatchTransitionType.MANEUVER) && (_patch.nextPatch.patchEndTransition == Orbit.PatchTransitionType.ENCOUNTER || _patch.nextPatch.patchEndTransition == Orbit.PatchTransitionType.ESCAPE)) { _snapTab.NextPatchButton.gameObject.SetActive(true); if (_patch.nextPatch.nextPatch.UTsoi > 0) { nextPUT = _patch.nextPatch.nextPatch.StartUT + ((_patch.nextPatch.nextPatch.UTsoi - _patch.nextPatch.nextPatch.StartUT) / 2); } else if (_patch.nextPatch.nextPatch.eccentricity < 1 && !double.IsNaN(_patch.nextPatch.nextPatch.period) && !double.IsInfinity(_patch.nextPatch.nextPatch.period)) { nextPUT = _patch.nextPatch.nextPatch.StartUT + (_patch.nextPatch.nextPatch.period / 2); } else { nextPUT = _patch.nextPatch.nextPatch.StartUT + ((_patch.nextPatch.nextPatch.EndUT - _patch.nextPatch.nextPatch.StartUT) / 2); } } else { _snapTab.NextPatchButton.gameObject.SetActive(false); } if (_patch.patchStartTransition == Orbit.PatchTransitionType.INITIAL || _patch.patchStartTransition == Orbit.PatchTransitionType.MANEUVER) { _snapTab.PreviousPatchButton.gameObject.SetActive(false); } else { _snapTab.PreviousPatchButton.gameObject.SetActive(true); if (_patch.previousPatch.UTsoi > 0) { prevPUT = _patch.previousPatch.StartUT + ((_patch.previousPatch.UTsoi - _patch.previousPatch.StartUT) / 2); } else if (_patch.previousPatch.eccentricity < 1 && !double.IsNaN(_patch.previousPatch.period) && !double.IsInfinity(_patch.previousPatch.period)) { prevPUT = _patch.previousPatch.StartUT + (_patch.previousPatch.period / 2); } else { prevPUT = _patch.previousPatch.StartUT + ((_patch.previousPatch.EndUT - _patch.previousPatch.StartUT) / 2); } } eqAscUT = ManeuverUtilities.EqAscTime(_patch); if (_patch.UTsoi > 0 && eqAscUT > _patch.UTsoi) { _snapTab.EqAscButton.gameObject.SetActive(false); } else if (eqAscUT < UT) { _snapTab.EqAscButton.gameObject.SetActive(false); } else { _snapTab.EqAscButton.gameObject.SetActive(true); } eqDescUT = ManeuverUtilities.EqDescTime(_patch); if (_patch.UTsoi > 0 && eqDescUT > _patch.UTsoi) { _snapTab.EqDescButton.gameObject.SetActive(false); } else if (eqDescUT < UT) { _snapTab.EqDescButton.gameObject.SetActive(false); } else { _snapTab.EqDescButton.gameObject.SetActive(true); } ITargetable target = FlightGlobals.fetch.VesselTarget; if (target == null) { _snapTab.RelAscButton.gameObject.SetActive(false); _snapTab.RelDescButton.gameObject.SetActive(false); _snapTab.ClAppButton.gameObject.SetActive(false); } else { Orbit tgtPatch = target.GetOrbit(); if (tgtPatch.referenceBody != _patch.referenceBody) { _snapTab.RelAscButton.gameObject.SetActive(false); _snapTab.RelDescButton.gameObject.SetActive(false); _snapTab.ClAppButton.gameObject.SetActive(false); } else { relAscUT = ManeuverUtilities.RelAscTime(_patch, target); if (_patch.UTsoi > 0 && relAscUT > _patch.UTsoi) { _snapTab.RelAscButton.gameObject.SetActive(false); } else if (relAscUT < UT) { _snapTab.RelAscButton.gameObject.SetActive(false); } else { _snapTab.RelAscButton.gameObject.SetActive(true); } relDescUT = ManeuverUtilities.RelDescTime(_patch, target); if (_patch.UTsoi > 0 && relDescUT > _patch.UTsoi) { _snapTab.RelDescButton.gameObject.SetActive(false); } else if (relDescUT < UT) { _snapTab.RelDescButton.gameObject.SetActive(false); } else { _snapTab.RelDescButton.gameObject.SetActive(true); } if (target.GetVessel() == null) { clAppUT = _patch.closestTgtApprUT; } else { clAppUT = ManeuverUtilities.closestVessel(0, _patch, tgtPatch, true, 0, 0); } if (clAppUT <= 0) { _snapTab.ClAppButton.gameObject.SetActive(false); } else if (_patch.UTsoi > 0 && clAppUT > _patch.UTsoi) { _snapTab.ClAppButton.gameObject.SetActive(false); } else if (clAppUT < UT) { _snapTab.ClAppButton.gameObject.SetActive(false); } else { _snapTab.ClAppButton.gameObject.SetActive(true); } } } } else { if (_patch.patchEndTransition == Orbit.PatchTransitionType.FINAL) { if (!double.IsNaN(_patch.period) && !double.IsInfinity(_patch.period)) { _snapTab.NextOrbitButton.gameObject.SetActive(true); nextOUT = _manager.SelectedManeuverNode.UT + _patch.period; } else { _snapTab.NextOrbitButton.gameObject.SetActive(false); } if (double.IsNaN(_patch.period) || double.IsInfinity(_patch.period) || _manager.SelectedManeuverNode.UT - _patch.period < UT) { _snapTab.PreviousOrbitButton.gameObject.SetActive(false); } else { _snapTab.PreviousOrbitButton.gameObject.SetActive(true); prevOUT = _manager.SelectedManeuverNode.UT - _patch.period; } _snapTab.ApoButton.gameObject.SetActive(true); apoUT = _patch.StartUT + _patch.timeToAp; _snapTab.PeriButton.gameObject.SetActive(true); periUT = _patch.StartUT + _patch.timeToPe; _snapTab.NextPatchButton.gameObject.SetActive(false); if (_patch.patchStartTransition == Orbit.PatchTransitionType.INITIAL || _patch.patchStartTransition == Orbit.PatchTransitionType.MANEUVER) { _snapTab.PreviousPatchButton.gameObject.SetActive(false); } else { _snapTab.PreviousPatchButton.gameObject.SetActive(true); if (_patch.previousPatch.UTsoi > 0) { prevPUT = _patch.previousPatch.StartUT + ((_patch.previousPatch.UTsoi - _patch.previousPatch.StartUT) / 2); } else if (_patch.previousPatch.eccentricity < 1 && !double.IsNaN(_patch.previousPatch.period) && !double.IsInfinity(_patch.previousPatch.period)) { prevPUT = _patch.previousPatch.StartUT + (_patch.previousPatch.period / 2); } else { prevPUT = _patch.previousPatch.StartUT + ((_patch.previousPatch.EndUT - _patch.previousPatch.StartUT) / 2); } } _snapTab.EqAscButton.gameObject.SetActive(true); eqAscUT = ManeuverUtilities.EqAscTime(_patch); _snapTab.EqDescButton.gameObject.SetActive(true); eqDescUT = ManeuverUtilities.EqDescTime(_patch); ITargetable target = FlightGlobals.fetch.VesselTarget; if (target == null) { _snapTab.RelAscButton.gameObject.SetActive(false); _snapTab.RelDescButton.gameObject.SetActive(false); _snapTab.ClAppButton.gameObject.SetActive(false); } else { Orbit tgtPatch = target.GetOrbit(); if (tgtPatch.referenceBody != _patch.referenceBody) { _snapTab.RelAscButton.gameObject.SetActive(false); _snapTab.RelDescButton.gameObject.SetActive(false); _snapTab.ClAppButton.gameObject.SetActive(false); } else { _snapTab.RelAscButton.gameObject.SetActive(true); relAscUT = ManeuverUtilities.RelAscTime(_patch, target); _snapTab.RelDescButton.gameObject.SetActive(true); relDescUT = ManeuverUtilities.RelDescTime(_patch, target); if (target.GetVessel() == null) { clAppUT = _patch.closestTgtApprUT; } else { clAppUT = ManeuverUtilities.closestVessel(0, _patch, tgtPatch, true, 0, 0); } if (clAppUT <= 0) { _snapTab.ClAppButton.gameObject.SetActive(false); } else { _snapTab.ClAppButton.gameObject.SetActive(true); } } } } else { if (_manager.SelectedManeuverNode.attachedGizmo != null) { _manager.SelectedManeuverNode.attachedGizmo.orbitsAdded = 0; } _orbitsAdded = 0; _snapTab.NextOrbitButton.gameObject.SetActive(false); _snapTab.PreviousOrbitButton.gameObject.SetActive(false); if (_patch.timeToAp < 0) { _snapTab.ApoButton.gameObject.SetActive(false); } if (_patch.UTsoi > 0 && _patch.timeToAp + _patch.StartUT > _patch.UTsoi) { _snapTab.ApoButton.gameObject.SetActive(false); } if (_patch.ApA > _patch.referenceBody.sphereOfInfluence) { _snapTab.ApoButton.gameObject.SetActive(false); } else { _snapTab.ApoButton.gameObject.SetActive(true); apoUT = _patch.StartUT + _patch.timeToAp; } if (_patch.timeToPe < 0) { _snapTab.PeriButton.gameObject.SetActive(false); } else if (_patch.PeR < 0) { _snapTab.PeriButton.gameObject.SetActive(false); } else if (_patch.UTsoi > 0 && _patch.timeToPe + _patch.StartUT > _patch.UTsoi) { _snapTab.PeriButton.gameObject.SetActive(false); } else { _snapTab.PeriButton.gameObject.SetActive(true); periUT = _patch.StartUT + _patch.timeToPe; } if ((_patch.patchEndTransition == Orbit.PatchTransitionType.ENCOUNTER || _patch.patchEndTransition == Orbit.PatchTransitionType.ESCAPE || _patch.patchEndTransition == Orbit.PatchTransitionType.MANEUVER) && (_patch.nextPatch.patchEndTransition == Orbit.PatchTransitionType.ENCOUNTER || _patch.nextPatch.patchEndTransition == Orbit.PatchTransitionType.ESCAPE)) { _snapTab.NextPatchButton.gameObject.SetActive(true); if (_patch.nextPatch.nextPatch.UTsoi > 0) { nextPUT = _patch.nextPatch.nextPatch.StartUT + ((_patch.nextPatch.nextPatch.UTsoi - _patch.nextPatch.nextPatch.StartUT) / 2); } else if (_patch.nextPatch.nextPatch.eccentricity < 1 && !double.IsNaN(_patch.nextPatch.nextPatch.period) && !double.IsInfinity(_patch.nextPatch.nextPatch.period)) { nextPUT = _patch.nextPatch.nextPatch.StartUT + (_patch.nextPatch.nextPatch.period / 2); } else { nextPUT = _patch.nextPatch.nextPatch.StartUT + ((_patch.nextPatch.nextPatch.EndUT - _patch.nextPatch.nextPatch.StartUT) / 2); } } else { _snapTab.NextPatchButton.gameObject.SetActive(false); } if (_patch.patchStartTransition == Orbit.PatchTransitionType.INITIAL || _patch.patchStartTransition == Orbit.PatchTransitionType.MANEUVER) { _snapTab.PreviousPatchButton.gameObject.SetActive(false); } else { _snapTab.PreviousPatchButton.gameObject.SetActive(true); if (_patch.previousPatch.UTsoi > 0) { prevPUT = _patch.previousPatch.StartUT + ((_patch.previousPatch.UTsoi - _patch.previousPatch.StartUT) / 2); } else if (_patch.previousPatch.eccentricity < 1 && !double.IsNaN(_patch.previousPatch.period) && !double.IsInfinity(_patch.previousPatch.period)) { prevPUT = _patch.previousPatch.StartUT + (_patch.previousPatch.period / 2); } else { prevPUT = _patch.previousPatch.StartUT + ((_patch.previousPatch.EndUT - _patch.previousPatch.StartUT) / 2); } } eqAscUT = ManeuverUtilities.EqAscTime(_patch); if (_patch.UTsoi > 0 && eqAscUT > _patch.UTsoi) { _snapTab.EqAscButton.gameObject.SetActive(false); } else if (eqAscUT < UT) { _snapTab.EqAscButton.gameObject.SetActive(false); } else { _snapTab.EqAscButton.gameObject.SetActive(true); } eqDescUT = ManeuverUtilities.EqDescTime(_patch); if (_patch.UTsoi > 0 && eqDescUT > _patch.UTsoi) { _snapTab.EqDescButton.gameObject.SetActive(false); } else if (eqDescUT < UT) { _snapTab.EqDescButton.gameObject.SetActive(false); } else { _snapTab.EqDescButton.gameObject.SetActive(true); } ITargetable target = FlightGlobals.fetch.VesselTarget; if (target == null) { _snapTab.RelAscButton.gameObject.SetActive(false); _snapTab.RelDescButton.gameObject.SetActive(false); _snapTab.ClAppButton.gameObject.SetActive(false); } else { Orbit tgtPatch = target.GetOrbit(); if (tgtPatch.referenceBody != _patch.referenceBody) { _snapTab.RelAscButton.gameObject.SetActive(false); _snapTab.RelDescButton.gameObject.SetActive(false); _snapTab.ClAppButton.gameObject.SetActive(false); } else { relAscUT = ManeuverUtilities.RelAscTime(_patch, target); if (_patch.UTsoi > 0 && relAscUT > _patch.UTsoi) { _snapTab.RelAscButton.gameObject.SetActive(false); } else if (relAscUT < UT) { _snapTab.RelAscButton.gameObject.SetActive(false); } else { _snapTab.RelAscButton.gameObject.SetActive(true); } relDescUT = ManeuverUtilities.RelDescTime(_patch, target); if (_patch.UTsoi > 0 && relDescUT > _patch.UTsoi) { _snapTab.RelDescButton.gameObject.SetActive(false); } else if (relDescUT < UT) { _snapTab.RelDescButton.gameObject.SetActive(false); } else { _snapTab.RelDescButton.gameObject.SetActive(true); } if (target.GetVessel() == null) { clAppUT = _patch.closestTgtApprUT; } else { clAppUT = ManeuverUtilities.closestVessel(0, _patch, tgtPatch, true, 0, 0); } if (clAppUT <= 0) { _snapTab.ClAppButton.gameObject.SetActive(false); } else if (_patch.UTsoi > 0 && clAppUT > _patch.UTsoi) { _snapTab.ClAppButton.gameObject.SetActive(false); } else if (clAppUT < UT) { _snapTab.ClAppButton.gameObject.SetActive(false); } else { _snapTab.ClAppButton.gameObject.SetActive(true); } } } } } }
protected void MaintainAerobrakeNode() { if (makeAerobrakeNodes) { //Remove node after finishing aerobraking: if (aerobrakeNode != null && vessel.patchedConicSolver.maneuverNodes.Contains(aerobrakeNode)) { if (aerobrakeNode.UT < vesselState.time && vesselState.altitudeASL > mainBody.RealMaxAtmosphereAltitude()) { vessel.patchedConicSolver.RemoveManeuverNode(aerobrakeNode); aerobrakeNode = null; } } //Update or create node if necessary: ReentrySimulation.Result r = GetResult(); if (r != null && r.outcome == ReentrySimulation.Outcome.AEROBRAKED) { //Compute the node dV: Orbit preAerobrakeOrbit = GetReenteringPatch(); //Put the node at periapsis, unless we're past periapsis. In that case put the node at the current time. double UT; if (preAerobrakeOrbit == orbit && vesselState.altitudeASL < mainBody.RealMaxAtmosphereAltitude() && vesselState.speedVertical > 0) { UT = vesselState.time; } else { UT = preAerobrakeOrbit.NextPeriapsisTime(preAerobrakeOrbit.StartUT); } Orbit postAerobrakeOrbit = MuUtils.OrbitFromStateVectors(r.WorldEndPosition(), r.WorldEndVelocity(), r.body, r.endUT); Vector3d dV = OrbitalManeuverCalculator.DeltaVToChangeApoapsis(preAerobrakeOrbit, UT, postAerobrakeOrbit.ApR); if (aerobrakeNode != null && vessel.patchedConicSolver.maneuverNodes.Contains(aerobrakeNode)) { //update the existing node Vector3d nodeDV = preAerobrakeOrbit.DeltaVToManeuverNodeCoordinates(UT, dV); aerobrakeNode.OnGizmoUpdated(nodeDV, UT); } else { //place a new node aerobrakeNode = vessel.PlaceManeuverNode(preAerobrakeOrbit, dV, UT); } } else { //no aerobraking, remove the node: if (aerobrakeNode != null && vessel.patchedConicSolver.maneuverNodes.Contains(aerobrakeNode)) { vessel.patchedConicSolver.RemoveManeuverNode(aerobrakeNode); } } } else { //Remove aerobrake node when it is turned off: if (aerobrakeNode != null && vessel.patchedConicSolver.maneuverNodes.Contains(aerobrakeNode)) { vessel.patchedConicSolver.RemoveManeuverNode(aerobrakeNode); } } }
internal static double timeOfTargetDistance(Orbit oOrig, Orbit oTgt, double timeStart, int orbit, out double closestdistance, double targetDistance) { //return timeOfClosestApproach(a, b, time + ((orbit - 1) * a.period), (orbit * a.period), 20, out closestdistance); return(timeOfTargetDistance(oOrig, oTgt, timeStart + ((orbit - 1) * oOrig.period), oOrig.period, 20, out closestdistance, targetDistance)); }
void update(bool with_brake, bool brake_at_fly_above) { reset(); //update everything update_from_orbit(); update_landing_steepness(); ActualLandingAngle = 90 - Utils.Angle2(AtTargetPos, -AtTargetVel); var AerobrakeStartUT = Path.Atmosphere ? Path.AtmoStartUT : AtTargetUT - 1; //correct for brake maneuver if (with_brake) { //estimate time needed to rotate the ship downwards var rotation_time = VSL.Torque.NoEngines ? VSL.Torque.NoEngines.RotationTime2Phase(90) : VSL.Torque.MaxPossible.RotationTime2Phase(90, 0.1f); //estimate amount of fuel needed for the maneuver var vertical_vel = Vector3d.Project(AtTargetVel, AtTargetPos); SetBrakeEndUT(Math.Max(AtTargetUT - LandingTrajectoryAutopilot.C.CorrectionOffset + rotation_time, StartUT)); SetBrakeDeltaV(vertical_vel); if (BrakeFuel > 0) { //calculate braking maneuver BrakeDuration = VSL.Engines.OnPlanetTTB(BrakeDeltaV, Orbit.getRelativePositionAtUT(BrakeEndPoint.UT), (float)(BrakeEndPointDeltaAlt + TargetAltitude)); BrakeDuration += rotation_time; if (FullBrake) { if (brake_at_fly_above) { FlyAbovePoint = Path.FlyAbovePoint(Target.OrbPos(Body)); if (WillOverheat) { SetBrakeEndPoint(Path.PointAtShipTemp(VSL.Physics.MinMaxTemperature - 100)); } else { SetBrakeEndPoint(FlyAbovePoint); } } else { //find appropriate point to perform the maneuver var brake_end_UT = Math.Max(AtTargetUT - Mathf.Max(LandingTrajectoryAutopilot.C.CorrectionOffset, BrakeDuration * 1.1f), StartUT); var fly_over_alt = TargetAltitude + LandingTrajectoryAutopilot.C.FlyOverAlt; if (WillOverheat) { SetBrakeEndPoint(Path.PointAtShipTemp(VSL.Physics.MinMaxTemperature - 100)); } else if (Path.UT2Altitude(brake_end_UT) < fly_over_alt) { SetBrakeEndPoint(Path.PointAtAltitude(fly_over_alt)); } else { SetBrakeEndUT(brake_end_UT); } } } else { SetBrakeEndUT(AerobrakeStartUT); } //update landing site update_landing_site_after_brake(); } else //no brake maneuver { SetBrakeEndUT(AerobrakeStartUT); BrakeStartPoint = BrakeEndPoint; BrakeDuration = 0; } } else { FlyAbovePoint = Path.FlyAbovePoint(Target.OrbPos(Body)); if (WillOverheat) { SetBrakeEndPoint(Path.PointAtShipTemp(VSL.Physics.MinMaxTemperature - 100)); } else { SetBrakeEndPoint(FlyAbovePoint); } SetBrakeDeltaV(-AtTargetVel - Vector3d.Cross(Body.zUpAngularVelocity, AtTargetPos)); if (BrakeFuel > 0) { var offset = MatchVelocityAutopilot.BrakingOffset((float)BrakeDeltaV.magnitude, VSL, out BrakeDuration); BrakeStartPoint = Path.PointAtUT(Math.Max(BrakeEndPoint.UT - offset, StartUT)); } else { SetBrakeEndUT(AerobrakeStartUT); BrakeStartPoint = BrakeStartPoint; BrakeDuration = 0; } } BrakeOffset = (float)Utils.ClampL(BrakeEndPoint.UT - BrakeStartPoint.UT, 0); //compute vessel coordinates at maneuver start if (VSL.LandedOrSplashed) { VslStartLat = Utils.ClampAngle(VSL.vessel.latitude); VslStartLon = Utils.ClampAngle(VSL.vessel.longitude); } else { var start_pos = (TrajectoryCalculator.BodyRotationAtdT(Body, -TimeToStart) * StartPos).xzy + Body.position; VslStartLat = Utils.ClampAngle(Body.GetLatitude(start_pos)); VslStartLon = Utils.ClampAngle(Body.GetLongitude(start_pos)); } //compute distance to target DistanceToTarget = Target.AngleTo(SurfacePoint) * Body.Radius; SurfacePoint.Name += string.Format("\n{0} from target", Utils.formatBigValue((float)DistanceToTarget, "m")); //compute distance in lat-lon coordinates DeltaLat = Utils.AngleDelta(SurfacePoint.Pos.Lat, Target.Pos.Lat) * Math.Sign(Utils.AngleDelta(approach.Lat, SurfacePoint.Pos.Lat)); DeltaLon = Utils.AngleDelta(SurfacePoint.Pos.Lon, Target.Pos.Lon) * Math.Sign(Utils.AngleDelta(approach.Lon, SurfacePoint.Pos.Lon)); //compute distance in radial coordinates DeltaFi = 90 - Utils.Angle2(Orbit.GetOrbitNormal(), TrajectoryCalculator.BodyRotationAtdT(Body, TimeToTarget) * Target.OrbPos(Body)); DeltaR = Utils.RadDelta(SurfacePoint.AngleTo(VslStartLat, VslStartLon), Target.AngleTo(VslStartLat, VslStartLon)) * Mathf.Rad2Deg; // Utils.Log("{}", this);//debug }
protected override void WindowGUI(int windowID) { GUILayout.BeginVertical(); List <ManeuverNode> maneuverNodes = GetManeuverNodes(); bool anyNodeExists = GetManeuverNodes().Any(); if (anyNodeExists) { GUILayout.BeginHorizontal(); if (GUILayout.Button(createNode ? "Create a new" : "Change the last")) { createNode = !createNode; } GUILayout.Label("maneuver node to:"); GUILayout.EndHorizontal(); } else { GUILayout.Label("Create a new maneuver node to:"); createNode = true; } operationId = GuiUtils.ComboBox.Box(operationId, operationNames, this); // Compute orbit and universal time parameters for next maneuver double UT = vesselState.time; Orbit o = orbit; if (anyNodeExists) { if (createNode) { ManeuverNode last = maneuverNodes.Last(); UT = last.UT; o = last.nextPatch; } else if (maneuverNodes.Count() > 1) { ManeuverNode last = maneuverNodes[maneuverNodes.Count() - 1]; UT = last.UT; o = last.nextPatch; } } try { operation[operationId].DoParametersGUI(o, UT, core.target); } catch (Exception) { } // TODO: Would be better to fix the problem but this will do for now if (anyNodeExists) { GUILayout.Label("after the last maneuver node."); } bool makingNode = false; bool executingNode = false; GUILayout.BeginHorizontal(); if (GUILayout.Button("Create node")) { makingNode = true; executingNode = false; } if (core.node != null && GUILayout.Button("Create and execute")) { makingNode = true; executingNode = true; } GUILayout.EndHorizontal(); if (makingNode) { var computedNode = operation[operationId].MakeNode(o, UT, core.target); if (computedNode != null) { if (!createNode) { vessel.patchedConicSolver.RemoveManeuverNode(maneuverNodes.Last()); } vessel.PlaceManeuverNode(o, computedNode.dV, computedNode.UT); } if (executingNode && core.node != null) { core.node.ExecuteOneNode(this); } } if (operation[operationId].getErrorMessage().Length > 0) { GUIStyle s = new GUIStyle(GUI.skin.label); s.normal.textColor = Color.yellow; GUILayout.Label(operation[operationId].getErrorMessage(), s); } if (GUILayout.Button("Remove ALL nodes")) { vessel.RemoveAllManeuverNodes(); } if (core.node != null) { if (anyNodeExists && !core.node.enabled) { if (GUILayout.Button("Execute next node")) { core.node.ExecuteOneNode(this); } if (vessel.patchedConicSolver.maneuverNodes.Count > 1) { if (GUILayout.Button("Execute all nodes")) { core.node.ExecuteAllNodes(this); } } } else if (core.node.enabled) { if (GUILayout.Button("Abort node execution")) { core.node.Abort(); } } GUILayout.BeginHorizontal(); core.node.autowarp = GUILayout.Toggle(core.node.autowarp, "Auto-warp", GUILayout.ExpandWidth(true)); GUILayout.Label("Tolerance:", GUILayout.ExpandWidth(false)); core.node.tolerance.text = GUILayout.TextField(core.node.tolerance.text, GUILayout.Width(35), GUILayout.ExpandWidth(false)); if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) { core.node.tolerance.val += 0.1; } if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) { core.node.tolerance.val -= core.node.tolerance.val > 0.1 ? 0.1 : 0.0; } if (GUILayout.Button("R", GUILayout.ExpandWidth(false))) { core.node.tolerance.val = 0.1; } GUILayout.Label("m/s", GUILayout.ExpandWidth(false)); GUILayout.EndHorizontal(); } GUILayout.EndVertical(); base.WindowGUI(windowID, operation[operationId].draggable); }
void Start() { orbit = transform.GetComponent <Orbit>(); }
//Estimate the delta-V of the correction burn that would be required to put us on //course for the target public Vector3d ComputeCourseCorrection(bool allowPrograde) { //actualLandingPosition is the predicted actual landing position Vector3d actualLandingPosition = RotatedLandingSite - mainBody.position; //orbitLandingPosition is the point where our current orbit intersects the planet double endRadius = mainBody.Radius + DecelerationEndAltitude() - 100; Vector3d orbitLandingPosition = orbit.SwappedRelativePositionAtUT(orbit.NextTimeOfRadius(vesselState.time, endRadius)); //convertOrbitToActual is a rotation that rotates orbitLandingPosition on actualLandingPosition Quaternion convertOrbitToActual = Quaternion.FromToRotation(orbitLandingPosition, actualLandingPosition); //Consider the effect small changes in the velocity in each of these three directions Vector3d[] perturbationDirections = { vesselState.velocityVesselSurfaceUnit, vesselState.radialPlusSurface, vesselState.normalPlusSurface }; //Compute the effect burns in these directions would //have on the landing position, where we approximate the landing position as the place //the perturbed orbit would intersect the planet. Vector3d[] deltas = new Vector3d[3]; for (int i = 0; i < 3; i++) { double perturbationDeltaV = 1; //warning: hard experience shows that setting this too low leads to bewildering bugs due to finite precision of Orbit functions Orbit perturbedOrbit = orbit.PerturbedOrbit(vesselState.time, perturbationDeltaV * perturbationDirections[i]); //compute the perturbed orbit double perturbedLandingTime; if (perturbedOrbit.PeR < endRadius) { perturbedLandingTime = perturbedOrbit.NextTimeOfRadius(vesselState.time, endRadius); } else { perturbedLandingTime = perturbedOrbit.NextPeriapsisTime(vesselState.time); } Vector3d perturbedLandingPosition = perturbedOrbit.SwappedRelativePositionAtUT(perturbedLandingTime); //find where it hits the planet Vector3d landingDelta = perturbedLandingPosition - orbitLandingPosition; //find the difference between that and the original orbit's intersection point landingDelta = convertOrbitToActual * landingDelta; //rotate that difference vector so that we can now think of it as starting at the actual landing position landingDelta = Vector3d.Exclude(actualLandingPosition, landingDelta); //project the difference vector onto the plane tangent to the actual landing position deltas[i] = landingDelta / perturbationDeltaV; //normalize by the delta-V considered, so that deltas now has units of meters per (meter/second) [i.e., seconds] } //Now deltas stores the predicted offsets in landing position produced by each of the three perturbations. //We now figure out the offset we actually want //First we compute the target landing position. We have to convert the latitude and longitude of the target //into a position. We can't just get the current position of those coordinates, because the planet will //rotate during the descent, so we have to account for that. Vector3d desiredLandingPosition = mainBody.GetRelSurfacePosition(core.target.targetLatitude, core.target.targetLongitude, 0); float bodyRotationAngleDuringDescent = (float)(360 * (prediction.endUT - vesselState.time) / mainBody.rotationPeriod); Quaternion bodyRotationDuringFall = Quaternion.AngleAxis(bodyRotationAngleDuringDescent, mainBody.angularVelocity.normalized); desiredLandingPosition = bodyRotationDuringFall * desiredLandingPosition; Vector3d desiredDelta = desiredLandingPosition - actualLandingPosition; desiredDelta = Vector3d.Exclude(actualLandingPosition, desiredDelta); //Now desiredDelta gives the desired change in our actual landing position (projected onto a plane //tangent to the actual landing position). Vector3d downrangeDirection; Vector3d downrangeDelta; if (allowPrograde) { //Construct the linear combination of the prograde and radial+ perturbations //that produces the largest effect on the landing position. The Math.Sign is to //detect and handle the case where radial+ burns actually bring the landing sign closer //(e.g. when we are traveling close to straight up) downrangeDirection = (deltas[0].magnitude * perturbationDirections[0] + Math.Sign(Vector3d.Dot(deltas[0], deltas[1])) * deltas[1].magnitude * perturbationDirections[1]).normalized; downrangeDelta = Vector3d.Dot(downrangeDirection, perturbationDirections[0]) * deltas[0] + Vector3d.Dot(downrangeDirection, perturbationDirections[1]) * deltas[1]; } else { //If we aren't allowed to burn prograde, downrange component of the landing //position has to be controlled by radial+/- burns: downrangeDirection = perturbationDirections[1]; downrangeDelta = deltas[1]; } //Now solve a 2x2 system of linear equations to determine the linear combination //of perturbationDirection01 and normal+ that will give the desired offset in the //predicted landing position. Matrix2x2 A = new Matrix2x2( downrangeDelta.sqrMagnitude, Vector3d.Dot(downrangeDelta, deltas[2]), Vector3d.Dot(downrangeDelta, deltas[2]), deltas[2].sqrMagnitude ); Vector2d b = new Vector2d(Vector3d.Dot(desiredDelta, downrangeDelta), Vector3d.Dot(desiredDelta, deltas[2])); Vector2d coeffs = A.inverse() * b; Vector3d courseCorrection = coeffs.x * downrangeDirection + coeffs.y * perturbationDirections[2]; return(courseCorrection); }
/// <summary> /// Draws the orbit. /// </summary> /// <param name="orbit">The orbit.</param> /// <param name="color">The color of the created line.</param> public static void DrawOrbit(Orbit orbit, Color color) { DrawOrbit(orbit, color, Style.SOLID); }
bool OrbitReenters(Orbit initialOrbit) { return(initialOrbit.PeR < decelRadius || initialOrbit.PeR < aerobrakedRadius); }
public override void Drive(FlightCtrlState s) { if (!core.target.NormalTargetExists) { users.Clear(); return; } core.node.autowarp = core.node.autowarp && core.target.Distance > 1000; //If we get within the target distance and then next maneuver node is still //far in the future, delete it and we will create a new one to match velocities immediately. //This can often happen because the target vessel's orbit shifts slightly when it is unpacked. if (core.target.Distance < desiredDistance && vessel.patchedConicSolver.maneuverNodes.Count > 0 && vessel.patchedConicSolver.maneuverNodes[0].UT > vesselState.time + 1) { vessel.RemoveAllManeuverNodes(); } if (vessel.patchedConicSolver.maneuverNodes.Count > 0) { //If we have plotted a maneuver, execute it. if (!core.node.enabled) { core.node.ExecuteAllNodes(this); } } else if (core.target.Distance < desiredDistance * 1.05 + 2 && core.target.RelativeVelocity.magnitude < 1) { //finished users.Clear(); core.thrust.ThrustOff(); status = Localizer.Format("#MechJeb_RZauto_statu1");//"Successful rendezvous" } else if (core.target.Distance < desiredDistance * 1.05 + 2) { //We are within the target distance: match velocities double UT = vesselState.time; Vector3d dV = OrbitalManeuverCalculator.DeltaVToMatchVelocities(orbit, UT, core.target.TargetOrbit); vessel.PlaceManeuverNode(orbit, dV, UT); status = Localizer.Format("#MechJeb_RZauto_statu2", desiredDistance.ToString());//"Within " + + "m: matching velocities." } else if (core.target.Distance < vesselState.radius / 25) { if (orbit.NextClosestApproachDistance(core.target.TargetOrbit, vesselState.time) < desiredDistance && orbit.NextClosestApproachTime(core.target.TargetOrbit, vesselState.time) < vesselState.time + 150) { //We're close to the target, and on a course that will take us closer. Kill relvel at closest approach double UT = orbit.NextClosestApproachTime(core.target.TargetOrbit, vesselState.time); Vector3d dV = OrbitalManeuverCalculator.DeltaVToMatchVelocities(orbit, UT, core.target.TargetOrbit); //adjust burn time so as to come to rest at the desired distance from the target: double approachDistance = orbit.Separation(core.target.TargetOrbit, UT); double approachSpeed = (orbit.SwappedOrbitalVelocityAtUT(UT) - core.target.TargetOrbit.SwappedOrbitalVelocityAtUT(UT)).magnitude; if (approachDistance < desiredDistance) { UT -= Math.Sqrt(Math.Abs(desiredDistance * desiredDistance - approachDistance * approachDistance)) / approachSpeed; } //if coming in hot, stop early to avoid crashing: if (approachSpeed > 10) { UT -= 1; } vessel.PlaceManeuverNode(orbit, dV, UT); status = Localizer.Format("#MechJeb_RZauto_statu3");//"Planning to match velocities at closest approach." } else { //We're not far from the target. Close the distance double closingSpeed = core.target.Distance / 100; if (closingSpeed > maxClosingSpeed) { closingSpeed = maxClosingSpeed; } closingSpeed = Math.Max(0.01, closingSpeed); double closingTime = core.target.Distance / closingSpeed; double UT = vesselState.time + 15; Vector3d dV = OrbitalManeuverCalculator.DeltaVToInterceptAtTime(orbit, UT, core.target.TargetOrbit, closingTime); vessel.PlaceManeuverNode(orbit, dV, UT); status = Localizer.Format("#MechJeb_RZauto_statu4");//"Close to target: plotting intercept" } } else if (orbit.NextClosestApproachDistance(core.target.TargetOrbit, vesselState.time) < core.target.TargetOrbit.semiMajorAxis / 25) { //We're not close to the target, but we're on an approximate intercept course. //Kill relative velocities at closest approach double UT = orbit.NextClosestApproachTime(core.target.TargetOrbit, vesselState.time); Vector3d dV = OrbitalManeuverCalculator.DeltaVToMatchVelocities(orbit, UT, core.target.TargetOrbit); //adjust burn time so as to come to rest at the desired distance from the target: double approachDistance = (orbit.SwappedAbsolutePositionAtUT(UT) - core.target.TargetOrbit.SwappedAbsolutePositionAtUT(UT)).magnitude; double approachSpeed = (orbit.SwappedOrbitalVelocityAtUT(UT) - core.target.TargetOrbit.SwappedOrbitalVelocityAtUT(UT)).magnitude; if (approachDistance < desiredDistance) { UT -= Math.Sqrt(Math.Abs(desiredDistance * desiredDistance - approachDistance * approachDistance)) / approachSpeed; } //if coming in hot, stop early to avoid crashing: if (approachSpeed > 10) { UT -= 1; } vessel.PlaceManeuverNode(orbit, dV, UT); status = Localizer.Format("#MechJeb_RZauto_statu5");//"On intercept course. Planning to match velocities at closest approach." } else if (orbit.RelativeInclination(core.target.TargetOrbit) < 0.05 && orbit.eccentricity < 0.05) { //We're not on an intercept course, but we have a circular orbit in the right plane. double hohmannUT; Vector3d hohmannDV = OrbitalManeuverCalculator.DeltaVAndTimeForHohmannTransfer(orbit, core.target.TargetOrbit, vesselState.time, out hohmannUT); double numPhasingOrbits = (hohmannUT - vesselState.time) / orbit.period; double actualMaxPhasingOrbits = Math.Max(maxPhasingOrbits, 5); // ignore input values that are unreasonably small if (numPhasingOrbits < actualMaxPhasingOrbits) { //It won't be too long until the intercept window. Plot a Hohmann transfer intercept. vessel.PlaceManeuverNode(orbit, hohmannDV, hohmannUT); status = Localizer.Format("#MechJeb_RZauto_statu6", numPhasingOrbits.ToString("F2"));//"Planning Hohmann transfer for intercept after " + + " phasing orbits." } else { //We are in a circular orbit in the right plane, but we aren't phasing quickly enough. Move to a better phasing orbit double axisRatio = Math.Pow(1 + 1.25 / actualMaxPhasingOrbits, 2.0 / 3.0); double lowPhasingRadius = core.target.TargetOrbit.semiMajorAxis / axisRatio; double highPhasingRadius = core.target.TargetOrbit.semiMajorAxis * axisRatio; bool useLowPhasingRadius = (lowPhasingRadius > mainBody.Radius + mainBody.RealMaxAtmosphereAltitude() + 3000) && (orbit.semiMajorAxis < core.target.TargetOrbit.semiMajorAxis); double phasingOrbitRadius = (useLowPhasingRadius ? lowPhasingRadius : highPhasingRadius); if (orbit.ApR < phasingOrbitRadius) { double UT1 = vesselState.time + 15; Vector3d dV1 = OrbitalManeuverCalculator.DeltaVToChangeApoapsis(orbit, UT1, phasingOrbitRadius); vessel.PlaceManeuverNode(orbit, dV1, UT1); Orbit transferOrbit = vessel.patchedConicSolver.maneuverNodes[0].nextPatch; double UT2 = transferOrbit.NextApoapsisTime(UT1); Vector3d dV2 = OrbitalManeuverCalculator.DeltaVToCircularize(transferOrbit, UT2); vessel.PlaceManeuverNode(transferOrbit, dV2, UT2); } else if (orbit.PeR > phasingOrbitRadius) { double UT1 = vesselState.time + 15; Vector3d dV1 = OrbitalManeuverCalculator.DeltaVToChangePeriapsis(orbit, UT1, phasingOrbitRadius); vessel.PlaceManeuverNode(orbit, dV1, UT1); Orbit transferOrbit = vessel.patchedConicSolver.maneuverNodes[0].nextPatch; double UT2 = transferOrbit.NextPeriapsisTime(UT1); Vector3d dV2 = OrbitalManeuverCalculator.DeltaVToCircularize(transferOrbit, UT2); vessel.PlaceManeuverNode(transferOrbit, dV2, UT2); } else { double UT = orbit.NextTimeOfRadius(vesselState.time, phasingOrbitRadius); Vector3d dV = OrbitalManeuverCalculator.DeltaVToCircularize(orbit, UT); vessel.PlaceManeuverNode(orbit, dV, UT); } status = Localizer.Format("#MechJeb_RZauto_statu7", numPhasingOrbits.ToString("F1"), maxPhasingOrbits.text, MuUtils.ToSI(phasingOrbitRadius - mainBody.Radius, 0));//"Next intercept window would be <<1>> orbits away, which is more than the maximum of <<2>> phasing orbits. Increasing phasing rate by establishing new phasing orbit at <<3>>m } } else if (orbit.RelativeInclination(core.target.TargetOrbit) < 0.05) { //We're not on an intercept course. We're in the right plane, but our orbit isn't circular. Circularize. bool circularizeAtPe; if (orbit.eccentricity > 1) { circularizeAtPe = true; } else { circularizeAtPe = Math.Abs(orbit.PeR - core.target.TargetOrbit.semiMajorAxis) < Math.Abs(orbit.ApR - core.target.TargetOrbit.semiMajorAxis); } double UT; if (circularizeAtPe) { UT = Math.Max(vesselState.time, orbit.NextPeriapsisTime(vesselState.time)); } else { UT = orbit.NextApoapsisTime(vesselState.time); } Vector3d dV = OrbitalManeuverCalculator.DeltaVToCircularize(orbit, UT); vessel.PlaceManeuverNode(orbit, dV, UT); status = Localizer.Format("#MechJeb_RZauto_statu8");//"Circularizing." } else { //We're not on an intercept course, and we're not in the right plane. Match planes bool ascending; if (orbit.eccentricity < 1) { if (orbit.TimeOfAscendingNode(core.target.TargetOrbit, vesselState.time) < orbit.TimeOfDescendingNode(core.target.TargetOrbit, vesselState.time)) { ascending = true; } else { ascending = false; } } else { if (orbit.AscendingNodeExists(core.target.TargetOrbit)) { ascending = true; } else { ascending = false; } } double UT; Vector3d dV; if (ascending) { dV = OrbitalManeuverCalculator.DeltaVAndTimeToMatchPlanesAscending(orbit, core.target.TargetOrbit, vesselState.time, out UT); } else { dV = OrbitalManeuverCalculator.DeltaVAndTimeToMatchPlanesDescending(orbit, core.target.TargetOrbit, vesselState.time, out UT); } vessel.PlaceManeuverNode(orbit, dV, UT); status = Localizer.Format("#MechJeb_RZauto_statu9");//"Matching planes." } }
/// <summary> /// Draws the orbit. /// </summary> /// <param name="orbit">The orbit.</param> public static void DrawOrbit(Orbit orbit) { DrawOrbit(orbit, Color.white, Style.SOLID); }
public static ManeuverParameters ComputeEjectionManeuver(Vector3d exit_velocity, Orbit initial_orbit, double UT_0) { double GM = initial_orbit.referenceBody.gravParameter; double C3 = exit_velocity.sqrMagnitude; double Mh = initial_orbit.referenceBody.sphereOfInfluence; double Rpe = initial_orbit.semiMajorAxis; double isma = 2 / Mh - C3 / GM; //inverted Semi-major Axis, will work for parabolic orbit double ecc = 1.0 - Rpe * isma; //double vstart = Math.Sqrt(GM * (2 / Rpe - isma)); //{ total start boost} //double slat = Rpe * Rpe * vstart * vstart / GM; //the problem here should be R for circular orbit instead of Rpe double slat = 2 * Rpe - isma * Rpe * Rpe; //but we don't know start point (yet) in elliptic orbit double theta = Math.Acos((slat / Mh - 1) / ecc); /* * //old way infinity hyperbolic: * //problems: it's not necessary hyperbolic (in case of low speed exit_velocity), * //and exit_velocity appears not infinite far from celestial body, but only sphereOfInfluence far * //i.e. Mh in previous statements(theta, isma) is not infinity! * * double sma = -GM / C3; * * double ecc = 1 - Rpe / sma; * double theta = Math.Acos(-1 / ecc); * */ Vector3d intersect_1; Vector3d intersect_2; // intersect_1 is the optimal position on the orbit assuming the orbit is circular and the sphere of influence is infinite IntersectConePlane(exit_velocity, theta, initial_orbit.h, out intersect_1, out intersect_2); Vector3d eccvec = initial_orbit.GetEccVector(); double true_anomaly = AngleAboutAxis(eccvec, intersect_1, initial_orbit.h); // GetMeanAnomalyAtTrueAnomaly expects degrees and returns radians double mean_anomaly = initial_orbit.GetMeanAnomalyAtTrueAnomaly(true_anomaly * 180 / Math.PI); double delta_mean_anomaly = MuUtils.ClampRadiansPi(mean_anomaly - initial_orbit.MeanAnomalyAtUT(UT_0)); double UT = UT_0 + delta_mean_anomaly / initial_orbit.MeanMotion(); Vector3d V_0 = initial_orbit.getOrbitalVelocityAtUT(UT); Vector3d pos = initial_orbit.getRelativePositionAtUT(UT); double final_vel = Math.Sqrt(C3 + 2 * GM / pos.magnitude); Vector3d x = pos.normalized; Vector3d z = Vector3d.Cross(x, exit_velocity).normalized; Vector3d y = Vector3d.Cross(z, x); theta = Math.PI / 2; double dtheta = 0.001; Orbit sample = new Orbit(); double theta_err = Double.MaxValue; for (int iteration = 0; iteration < 50; ++iteration) { Vector3d V_1 = final_vel * (Math.Cos(theta) * x + Math.Sin(theta) * y); sample.UpdateFromStateVectors(pos, V_1, initial_orbit.referenceBody, UT); theta_err = AngleAboutAxis(exit_velocity, sample.getOrbitalVelocityAtUT(OrbitExtensions.NextTimeOfRadius(sample, UT, sample.referenceBody.sphereOfInfluence)), z); if (double.IsNaN(theta_err)) { return(null); } if (Math.Abs(theta_err) <= Math.PI / 1800) { return(new ManeuverParameters((V_1 - V_0).xzy, UT)); } V_1 = final_vel * (Math.Cos(theta + dtheta) * x + Math.Sin(theta + dtheta) * y); sample.UpdateFromStateVectors(pos, V_1, initial_orbit.referenceBody, UT); double theta_err_2 = AngleAboutAxis(exit_velocity, sample.getOrbitalVelocityAtUT(OrbitExtensions.NextTimeOfRadius(sample, UT, sample.referenceBody.sphereOfInfluence)), z); V_1 = final_vel * (Math.Cos(theta - dtheta) * x + Math.Sin(theta - dtheta) * y); sample.UpdateFromStateVectors(pos, V_1, initial_orbit.referenceBody, UT); double theta_err_3 = AngleAboutAxis(exit_velocity, sample.getOrbitalVelocityAtUT(OrbitExtensions.NextTimeOfRadius(sample, UT, sample.referenceBody.sphereOfInfluence)), z); double derr = MuUtils.ClampRadiansPi(theta_err_2 - theta_err_3) / (2 * dtheta); theta = MuUtils.ClampRadiansTwoPi(theta - theta_err / derr); // if theta > pi, replace with 2pi - theta, the function ejection_angle=f(theta) is symmetrc wrt theta=pi if (theta > Math.PI) { theta = 2 * Math.PI - theta; } } return(null); }
public static ManeuverParameters OptimizeEjection(ManeuverParameters original_maneuver, Orbit initial_orbit, Orbit target, double UT_arrival, double earliest_UT) { while (true) { alglib.minlmstate state; alglib.minlmreport rep; double[] x = new double[4]; double[] scale = new double[4]; x[0] = original_maneuver.dV.x; x[1] = original_maneuver.dV.y; x[2] = original_maneuver.dV.z; x[3] = 0; scale[0] = scale[1] = scale[2] = original_maneuver.dV.magnitude; scale[3] = initial_orbit.period; OptimizerData data = new OptimizerData(); data.initial_orbit = initial_orbit; data.original_UT = original_maneuver.UT; data.UT_arrival = UT_arrival; data.pos_arrival = target.getTruePositionAtUT(UT_arrival); alglib.minlmcreatev(4, x, 0.01, out state); double epsx = 1e-4; // stop if |(x(k+1)-x(k)) ./ scale| <= EpsX double epsf = 0.01; // stop if |F(k+1)-F(k)| <= EpsF*max{|F(k)|,|F(k+1)|,1} alglib.minlmsetcond(state, 0, epsf, epsx, 50); alglib.minlmsetscale(state, scale); alglib.minlmoptimize(state, DistanceToTarget, null, data); alglib.minlmresults(state, out x, out rep); Debug.Log("Transfer calculator: termination type=" + rep.terminationtype); Debug.Log("Transfer calculator: iteration count=" + rep.iterationscount); // try again in one orbit if the maneuver node is in the past if (original_maneuver.UT + x[3] < earliest_UT) { Debug.Log("Transfer calculator: maneuver is " + (earliest_UT - original_maneuver.UT - x[3]) + " s too early, trying again in " + initial_orbit.period + " s"); original_maneuver.UT += initial_orbit.period; } else { return(new ManeuverParameters(new Vector3d(x[0], x[1], x[2]), original_maneuver.UT + x[3])); } } }
// Creates a new ship with the given parameters for this mission. The code however seems unnecessarily convoluted and // error-prone, but there are no better examples available on the internet. private void CreateShip() { try { // The ShipConstruct-object can only savely exist while not in flight, otherwise it will spam Null-Pointer Exceptions every tick: if (HighLogic.LoadedScene == GameScenes.FLIGHT) { throw new Exception("unable to run CreateShip while in flight"); } // Load the parts form the saved vessel: if (!File.Exists(shipTemplateFilename)) { throw new Exception("file '" + shipTemplateFilename + "' not found"); } ShipConstruct shipConstruct = ShipConstruction.LoadShip(shipTemplateFilename); ProtoVessel dummyProto = new ProtoVessel(new ConfigNode(), null); Vessel dummyVessel = new Vessel(); dummyProto.vesselRef = dummyVessel; // Maybe adjust the orbit: float vesselHeight = Math.Max(Math.Max(shipConstruct.shipSize.x, shipConstruct.shipSize.y), shipConstruct.shipSize.z); if (missionType == MissionType.DEPLOY) { // Make sure that there won't be any collisions, when the vessel is created at the given orbit: orbit = GUIOrbitEditor.ApplySafetyDistance(orbit, vesselHeight); } else if (missionType == MissionType.CONSTRUCT) { // Deploy the new ship next to the space-dock: Vessel spaceDock = TargetVessel.GetVesselById((Guid)targetVesselId); orbit = GUIOrbitEditor.CreateFollowingOrbit(spaceDock.orbit, TargetVessel.GetVesselSize(spaceDock) + vesselHeight); orbit = GUIOrbitEditor.ApplySafetyDistance(orbit, vesselHeight); } else { throw new Exception("invalid mission-type '" + missionType.ToString() + "'"); } // Instead of loading and constructing the ship ourselfs ShipConstruction.AssembleForLaunch() seems like the better // option, this however just seems to work while in flight and even then I was unable to get it working correctly // (it switches to the newly created ship and setting an orbit afterwards does not work correctly). // In theory it should be enough to simply copy the parts from the ShipConstruct to the ProtoVessel, but // this only seems to work when the saved vessel starts with the root-part and is designed top down from there. // It seems that the root part has to be the first part in the ProtoVessel's parts-list and all other parts have // to be listed in sequence radiating from the root part (eg 1=>2=>R<=3<=4 should be R,2,1,3,4). If the parts // are not in the correct order, their rotation seems to get messed up or they are attached to the wrong // attachmet-nodes, which is why we have to re-sort the parts with our own logic here. // This part of the code is experimental however and only based on my own theories and observations about KSP's vessels. Part rootPart = null; foreach (Part p in shipConstruct.parts) { if (p.parent == null) { rootPart = p; break; } } List <Part> pList = null; dummyVessel.parts = FindAndAddAttachedParts(rootPart, ref pList); // Find all parts which are directly attached to the root-part and add them in order. // Handle Subassemblies which are attached by surface attachment-nodes: bool handleSurfaceAttachments = true; while (dummyVessel.parts.Count < shipConstruct.parts.Count) { int processedParts = 0; foreach (Part p in shipConstruct.parts) { if (dummyVessel.parts.Contains(p)) { continue; } if (handleSurfaceAttachments) { // Check if the part is attached by a surface-node: if (p.srfAttachNode != null && dummyVessel.parts.Contains(p.srfAttachNode.attachedPart)) { // Add this surface attached part and all the sub-parts: dummyVessel.parts = FindAndAddAttachedParts(p, ref dummyVessel.parts); processedParts++; } } else { // Simply copy this part: dummyVessel.parts.Add(p); } } if (processedParts == 0) { // If there are still unprocessed parts, just throw them in the list during the next iteration, // this should not happen but we don't want to end up in an endless loop: handleSurfaceAttachments = false; } } // Initialize all parts: uint missionID = (uint)Guid.NewGuid().GetHashCode(); uint launchID = HighLogic.CurrentGame.launchID++; int maxStageOffset = -1; foreach (Part p in dummyVessel.parts) { p.flagURL = flagURL == null ? HighLogic.CurrentGame.flagURL : flagURL; p.missionID = missionID; p.launchID = launchID; p.temperature = 1.0; maxStageOffset = Math.Max(p.stageOffset, maxStageOffset); // stageOffset is offset of this part in the staging-order (0..n with -1 meaning not staged) // Apparently the part's ID from the saved craft is stored in craftID and has to get copied by hand into flightID, which is 0 by default. // If it is not set, docking won't work and since it is referenced by surface-attachments like struts and fuel lines, it should always // be the same as in the stored craft: p.flightID = p.craftID; // If the KRnD-Mod is installed, make sure that all parts of this newly created ship are set to the lates version: foreach (PartModule module in p.Modules) { if (module.moduleName != "KRnDModule") { continue; } Debug.Log("[KSTS] found KRnD on '" + p.name.ToString() + "', setting to latest stats"); foreach (BaseField field in module.Fields) { if (field.name.ToString() == "upgradeToLatest") { field.SetValue(1, module); // Newer versions of KRnD use this flag to upgrade all attributes of the given part to the latest levels, when the vessel is activated. if (field.GetValue(module).ToString() != "1") { Debug.LogError("[KSTS] unable to modify '" + field.name.ToString() + "'"); } } } } dummyProto.protoPartSnapshots.Add(new ProtoPartSnapshot(p, dummyProto)); } // Store the parts in Config-Nodes: foreach (ProtoPartSnapshot p in dummyProto.protoPartSnapshots) { p.storePartRefs(); } List <ConfigNode> partNodesL = new List <ConfigNode>(); foreach (ProtoPartSnapshot snapShot in dummyProto.protoPartSnapshots) { ConfigNode node = new ConfigNode("PART"); snapShot.Save(node); partNodesL.Add(node); } ConfigNode[] partNodes = partNodesL.ToArray(); ConfigNode[] additionalNodes = new ConfigNode[0]; // This will actually create the ship and add it to the global list of flights: ConfigNode protoVesselNode = ProtoVessel.CreateVesselNode(shipName, VesselType.Ship, orbit, 0, partNodes, additionalNodes); ProtoVessel pv = HighLogic.CurrentGame.AddVessel(protoVesselNode); Debug.Log("[KSTS] deployed new ship '" + shipName.ToString() + "' as '" + pv.vesselRef.id.ToString() + "'"); ScreenMessages.PostScreenMessage("Vessel '" + shipName.ToString() + "' deployed"); // Popup message to notify the player Vessel newVessel = FlightGlobals.Vessels.Find(x => x.id == pv.vesselID); // While each part knows in which stage they are, the vessel has to know how many stages there are in total: newVessel.protoVessel.stage = maxStageOffset + 1; // an offest of 0 would mean that there is only one stage // Maybe add the initial crew to the vessel: if (crewToDeliver != null && crewToDeliver.Count > 0 && newVessel != null) { foreach (string kerbonautName in crewToDeliver) { TargetVessel.AddCrewMember(newVessel, kerbonautName); } } // Notify other mods about the new vessel: GameEvents.onVesselCreate.Fire(newVessel); } catch (Exception e) { Debug.LogError("[KSTS] Mission.CreateShip(): " + e.ToString()); } }
public static Mission CreateDeployment(string shipName, ShipTemplate template, Orbit orbit, MissionProfile profile, List <string> crew, string flagURL) { Mission mission = new Mission(); mission.missionType = MissionType.DEPLOY; mission.shipTemplateFilename = SanitizePath(template.filename); // The filename contains silly portions like "KSP_x64_Data/..//saves", which break savegames because "//" starts a comment in the savegame ... mission.orbit = orbit; mission.shipName = shipName; mission.profileName = profile.profileName; mission.eta = Planetarium.GetUniversalTime() + profile.missionDuration; mission.crewToDeliver = crew; // The crew we want the new vessel to start with. mission.flagURL = flagURL; return(mission); }
public Quaternion attitudeGetReferenceRotation(AttitudeReference reference) { Vector3 fwd, up; Quaternion rotRef = Quaternion.identity; if (core.target.Target == null && (reference == AttitudeReference.TARGET || reference == AttitudeReference.TARGET_ORIENTATION || reference == AttitudeReference.RELATIVE_VELOCITY)) { attitudeDeactivate(); return(rotRef); } if ((reference == AttitudeReference.MANEUVER_NODE) && (vessel.patchedConicSolver.maneuverNodes.Count == 0)) { attitudeDeactivate(); return(rotRef); } switch (reference) { case AttitudeReference.ORBIT: rotRef = Quaternion.LookRotation(vesselState.orbitalVelocity.normalized, vesselState.up); break; case AttitudeReference.ORBIT_HORIZONTAL: rotRef = Quaternion.LookRotation(Vector3d.Exclude(vesselState.up, vesselState.orbitalVelocity.normalized), vesselState.up); break; case AttitudeReference.SURFACE_NORTH: rotRef = vesselState.rotationSurface; break; case AttitudeReference.SURFACE_VELOCITY: rotRef = Quaternion.LookRotation(vesselState.surfaceVelocity.normalized, vesselState.up); break; case AttitudeReference.TARGET: fwd = (core.target.Position - vessel.GetTransform().position).normalized; up = Vector3d.Cross(fwd, vesselState.normalPlus); Vector3.OrthoNormalize(ref fwd, ref up); rotRef = Quaternion.LookRotation(fwd, up); break; case AttitudeReference.RELATIVE_VELOCITY: fwd = core.target.RelativeVelocity.normalized; up = Vector3d.Cross(fwd, vesselState.normalPlus); Vector3.OrthoNormalize(ref fwd, ref up); rotRef = Quaternion.LookRotation(fwd, up); break; case AttitudeReference.TARGET_ORIENTATION: Transform targetTransform = core.target.Transform; if (core.target.CanAlign) { rotRef = Quaternion.LookRotation(targetTransform.forward, targetTransform.up); } else { rotRef = Quaternion.LookRotation(targetTransform.up, targetTransform.right); } break; case AttitudeReference.MANEUVER_NODE: fwd = vessel.patchedConicSolver.maneuverNodes[0].GetBurnVector(orbit); up = Vector3d.Cross(fwd, vesselState.normalPlus); Vector3.OrthoNormalize(ref fwd, ref up); rotRef = Quaternion.LookRotation(fwd, up); break; case AttitudeReference.SUN: Orbit baseOrbit = vessel.mainBody == Planetarium.fetch.Sun ? vessel.orbit : orbit.TopParentOrbit(); up = vesselState.CoM - Planetarium.fetch.Sun.transform.position; fwd = Vector3d.Cross(-baseOrbit.GetOrbitNormal().xzy.normalized, up); rotRef = Quaternion.LookRotation(fwd, up); break; case AttitudeReference.SURFACE_HORIZONTAL: rotRef = Quaternion.LookRotation(Vector3d.Exclude(vesselState.up, vesselState.surfaceVelocity.normalized), vesselState.up); break; } return(rotRef); }
public override AutopilotStep OnFixedUpdate() { //if we don't want to deorbit but we're already on a reentry trajectory, we can't wait until the ideal point //in the orbit to deorbt; we already have deorbited. if (orbit.ApA < mainBody.RealMaxAtmosphereAltitude()) { core.thrust.targetThrottle = 0; return(new CourseCorrection(core)); } //We aim for a trajectory that // a) has the same vertical speed as our current trajectory // b) has a horizontal speed that will give it a periapsis of -10% of the body's radius // c) has a heading that points toward where the target will be at the end of free-fall, accounting for planetary rotation Vector3d horizontalDV = OrbitalManeuverCalculator.DeltaVToChangePeriapsis(orbit, vesselState.time, 0.9 * mainBody.Radius); //Imagine we are going to deorbit now. Find the burn that would lower our periapsis to -10% of the planet's radius Orbit forwardDeorbitTrajectory = orbit.PerturbedOrbit(vesselState.time, horizontalDV); //Compute the orbit that would put us on double freefallTime = forwardDeorbitTrajectory.NextTimeOfRadius(vesselState.time, mainBody.Radius) - vesselState.time; //Find how long that orbit would take to impact the ground double planetRotationDuringFreefall = 360 * freefallTime / mainBody.rotationPeriod; //Find how many degrees the planet will rotate during that time Vector3d currentTargetRadialVector = mainBody.GetWorldSurfacePosition(core.target.targetLatitude, core.target.targetLongitude, 0) - mainBody.position; //Find the current vector from the planet center to the target landing site Quaternion freefallPlanetRotation = Quaternion.AngleAxis((float)planetRotationDuringFreefall, mainBody.angularVelocity); //Construct a quaternion representing the rotation of the planet found above Vector3d freefallEndTargetRadialVector = freefallPlanetRotation * currentTargetRadialVector; //Use this quaternion to find what the vector from the planet center to the target will be when we hit the ground Vector3d freefallEndTargetPosition = mainBody.position + freefallEndTargetRadialVector; //Then find the actual position of the target at that time Vector3d freefallEndHorizontalToTarget = Vector3d.Exclude(vesselState.up, freefallEndTargetPosition - vesselState.CoM).normalized; //Find a horizontal unit vector that points toward where the target will be when we hit the ground Vector3d currentHorizontalVelocity = Vector3d.Exclude(vesselState.up, vesselState.orbitalVelocity); //Find our current horizontal velocity double finalHorizontalSpeed = (currentHorizontalVelocity + horizontalDV).magnitude; //Find the desired horizontal speed after the deorbit burn Vector3d finalHorizontalVelocity = finalHorizontalSpeed * freefallEndHorizontalToTarget; //Combine the desired speed and direction to get the desired velocity after the deorbi burn //Compute the angle between the location of the target at the end of freefall and the normal to our orbit: Vector3d currentRadialVector = vesselState.CoM - mainBody.position; double targetAngleToOrbitNormal = Vector3d.Angle(orbit.SwappedOrbitNormal(), freefallEndTargetRadialVector); targetAngleToOrbitNormal = Math.Min(targetAngleToOrbitNormal, 180 - targetAngleToOrbitNormal); double targetAheadAngle = Vector3d.Angle(currentRadialVector, freefallEndTargetRadialVector); //How far ahead the target is, in degrees double planeChangeAngle = Vector3d.Angle(currentHorizontalVelocity, freefallEndHorizontalToTarget); //The plane change required to get onto the deorbit trajectory, in degrees //If the target is basically almost normal to our orbit, it doesn't matter when we deorbit; might as well do it now //Otherwise, wait until the target is ahead if (targetAngleToOrbitNormal < 10 || (targetAheadAngle < 90 && targetAheadAngle > 60 && planeChangeAngle < 90)) { deorbitBurnTriggered = true; } if (deorbitBurnTriggered) { if (!MuUtils.PhysicsRunning()) { core.warp.MinimumWarp(); } //get out of warp Vector3d deltaV = finalHorizontalVelocity - currentHorizontalVelocity; core.attitude.attitudeTo(deltaV.normalized, AttitudeReference.INERTIAL, core.landing); if (deltaV.magnitude < 2.0) { return(new CourseCorrection(core)); } status = Localizer.Format("#MechJeb_LandingGuidance_Status7");//"Doing high deorbit burn" } else { core.attitude.attitudeTo(Vector3d.back, AttitudeReference.ORBIT, core.landing); if (core.node.autowarp) { core.warp.WarpRegularAtRate((float)(orbit.period / 10)); } status = Localizer.Format("#MechJeb_LandingGuidance_Status8");//"Moving to high deorbit burn point" } return(this); }