From abbed5c1d7e9dab3908c7d5d92a95b4404bd017f Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Wed, 30 Apr 2025 20:20:32 -0400 Subject: [PATCH 01/14] Update LunarTransferPlanner.cs https://github.com/KSP-RO/LunarTransferPlanner/commit/2478116081e790894f9a45d349512fb61c9ae141 --- LunarTransferPlanner.cs | 288 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 285 insertions(+), 3 deletions(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index bcf3b46..2df377f 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -63,6 +63,20 @@ public static float Floor(float x, int digits) } return (float)((int)(x * lol)) / lol; } + + // Math.Acosh does not seem to work in .NET 4 + public static double Acosh(double x) + { + if (x >= 1) + { + return Math.Log(x + Math.Sqrt(x*x - 1)); + } + else + { + return Double.NaN; + } + + } } [KSPAddon(KSPAddon.Startup.FlightAndKSC, false)] @@ -72,8 +86,12 @@ public class LunarTransferPlanner : DaMichelToolbarSuperWrapper.PluginWithToolba Rect windowRect = new Rect(100,100,-1,-1); bool isWindowOpen = true; // gui stuff + const float tliAltitudeKM = 200f; // Parking orbit altitude (circular orbit assumed). Not so important to be in the GUI. float flightTime = 4f; float nextTickFT = 0f; + bool showParking0 = false; // Expand/collapse time in parking orbit for launch now + bool showParking1 = false; // Expand/collapse time in parking orbit for first window + bool showParking2 = false; // Expand/collapse time in parking orbit for second window float warpMargin = 60f; float nextTickWM = 0f; string windowTitle = ""; @@ -383,6 +401,201 @@ private double EstimateLaunchTime(CelestialBody target, Vector3d launchPos, doub return t; } + private double EstimateFlightTimeAfterTLI(CelestialBody target, double dV, bool movingTarget = true) + { + CelestialBody mainBody = target.referenceBody; + double gravParameter = mainBody.gravParameter; + + // The formulas are from http://www.braeunig.us/space/orbmech.htm + + // Assuming that the TLI is performed from a circular orbit with altitude = tliAltitudeKM + // Radius of the orbit, including the radius of the Earth + double r0 = mainBody.Radius + tliAltitudeKM * 1000; + + // Orbital velocity after TLI + double v0 = Math.Sqrt(gravParameter / r0) + dV; + + // Eccentricity after TLI (not the full formula, this is correct only at the periapsis) + double e = r0 * v0*v0 / gravParameter - 1; + + // e == 1 would mean that the orbit is parabolic. No idea which formulas are applicable in this case. + // But it's so unlikely that I will just cheat and make such orbits slightly hyperbolic. + if (e == 1) + { + // Increase velocity after TLI by 0.1 m/s + v0 = v0 + 0.1; + // Recalculate eccentricity + e = r0 * v0*v0 / gravParameter - 1; + } + + // Semi-major axis after TLI + double a = 1 / (2/r0 - v0*v0 / gravParameter); + + // Altitude of the Moon at the time of the TLI + double r1 = 0; + + // The Moon is moving, so we need to know its altitude when the probe arrives + // For that we need to know the flight time, which is being calculated here in the first place + // It can be done in two steps: + // 1) make a recursive call of EstimateFlightTimeAfterTLI to find out the approximate flight time, + // based on the Moon's current altitude + // 2) use the Moon's altitude at this approximate time for further calculations + if (movingTarget) + { + // This is the "normal" call of EstimateFlightTimeAfterTLI from outside + + // Making the recursive call of EstimateFlightTimeAfterTLI with movingTarget = false + double approxFlightTime = EstimateFlightTimeAfterTLI(target, dV, false); + + // Altitude of the Moon at the approximate time of the TLI + r1 = target.orbit.GetRadiusAtUT(Planetarium.GetUniversalTime() + approxFlightTime); + } + else + { + // This is the recursive call of EstimateFlightTimeAfterTLI + + // Altitude of the Moon now + r1 = target.orbit.GetRadiusAtUT(Planetarium.GetUniversalTime()); + } + + // True anomaly when the vessel reaches the altitude of the Moon (r1) + double trueAnomaly1 = Math.Acos( (a * (1 - e*e) - r1) / (e * r1) ); + + // Time until the vessel reaches the altitude of the Moon (r1) + double t1 = 0; + + // Elliptic orbit after TLI + if (e < 1) + { + // Eccentric Anomaly when the vessel reaches the altitude of the Moon + double eccAnomaly1 = Math.Acos( (e + Math.Cos(trueAnomaly1)) / (1 + e * Math.Cos(trueAnomaly1)) ); + double meanAnomaly1 = eccAnomaly1 - e * Math.Sin(eccAnomaly1); + t1 = meanAnomaly1 / Math.Sqrt( gravParameter / (a*a*a) ); + } + + // Parabolic orbit (e == 1) has been prevented earlier + + // Hyperbolic orbit + if (e > 1) + { + // Hyperbolic Eccentric Anomaly when the vessel reaches the altitude of the Moon + // Can't use Math.Acosh, it does not seem to work in .NET 4 + double hEccAnomaly1 = Util.Acosh( (e + Math.Cos(trueAnomaly1)) / (1 + e * Math.Cos(trueAnomaly1)) ); + + t1 = Math.Sqrt( ((-a)*(-a)*(-a)) / gravParameter ) * (e * Math.Sinh(hEccAnomaly1) - hEccAnomaly1); + } + + return t1; + } + + private double EstimateFlightTimeBeforeTLI(CelestialBody target, Vector3d launchPos, double delayTime) + { + CelestialBody mainBody = target.referenceBody; + double gravParameter = mainBody.gravParameter; + + // This code is copied from CalcOrbitForTime + Vector3d EarthPos = mainBody.position; + Vector3d EarthAxis = mainBody.angularVelocity.normalized; + + double targetTime = Planetarium.GetUniversalTime() + flightTime * 24d * 3600d + delayTime; + Vector3d targetPos = target.getPositionAtUT(targetTime); + + Vector3d upVector = Quaternion.AngleAxis((float)(delayTime * 360d / mainBody.rotationPeriod), EarthAxis) * (launchPos - EarthPos).normalized; + // end of copied code + + // TLI takes place at the point of the orbit that is opposite to the future position of the Moon + Vector3d tliUpVector = (EarthPos - targetPos).normalized; + + // Need to calculate 0-360 degree angle in prograde direction between upVector at launch and at the time of the TLI. + float rotationAngle; + Vector3 rotationAxis; + Quaternion rotationFromLaunchToTLI = new Quaternion(); + rotationFromLaunchToTLI.SetFromToRotation(upVector, tliUpVector); + rotationFromLaunchToTLI.ToAngleAxis(out rotationAngle, out rotationAxis); + // Sometimes this rotation is prograde, sometimes retrograde. We need only prograde rotation. + if (Vector3d.Dot(rotationAxis.normalized, EarthAxis) > 0) + { + // rotationAxis is pointing roughly in the direction of Earth rotation axis => rotation is prograde => do nothing + } + else + { + // rotationAxis is pointing roughly opposite to Earth rotation axis => rotation is retrograde => rotate the other way + rotationAngle = (-rotationAngle) % 360; + } + + // The angle is converted to time in orbit + double orbitRadius = mainBody.Radius + tliAltitudeKM * 1000; + double orbitPeriod = 2 * Math.PI * Math.Sqrt( (orbitRadius*orbitRadius*orbitRadius) / gravParameter ); + double flightTimeBeforeTLI = rotationAngle / 360 * orbitPeriod; + + // Launch and maneuver planning take some time, say 15 minutes. If time to TLI is less than that, add an orbit. + if (flightTimeBeforeTLI < 15*60) + { + flightTimeBeforeTLI += orbitPeriod; + } + + return flightTimeBeforeTLI; + } + + private double EstimateDV(CelestialBody target, Vector3d launchPos) + { + float dV = float.NaN; + + // Search in this range + const float minPossibleDV = 3000; + const float maxPossibleDV = 4200; + + // Current search range, will be gradually narrowed + float lowerBound = minPossibleDV; + float upperBound = maxPossibleDV; + + // Max. 16 attempts, then return whatever value was found + for (int i = 0; i < 16; i++) + { + // guess dV + dV = (lowerBound + upperBound) / 2; + + // calculate flight time for this dV + double flightTimeAfterTLI = EstimateFlightTimeAfterTLI(target, dV); + double flightTimeBeforeTLI = EstimateFlightTimeBeforeTLI(target, launchPos, 0d); + double estimatedFlightTime = flightTimeBeforeTLI + flightTimeAfterTLI; + + // Debug.Log(i + " " + dV + " " + flightTime + " " + flightTimeBeforeTLI + " " + flightTimeAfterTLI + " " + estimatedFlightTime); + + if (Double.IsNaN(flightTimeAfterTLI)) + { + // dV is so low that target is unreachable, set lower bound to current guess and try again + lowerBound = dV; + continue; + } + else if (estimatedFlightTime > (flightTime * (24*60*60) + 60)) + { + // dV is too low, set lower bound to current guess and try again + lowerBound = dV; + continue; + } + else if (estimatedFlightTime < (flightTime * (24*60*60) - 60)) + { + // dV is too high, set upper bound to current guess and try again + upperBound = dV; + continue; + } + else + { + // correct flight time with this dV + break; + } + } + + if (dV == minPossibleDV || dV == maxPossibleDV) + { + // dV is incorrect, the correct value is outside the initial search range + dV = float.NaN; + } + + return dV; + } + private string FormatTime(double t) { t = Math.Round(t); @@ -422,23 +635,92 @@ void MakeMainWindow(int id) double firstLaunchETA = EstimateLaunchTime(target, launchPos, latitude, 0d); double secondLaunchETA = EstimateLaunchTime(target, launchPos, latitude, firstLaunchETA + 3600d); + double dV = EstimateDV(target, launchPos); + GUILayout.Space(4); + GUILayout.Label("Required dV", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(String.Format("{0:0 m/s}", dV), "Required dV for the selected flight time"), GUILayout.MinWidth(100)); + + GUILayout.Space(4); + GUILayout.BeginHorizontal(); GUILayout.Label("Launch Now Incl", GUILayout.ExpandWidth(true)); - GUILayout.Box(new GUIContent($"{(launchOrbit.azimuth > 90d ? -launchOrbit.inclination : launchOrbit.inclination):F2}°", + bool showParking0_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); + GUILayout.EndHorizontal(); + + GUILayout.Space(4); + GUILayout.Box(new GUIContent($"{(launchOrbit.azimuth > 90d ? -launchOrbit.inclination : launchOrbit.inclination):F2}\u00B0", "Launch to this inclination now to reach a Lunar parking orbit"), GUILayout.MinWidth(100)); string tooltip = latitude >= target.orbit.inclination ? "Launch at this time for an Easterly launch to Lunar parking orbit" : "Launch at this time for a low inclination launch to Lunar parking orbit"; + if (showParking0_pressed) + { + showParking0 = !showParking0; + // Doing this forces the window to be resized + // Without it, the window will become bigger when controls expand, but never become smaller again + windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); + } + + if (showParking0) + { + double timeInOrbit0 = EstimateFlightTimeBeforeTLI(target, launchPos, 0d); + GUILayout.Space(4); + GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(FormatTime(timeInOrbit0), ""), GUILayout.MinWidth(100)); + } + + GUILayout.Space(4); + GUILayout.BeginHorizontal(); + GUILayout.Label("First Window ", GUILayout.ExpandWidth(true)); + bool showParking1_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); + GUILayout.EndHorizontal(); + GUILayout.Space(4); - GUILayout.Label("First Window", GUILayout.ExpandWidth(true)); GUILayout.Box(new GUIContent(FormatTime(firstLaunchETA), tooltip), GUILayout.MinWidth(100)); + if (showParking1_pressed) + { + showParking1 = !showParking1; + // Doing this forces the window to be resized + // Without it, the window will become bigger when controls expand, but never become smaller again + windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); + } + + if (showParking1) + { + double timeInOrbit1 = EstimateFlightTimeBeforeTLI(target, launchPos, firstLaunchETA); + GUILayout.Space(4); + GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(FormatTime(timeInOrbit1), ""), GUILayout.MinWidth(100)); + } + GUILayout.Space(4); + GUILayout.BeginHorizontal(); GUILayout.Label("Second Window", GUILayout.ExpandWidth(true)); + bool showParking2_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); + GUILayout.EndHorizontal(); + + GUILayout.Space(4); GUILayout.Box(new GUIContent(FormatTime(secondLaunchETA), tooltip), GUILayout.MinWidth(100)); + if (showParking2_pressed) + { + showParking2 = !showParking2; + // Doing this forces the window to be resized + // Without it, the window will become bigger when controls expand, but never become smaller again + windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); + } + + if (showParking2) + { + double timeInOrbit2 = EstimateFlightTimeBeforeTLI(target, launchPos, secondLaunchETA); + GUILayout.Space(4); + GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(FormatTime(timeInOrbit2), "")); + } + GUILayout.Label("Warp Margin (sec)", GUILayout.ExpandWidth(true)); MakeNumberEditField(ref warpMargin, ref nextTickWM, 0, 5f, 0f); @@ -477,7 +759,7 @@ void MakeMainWindow(int id) { TimeWarp.fetch.WarpTo(Planetarium.GetUniversalTime() + firstLaunchETA - warpMargin); } - } + } } GUILayout.EndVertical(); From fe3e6874999df2501cd3d98cdefbcd056a94d78f Mon Sep 17 00:00:00 2001 From: Clayel Date: Wed, 30 Apr 2025 20:30:08 -0400 Subject: [PATCH 02/14] merge nazfib (https://github.com/KSP-RO/LunarTransferPlanner/commit/302f3192c742531ce11fd0c87b48f6f8a9f3559d) --- LunarTransferPlanner.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index 2df377f..10ce169 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -287,20 +287,23 @@ private OrbitData CalcOrbitForTime(CelestialBody target, Vector3d launchPos, dou double targetTime = Planetarium.GetUniversalTime() + flightTime * 24d * 3600d + delayTime; Vector3d targetPos = target.getPositionAtUT(targetTime); - Vector3d upVector = Quaternion.AngleAxis((float)(delayTime * 360d / mainBody.rotationPeriod), EarthAxis) * (launchPos - EarthPos).normalized; + Vector3d upVector = QuaternionD.AngleAxis(delayTime * 360d / mainBody.rotationPeriod, EarthAxis) * (launchPos - EarthPos).normalized; Vector3d orbitNorm = Vector3d.Cross(targetPos - EarthPos, upVector).normalized; - double inclination = Math.Acos(Vector3d.Dot(orbitNorm, mainBody.angularVelocity.normalized)); + double inclination = Math.Acos(Vector3d.Dot(orbitNorm, EarthAxis)); if (inclination > Math.PI / 2) + { inclination = Math.PI - inclination; + orbitNorm *= -1; // make sure orbitNorm always points roughly northwards + } - Vector3d eastVec = Vector3d.Cross(EarthAxis, upVector).normalized; + // When checking this: remember that Unity (and KSP) use a left-handed coordinate system; therefore, the + // cross product follows the left-hand rule. + Vector3d eastVec = Vector3d.Cross(upVector, EarthAxis).normalized; Vector3d northVec = Vector3d.Cross(eastVec, upVector).normalized; Vector3d launchVec = Vector3d.Cross(upVector, orbitNorm).normalized; double azimuth = Math.Acos(Vector3d.Dot(launchVec, northVec)); - if (Vector3d.Dot(launchVec, eastVec) < 0d) - azimuth = Math.PI - azimuth; return new OrbitData(orbitNorm, inclination * 180d / Math.PI, azimuth * 180d / Math.PI); } @@ -311,7 +314,7 @@ private double EstimateLaunchTime(CelestialBody target, Vector3d launchPos, doub double t = startTime; OrbitData launchOrbit = CalcOrbitForTime(target, launchPos, t); - if (latitude >= target.orbit.inclination) + if (Math.Abs(latitude) >= target.orbit.inclination) { // High latitude path - find the next easterly launch to the target while (Math.Abs(launchOrbit.azimuth - targetAz) > 0.01d) @@ -651,7 +654,7 @@ void MakeMainWindow(int id) GUILayout.Box(new GUIContent($"{(launchOrbit.azimuth > 90d ? -launchOrbit.inclination : launchOrbit.inclination):F2}\u00B0", "Launch to this inclination now to reach a Lunar parking orbit"), GUILayout.MinWidth(100)); - string tooltip = latitude >= target.orbit.inclination ? + string tooltip = Math.Abs(latitude) >= target.orbit.inclination ? "Launch at this time for an Easterly launch to Lunar parking orbit" : "Launch at this time for a low inclination launch to Lunar parking orbit"; From f532977f1d7d9ddf1f48dda0ee6992481b6315e4 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Wed, 30 Apr 2025 21:07:14 -0400 Subject: [PATCH 03/14] Update LunarTransferPlanner.cs --- LunarTransferPlanner.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index 10ce169..e48a3c9 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -644,6 +644,10 @@ void MakeMainWindow(int id) GUILayout.Label("Required dV", GUILayout.ExpandWidth(true)); GUILayout.Box(new GUIContent(String.Format("{0:0 m/s}", dV), "Required dV for the selected flight time"), GUILayout.MinWidth(100)); + GUILayout.Space(4); + GUILayout.Label("Latitude", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(String.Format("{0:0.00}\u00B0", latitude), "Latitude of current launch site"), GUILayout.MinWidth(100)); + GUILayout.Space(4); GUILayout.BeginHorizontal(); GUILayout.Label("Launch Now Incl", GUILayout.ExpandWidth(true)); From b2de6c2e5efbd0cfe44e5fb33a637133ef21a79b Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Wed, 30 Apr 2025 21:47:39 -0400 Subject: [PATCH 04/14] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e783d8..6be202f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ Lunar Transfer Planner ======================================== Provides a GUI for Lunar transfer planning for RSS. -Select the expecteed flight time to the Moon, i.e. the time from leaving Earth orbit to Lunar periapsis, this defaults to 4 days. -The correct launch inclination for immediate launch is shown. The next two windows for a minimum inclination (i.e. Easterly) launch are also given. +Select the expected flight time to the Moon, i.e. the time from leaving Earth orbit to Lunar periapsis, this defaults to 4 days. The delta-V required for this maneuver is also given. +The correct launch inclination for immediate launch is shown, and your current latitude is shown for reference. The first two windows for a minimum inclination (i.e. Easterly) launch are also given, along with the time in parking orbit that you will spend while waiting for the lunar transfer. Forum Thread: From a2674baae47ce611d7370718c8bf360122cf97e7 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Wed, 30 Apr 2025 21:51:49 -0400 Subject: [PATCH 05/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6be202f..718a2e7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Lunar Transfer Planner ======================================== -Provides a GUI for Lunar transfer planning for RSS. +Provides a GUI for planning a Lunar transfer for RSS. Select the expected flight time to the Moon, i.e. the time from leaving Earth orbit to Lunar periapsis, this defaults to 4 days. The delta-V required for this maneuver is also given. The correct launch inclination for immediate launch is shown, and your current latitude is shown for reference. The first two windows for a minimum inclination (i.e. Easterly) launch are also given, along with the time in parking orbit that you will spend while waiting for the lunar transfer. From eaa9480d863cda8bf247a8dd997e0d1c99966c12 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Thu, 1 May 2025 21:49:52 -0400 Subject: [PATCH 06/14] add digits change --- LunarTransferPlanner.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index e48a3c9..6fe52f4 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -184,10 +184,10 @@ void OnGUI() } } - private void MakeNumberEditField(ref float value, ref float nextTick, int digits, float step, float minValue) + private void MakeNumberEditField(ref float value, ref float nextTick, float step, float minValue) { GUILayout.BeginHorizontal(); - string textValue = value.ToString("F" + digits.ToString()); + string textValue = value.ToString(); string newlabel = GUILayout.TextField(textValue, GUILayout.MinWidth(40)); if (textValue != newlabel) { @@ -621,7 +621,7 @@ void MakeMainWindow(int id) GUILayout.Space(4); GUILayout.Label("Flight Time (days)", GUILayout.ExpandWidth(true)); - MakeNumberEditField(ref flightTime, ref nextTickFT, 1, 0.1f, 0.1f); + MakeNumberEditField(ref flightTime, ref nextTickFT, 0.1f, 0.1f); CelestialBody target = FlightGlobals.fetch.bodies.FirstOrDefault(body => body.name.Equals("Moon", StringComparison.OrdinalIgnoreCase)); if (target == null) @@ -729,7 +729,7 @@ void MakeMainWindow(int id) } GUILayout.Label("Warp Margin (sec)", GUILayout.ExpandWidth(true)); - MakeNumberEditField(ref warpMargin, ref nextTickWM, 0, 5f, 0f); + MakeNumberEditField(ref warpMargin, ref nextTickWM, 5f, 0f); GUILayout.Space(2); GUILayout.BeginHorizontal(); From 9dfc67a3c0ee26b111db840efc57608325a04285 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Sun, 11 May 2025 13:57:46 -0400 Subject: [PATCH 07/14] add parking orbit changer --- LunarTransferPlanner.cs | 348 ++++++++++++++++++++-------------------- 1 file changed, 178 insertions(+), 170 deletions(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index 6fe52f4..be1f5ae 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -63,19 +63,19 @@ public static float Floor(float x, int digits) } return (float)((int)(x * lol)) / lol; } - + // Math.Acosh does not seem to work in .NET 4 public static double Acosh(double x) { - if (x >= 1) + if (x >= 1) { - return Math.Log(x + Math.Sqrt(x*x - 1)); + return Math.Log(x + Math.Sqrt(x * x - 1)); } else { return Double.NaN; } - + } } @@ -83,24 +83,26 @@ public static double Acosh(double x) public class LunarTransferPlanner : DaMichelToolbarSuperWrapper.PluginWithToolbarSupport { // main window - Rect windowRect = new Rect(100,100,-1,-1); - bool isWindowOpen = true; + Rect windowRect = new Rect(100, 100, -1, -1); + bool isWindowOpen = true; // gui stuff - const float tliAltitudeKM = 200f; // Parking orbit altitude (circular orbit assumed). Not so important to be in the GUI. - float flightTime = 4f; - float nextTickFT = 0f; - bool showParking0 = false; // Expand/collapse time in parking orbit for launch now - bool showParking1 = false; // Expand/collapse time in parking orbit for first window - bool showParking2 = false; // Expand/collapse time in parking orbit for second window - float warpMargin = 60f; - float nextTickWM = 0f; - string windowTitle = ""; - GUISkin skin; - -#region boring stuff + float flightTime = 4f; + float nextTickFT = 0f; + float tliAltitudeKM = 200f; // Parking orbit altitude (circular orbit assumed). + float nextTickTA = 0f; + bool showParking0 = false; // Expand/collapse time in parking orbit for launch now + bool showParking1 = false; // Expand/collapse time in parking orbit for first window + bool showParking2 = false; // Expand/collapse time in parking orbit for second window + float warpMargin = 60f; + float nextTickWM = 0f; + string windowTitle = ""; + GUISkin skin; + + #region boring stuff protected override DaMichelToolbarSuperWrapper.ToolbarInfo GetToolbarInfo() { - return new DaMichelToolbarSuperWrapper.ToolbarInfo { + return new DaMichelToolbarSuperWrapper.ToolbarInfo + { name = "LunarTransferPlanner", tooltip = "LunarTransferPlanner Show/Hide Gui", toolbarTexture = "LunarTransferPlanner/toolbarbutton", @@ -116,8 +118,8 @@ void Awake() skin.button.margin = new RectOffset(1, 1, 1, 1); skin.box.padding = new RectOffset(2, 2, 2, 2); skin.box.margin = new RectOffset(1, 1, 1, 1); - skin.textField.margin = new RectOffset(3,1,1,1); - skin.textField.padding = new RectOffset(4,2,1,0); + skin.textField.margin = new RectOffset(3, 1, 1, 1); + skin.textField.padding = new RectOffset(4, 2, 1, 0); LoadSettings(); InitializeToolbars(); @@ -136,7 +138,7 @@ public void OnDestroy() } - protected override void OnGuiVisibilityChange() + protected override void OnGuiVisibilityChange() { isWindowOpen = isGuiVisible; } @@ -150,6 +152,7 @@ void SaveSettings() settings.AddValue("windowRect.xMin", windowRect.xMin); settings.AddValue("windowRect.yMin", windowRect.yMin); settings.AddValue("flightTime", flightTime); + settings.AddValue("tliAltitudeKM", tliAltitudeKM); settings.AddValue("warpMargin", warpMargin); settings.Save(AssemblyLoader.loadedAssemblies.GetPathByType(typeof(LunarTransferPlanner)) + "/settings.cfg"); } @@ -164,13 +167,14 @@ void LoadSettings() Util.TryReadValue(ref x, settings, "windowRect.xMin"); Util.TryReadValue(ref y, settings, "windowRect.yMin"); Util.TryReadValue(ref flightTime, settings, "flightTime"); + Util.TryReadValue(ref tliAltitudeKM, settings, "tliAltitudeKM"); Util.TryReadValue(ref warpMargin, settings, "warpMargin"); windowRect = new Rect(x, y, windowRect.width, windowRect.height); LoadMutableToolbarSettings(settings); LoadImmutableToolbarSettings(settings); } } -#endregion + #endregion void OnGUI() { @@ -178,8 +182,8 @@ void OnGUI() { GUI.skin = this.skin; windowRect = GUILayout.Window(this.GetHashCode(), windowRect, MakeMainWindow, windowTitle); - float left = Mathf.Clamp(windowRect.x, 0, Screen.width-windowRect.width); - float top = Mathf.Clamp(windowRect.y, 0, Screen.height-windowRect.height); + float left = Mathf.Clamp(windowRect.x, 0, Screen.width - windowRect.width); + float top = Mathf.Clamp(windowRect.y, 0, Screen.height - windowRect.height); windowRect = new Rect(left, top, windowRect.width, windowRect.height); } } @@ -198,7 +202,7 @@ private void MakeNumberEditField(ref float value, ref float nextTick, float step } } bool hitMinusButton = GUILayout.RepeatButton("-", GUILayout.MinWidth(16)); - bool hitPlusButton = GUILayout.RepeatButton("+", GUILayout.MinWidth(16)); + bool hitPlusButton = GUILayout.RepeatButton("+", GUILayout.MinWidth(16)); if (hitPlusButton || hitMinusButton) { float tick = Time.realtimeSinceStartup; @@ -273,8 +277,8 @@ public OrbitData(Vector3d n, double i, double a) } public readonly Vector3d normal; - public readonly double inclination; - public readonly double azimuth; + public readonly double inclination; + public readonly double azimuth; } private OrbitData CalcOrbitForTime(CelestialBody target, Vector3d launchPos, double delayTime) @@ -326,7 +330,7 @@ private double EstimateLaunchTime(CelestialBody target, Vector3d launchPos, doub OrbitData nextOrbit = CalcOrbitForTime(target, launchPos, t + tStep); double gradient = (nextOrbit.azimuth - launchOrbit.azimuth); - + if (Math.Abs(gradient) > 100d) { // Wrapping from north to south, short step and recalc. @@ -354,7 +358,7 @@ private double EstimateLaunchTime(CelestialBody target, Vector3d launchPos, doub t += tStep * (0 - launchOrbit.azimuth) / (wrapMargin * gradient); } } - + launchOrbit = CalcOrbitForTime(target, launchPos, t); } } @@ -363,7 +367,7 @@ private double EstimateLaunchTime(CelestialBody target, Vector3d launchPos, doub double tStep = 1200d; double startGradient; - for (;;) + for (; ; ) { OrbitData nextOrbit = CalcOrbitForTime(target, launchPos, t + tStep); startGradient = (nextOrbit.azimuth - launchOrbit.azimuth); @@ -419,21 +423,21 @@ private double EstimateFlightTimeAfterTLI(CelestialBody target, double dV, bool double v0 = Math.Sqrt(gravParameter / r0) + dV; // Eccentricity after TLI (not the full formula, this is correct only at the periapsis) - double e = r0 * v0*v0 / gravParameter - 1; + double e = r0 * v0 * v0 / gravParameter - 1; // e == 1 would mean that the orbit is parabolic. No idea which formulas are applicable in this case. // But it's so unlikely that I will just cheat and make such orbits slightly hyperbolic. - if (e == 1) + if (e == 1) { // Increase velocity after TLI by 0.1 m/s - v0 = v0 + 0.1; + v0 = v0 + 0.1; // Recalculate eccentricity - e = r0 * v0*v0 / gravParameter - 1; + e = r0 * v0 * v0 / gravParameter - 1; } // Semi-major axis after TLI - double a = 1 / (2/r0 - v0*v0 / gravParameter); - + double a = 1 / (2 / r0 - v0 * v0 / gravParameter); + // Altitude of the Moon at the time of the TLI double r1 = 0; @@ -462,30 +466,30 @@ private double EstimateFlightTimeAfterTLI(CelestialBody target, double dV, bool } // True anomaly when the vessel reaches the altitude of the Moon (r1) - double trueAnomaly1 = Math.Acos( (a * (1 - e*e) - r1) / (e * r1) ); + double trueAnomaly1 = Math.Acos((a * (1 - e * e) - r1) / (e * r1)); // Time until the vessel reaches the altitude of the Moon (r1) double t1 = 0; // Elliptic orbit after TLI - if (e < 1) + if (e < 1) { // Eccentric Anomaly when the vessel reaches the altitude of the Moon - double eccAnomaly1 = Math.Acos( (e + Math.Cos(trueAnomaly1)) / (1 + e * Math.Cos(trueAnomaly1)) ); + double eccAnomaly1 = Math.Acos((e + Math.Cos(trueAnomaly1)) / (1 + e * Math.Cos(trueAnomaly1))); double meanAnomaly1 = eccAnomaly1 - e * Math.Sin(eccAnomaly1); - t1 = meanAnomaly1 / Math.Sqrt( gravParameter / (a*a*a) ); + t1 = meanAnomaly1 / Math.Sqrt(gravParameter / (a * a * a)); } // Parabolic orbit (e == 1) has been prevented earlier // Hyperbolic orbit - if (e > 1) + if (e > 1) { // Hyperbolic Eccentric Anomaly when the vessel reaches the altitude of the Moon // Can't use Math.Acosh, it does not seem to work in .NET 4 - double hEccAnomaly1 = Util.Acosh( (e + Math.Cos(trueAnomaly1)) / (1 + e * Math.Cos(trueAnomaly1)) ); + double hEccAnomaly1 = Util.Acosh((e + Math.Cos(trueAnomaly1)) / (1 + e * Math.Cos(trueAnomaly1))); - t1 = Math.Sqrt( ((-a)*(-a)*(-a)) / gravParameter ) * (e * Math.Sinh(hEccAnomaly1) - hEccAnomaly1); + t1 = Math.Sqrt(((-a) * (-a) * (-a)) / gravParameter) * (e * Math.Sinh(hEccAnomaly1) - hEccAnomaly1); } return t1; @@ -525,14 +529,14 @@ private double EstimateFlightTimeBeforeTLI(CelestialBody target, Vector3d launch // rotationAxis is pointing roughly opposite to Earth rotation axis => rotation is retrograde => rotate the other way rotationAngle = (-rotationAngle) % 360; } - + // The angle is converted to time in orbit double orbitRadius = mainBody.Radius + tliAltitudeKM * 1000; - double orbitPeriod = 2 * Math.PI * Math.Sqrt( (orbitRadius*orbitRadius*orbitRadius) / gravParameter ); + double orbitPeriod = 2 * Math.PI * Math.Sqrt((orbitRadius * orbitRadius * orbitRadius) / gravParameter); double flightTimeBeforeTLI = rotationAngle / 360 * orbitPeriod; // Launch and maneuver planning take some time, say 15 minutes. If time to TLI is less than that, add an orbit. - if (flightTimeBeforeTLI < 15*60) + if (flightTimeBeforeTLI < 15 * 60) { flightTimeBeforeTLI += orbitPeriod; } @@ -547,13 +551,13 @@ private double EstimateDV(CelestialBody target, Vector3d launchPos) // Search in this range const float minPossibleDV = 3000; const float maxPossibleDV = 4200; - + // Current search range, will be gradually narrowed float lowerBound = minPossibleDV; float upperBound = maxPossibleDV; // Max. 16 attempts, then return whatever value was found - for (int i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) { // guess dV dV = (lowerBound + upperBound) / 2; @@ -562,28 +566,28 @@ private double EstimateDV(CelestialBody target, Vector3d launchPos) double flightTimeAfterTLI = EstimateFlightTimeAfterTLI(target, dV); double flightTimeBeforeTLI = EstimateFlightTimeBeforeTLI(target, launchPos, 0d); double estimatedFlightTime = flightTimeBeforeTLI + flightTimeAfterTLI; - + // Debug.Log(i + " " + dV + " " + flightTime + " " + flightTimeBeforeTLI + " " + flightTimeAfterTLI + " " + estimatedFlightTime); - + if (Double.IsNaN(flightTimeAfterTLI)) { // dV is so low that target is unreachable, set lower bound to current guess and try again lowerBound = dV; continue; } - else if (estimatedFlightTime > (flightTime * (24*60*60) + 60)) + else if (estimatedFlightTime > (flightTime * (24 * 60 * 60) + 60)) { // dV is too low, set lower bound to current guess and try again lowerBound = dV; continue; } - else if (estimatedFlightTime < (flightTime * (24*60*60) - 60)) + else if (estimatedFlightTime < (flightTime * (24 * 60 * 60) - 60)) { // dV is too high, set upper bound to current guess and try again upperBound = dV; continue; } - else + else { // correct flight time with this dV break; @@ -617,157 +621,161 @@ void MakeMainWindow(int id) { GUILayout.BeginVertical(); - windowTitle = "Lunar Transfer"; + windowTitle = "Lunar Transfer"; + CelestialBody target = FlightGlobals.fetch.bodies.FirstOrDefault(body => body.name.Equals("Moon", StringComparison.OrdinalIgnoreCase)); + if (target == null) + { + GUILayout.Space(4); + GUILayout.Box("ERROR: Cannot find the Moon", GUILayout.MinWidth(80)); + } + else + { GUILayout.Space(4); GUILayout.Label("Flight Time (days)", GUILayout.ExpandWidth(true)); MakeNumberEditField(ref flightTime, ref nextTickFT, 0.1f, 0.1f); - CelestialBody target = FlightGlobals.fetch.bodies.FirstOrDefault(body => body.name.Equals("Moon", StringComparison.OrdinalIgnoreCase)); - if (target == null) - { - GUILayout.Space(4); - GUILayout.Box("Cannot find the Moon", GUILayout.MinWidth(80)); - } - else - { - double latitude = 0d; - Vector3d launchPos = GetLaunchPos(target.referenceBody, ref latitude); + GUILayout.Space(4); + GUILayout.Label("Circ. Parking Orbit (km)", GUILayout.ExpandWidth(true)); + MakeNumberEditField(ref tliAltitudeKM, ref nextTickTA, 5.0f, 140.0f); - OrbitData launchOrbit = CalcOrbitForTime(target, launchPos, 0d); - double firstLaunchETA = EstimateLaunchTime(target, launchPos, latitude, 0d); - double secondLaunchETA = EstimateLaunchTime(target, launchPos, latitude, firstLaunchETA + 3600d); + double latitude = 0d; + Vector3d launchPos = GetLaunchPos(target.referenceBody, ref latitude); - double dV = EstimateDV(target, launchPos); + OrbitData launchOrbit = CalcOrbitForTime(target, launchPos, 0d); + double firstLaunchETA = EstimateLaunchTime(target, launchPos, latitude, 0d); + double secondLaunchETA = EstimateLaunchTime(target, launchPos, latitude, firstLaunchETA + 3600d); - GUILayout.Space(4); - GUILayout.Label("Required dV", GUILayout.ExpandWidth(true)); - GUILayout.Box(new GUIContent(String.Format("{0:0 m/s}", dV), "Required dV for the selected flight time"), GUILayout.MinWidth(100)); + double dV = EstimateDV(target, launchPos); - GUILayout.Space(4); - GUILayout.Label("Latitude", GUILayout.ExpandWidth(true)); - GUILayout.Box(new GUIContent(String.Format("{0:0.00}\u00B0", latitude), "Latitude of current launch site"), GUILayout.MinWidth(100)); + GUILayout.Space(4); + GUILayout.Label("Required dV", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(String.Format("{0:0 m/s}", dV), "Required dV for the selected flight time"), GUILayout.MinWidth(100)); - GUILayout.Space(4); - GUILayout.BeginHorizontal(); - GUILayout.Label("Launch Now Incl", GUILayout.ExpandWidth(true)); - bool showParking0_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); - GUILayout.EndHorizontal(); + GUILayout.Space(4); + GUILayout.Label("Latitude", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(String.Format("{0:0.00}\u00B0", latitude), "Latitude of current launch site"), GUILayout.MinWidth(100)); - GUILayout.Space(4); - GUILayout.Box(new GUIContent($"{(launchOrbit.azimuth > 90d ? -launchOrbit.inclination : launchOrbit.inclination):F2}\u00B0", - "Launch to this inclination now to reach a Lunar parking orbit"), GUILayout.MinWidth(100)); + GUILayout.Space(4); + GUILayout.BeginHorizontal(); + GUILayout.Label("Launch Now Incl.", GUILayout.ExpandWidth(true)); + bool showParking0_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); + GUILayout.EndHorizontal(); - string tooltip = Math.Abs(latitude) >= target.orbit.inclination ? - "Launch at this time for an Easterly launch to Lunar parking orbit" : - "Launch at this time for a low inclination launch to Lunar parking orbit"; + GUILayout.Space(4); + GUILayout.Box(new GUIContent($"{(launchOrbit.azimuth > 90d ? -launchOrbit.inclination : launchOrbit.inclination):F2}\u00B0", + "Launch to this inclination now to reach a Lunar parking orbit"), GUILayout.MinWidth(100)); - if (showParking0_pressed) - { - showParking0 = !showParking0; - // Doing this forces the window to be resized - // Without it, the window will become bigger when controls expand, but never become smaller again - windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); - } + string tooltip = Math.Abs(latitude) >= target.orbit.inclination ? + "Launch at this time for an Easterly launch to Lunar parking orbit" : + "Launch at this time for a low inclination launch to Lunar parking orbit"; - if (showParking0) - { - double timeInOrbit0 = EstimateFlightTimeBeforeTLI(target, launchPos, 0d); - GUILayout.Space(4); - GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); - GUILayout.Box(new GUIContent(FormatTime(timeInOrbit0), ""), GUILayout.MinWidth(100)); - } + if (showParking0_pressed) + { + showParking0 = !showParking0; + // Doing this forces the window to be resized + // Without it, the window will become bigger when controls expand, but never become smaller again + windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); + } + if (showParking0) + { + double timeInOrbit0 = EstimateFlightTimeBeforeTLI(target, launchPos, 0d); GUILayout.Space(4); - GUILayout.BeginHorizontal(); - GUILayout.Label("First Window ", GUILayout.ExpandWidth(true)); - bool showParking1_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); - GUILayout.EndHorizontal(); + GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(FormatTime(timeInOrbit0), ""), GUILayout.MinWidth(100)); + } - GUILayout.Space(4); - GUILayout.Box(new GUIContent(FormatTime(firstLaunchETA), tooltip), GUILayout.MinWidth(100)); + GUILayout.Space(4); + GUILayout.BeginHorizontal(); + GUILayout.Label("First Window ", GUILayout.ExpandWidth(true)); + bool showParking1_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); + GUILayout.EndHorizontal(); - if (showParking1_pressed) - { - showParking1 = !showParking1; - // Doing this forces the window to be resized - // Without it, the window will become bigger when controls expand, but never become smaller again - windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); - } + GUILayout.Space(4); + GUILayout.Box(new GUIContent(FormatTime(firstLaunchETA), tooltip), GUILayout.MinWidth(100)); - if (showParking1) - { - double timeInOrbit1 = EstimateFlightTimeBeforeTLI(target, launchPos, firstLaunchETA); - GUILayout.Space(4); - GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); - GUILayout.Box(new GUIContent(FormatTime(timeInOrbit1), ""), GUILayout.MinWidth(100)); - } + if (showParking1_pressed) + { + showParking1 = !showParking1; + // Doing this forces the window to be resized + // Without it, the window will become bigger when controls expand, but never become smaller again + windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); + } + if (showParking1) + { + double timeInOrbit1 = EstimateFlightTimeBeforeTLI(target, launchPos, firstLaunchETA); GUILayout.Space(4); - GUILayout.BeginHorizontal(); - GUILayout.Label("Second Window", GUILayout.ExpandWidth(true)); - bool showParking2_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); - GUILayout.EndHorizontal(); + GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(FormatTime(timeInOrbit1), ""), GUILayout.MinWidth(100)); + } - GUILayout.Space(4); - GUILayout.Box(new GUIContent(FormatTime(secondLaunchETA), tooltip), GUILayout.MinWidth(100)); + GUILayout.Space(4); + GUILayout.BeginHorizontal(); + GUILayout.Label("Second Window", GUILayout.ExpandWidth(true)); + bool showParking2_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); + GUILayout.EndHorizontal(); - if (showParking2_pressed) - { - showParking2 = !showParking2; - // Doing this forces the window to be resized - // Without it, the window will become bigger when controls expand, but never become smaller again - windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); - } + GUILayout.Space(4); + GUILayout.Box(new GUIContent(FormatTime(secondLaunchETA), tooltip), GUILayout.MinWidth(100)); - if (showParking2) - { - double timeInOrbit2 = EstimateFlightTimeBeforeTLI(target, launchPos, secondLaunchETA); - GUILayout.Space(4); - GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); - GUILayout.Box(new GUIContent(FormatTime(timeInOrbit2), "")); - } + if (showParking2_pressed) + { + showParking2 = !showParking2; + // Doing this forces the window to be resized + // Without it, the window will become bigger when controls expand, but never become smaller again + windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); + } + + if (showParking2) + { + double timeInOrbit2 = EstimateFlightTimeBeforeTLI(target, launchPos, secondLaunchETA); + GUILayout.Space(4); + GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); + GUILayout.Box(new GUIContent(FormatTime(timeInOrbit2), "")); + } - GUILayout.Label("Warp Margin (sec)", GUILayout.ExpandWidth(true)); - MakeNumberEditField(ref warpMargin, ref nextTickWM, 5f, 0f); + GUILayout.Label("Warp Margin (sec)", GUILayout.ExpandWidth(true)); + MakeNumberEditField(ref warpMargin, ref nextTickWM, 5f, 0f); - GUILayout.Space(2); - GUILayout.BeginHorizontal(); - GUI.enabled = KACWrapper.APIReady; - bool addAlarm = GUILayout.Button("Add Alarm", GUILayout.MinWidth(80)); - GUI.enabled = true; - bool toggleWarp = GUILayout.Button(TimeWarp.CurrentRate > 1f ? "Stop Warp" : "Warp", GUILayout.MinWidth(80)); - GUILayout.EndHorizontal(); + GUILayout.Space(2); + GUILayout.BeginHorizontal(); + GUI.enabled = KACWrapper.APIReady; + bool addAlarm = GUILayout.Button("Add Alarm", GUILayout.MinWidth(80)); + GUI.enabled = true; + bool toggleWarp = GUILayout.Button(TimeWarp.CurrentRate > 1f ? "Stop Warp" : "Warp", GUILayout.MinWidth(80)); + GUILayout.EndHorizontal(); - if (addAlarm) + if (addAlarm) + { + if (KACWrapper.APIReady) { - if (KACWrapper.APIReady) + string alarmId = KACWrapper.KAC.CreateAlarm(KACWrapper.KACAPI.AlarmTypeEnum.Raw, "Lunar transfer window", Planetarium.GetUniversalTime() + firstLaunchETA - warpMargin); + if (!string.IsNullOrEmpty(alarmId)) { - string alarmId = KACWrapper.KAC.CreateAlarm(KACWrapper.KACAPI.AlarmTypeEnum.Raw, "Lunar transfer window", Planetarium.GetUniversalTime() + firstLaunchETA - warpMargin); - if (!string.IsNullOrEmpty(alarmId)) - { - //if the alarm was made get the object so we can update it - KACWrapper.KACAPI.KACAlarm alarm = KACWrapper.KAC.Alarms.First(z => z.ID == alarmId); - - //Now update some of the other properties - alarm.AlarmAction = KACWrapper.KACAPI.AlarmActionEnum.KillWarp; - } + //if the alarm was made get the object so we can update it + KACWrapper.KACAPI.KACAlarm alarm = KACWrapper.KAC.Alarms.First(z => z.ID == alarmId); + + //Now update some of the other properties + alarm.AlarmAction = KACWrapper.KACAPI.AlarmActionEnum.KillWarp; } } + } - if (toggleWarp) + if (toggleWarp) + { + if (TimeWarp.CurrentRate > 1f) { - if (TimeWarp.CurrentRate > 1f) - { - TimeWarp.fetch.CancelAutoWarp(); - TimeWarp.SetRate(0, false); - } - else - { - TimeWarp.fetch.WarpTo(Planetarium.GetUniversalTime() + firstLaunchETA - warpMargin); - } + TimeWarp.fetch.CancelAutoWarp(); + TimeWarp.SetRate(0, false); + } + else + { + TimeWarp.fetch.WarpTo(Planetarium.GetUniversalTime() + firstLaunchETA - warpMargin); } } + } GUILayout.EndVertical(); GUI.DragWindow(); From 05bd7e110d375861162b489862c49eeb44433f17 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Sun, 11 May 2025 14:07:42 -0400 Subject: [PATCH 08/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 718a2e7..17a174d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Lunar Transfer Planner ======================================== Provides a GUI for planning a Lunar transfer for RSS. Select the expected flight time to the Moon, i.e. the time from leaving Earth orbit to Lunar periapsis, this defaults to 4 days. The delta-V required for this maneuver is also given. -The correct launch inclination for immediate launch is shown, and your current latitude is shown for reference. The first two windows for a minimum inclination (i.e. Easterly) launch are also given, along with the time in parking orbit that you will spend while waiting for the lunar transfer. +The correct launch inclination for immediate launch is shown, and your current latitude is shown for reference. The first two windows for a minimum inclination (i.e. Easterly) launch are also given, along with the time in a given parking orbit that you will spend while waiting for the lunar transfer. Forum Thread: From d6a4a16ac1276df7df73c2c0ab7cc2e713d77469 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Sun, 11 May 2025 14:08:07 -0400 Subject: [PATCH 09/14] Update LunarTransferPlanner.cs --- LunarTransferPlanner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index be1f5ae..bd8b87d 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -88,7 +88,7 @@ public class LunarTransferPlanner : DaMichelToolbarSuperWrapper.PluginWithToolba // gui stuff float flightTime = 4f; float nextTickFT = 0f; - float tliAltitudeKM = 200f; // Parking orbit altitude (circular orbit assumed). + float tliAltitudeKM = 200f; // Parking orbit altitude (circular orbit assumed) float nextTickTA = 0f; bool showParking0 = false; // Expand/collapse time in parking orbit for launch now bool showParking1 = false; // Expand/collapse time in parking orbit for first window From 843d375594863410f91eb159d0d847057669d826 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Sun, 11 May 2025 21:43:44 -0400 Subject: [PATCH 10/14] add changes --- LunarTransferPlanner.cs | 57 +++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index bd8b87d..b9311b4 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -29,6 +29,7 @@ THE SOFTWARE. using System.Linq; using Contracts.Agents.Mentalities; using KerbalConstructionTime; +using System.Drawing; namespace LunarTransferPlanner { @@ -90,6 +91,7 @@ public class LunarTransferPlanner : DaMichelToolbarSuperWrapper.PluginWithToolba float nextTickFT = 0f; float tliAltitudeKM = 200f; // Parking orbit altitude (circular orbit assumed) float nextTickTA = 0f; + bool showInfo = false; // Expand/collapse tooltips bool showParking0 = false; // Expand/collapse time in parking orbit for launch now bool showParking1 = false; // Expand/collapse time in parking orbit for first window bool showParking2 = false; // Expand/collapse time in parking orbit for second window @@ -535,12 +537,6 @@ private double EstimateFlightTimeBeforeTLI(CelestialBody target, Vector3d launch double orbitPeriod = 2 * Math.PI * Math.Sqrt((orbitRadius * orbitRadius * orbitRadius) / gravParameter); double flightTimeBeforeTLI = rotationAngle / 360 * orbitPeriod; - // Launch and maneuver planning take some time, say 15 minutes. If time to TLI is less than that, add an orbit. - if (flightTimeBeforeTLI < 15 * 60) - { - flightTimeBeforeTLI += orbitPeriod; - } - return flightTimeBeforeTLI; } @@ -550,7 +546,7 @@ private double EstimateDV(CelestialBody target, Vector3d launchPos) // Search in this range const float minPossibleDV = 3000; - const float maxPossibleDV = 4200; + const float maxPossibleDV = 6000; // Current search range, will be gradually narrowed float lowerBound = minPossibleDV; @@ -566,6 +562,7 @@ private double EstimateDV(CelestialBody target, Vector3d launchPos) double flightTimeAfterTLI = EstimateFlightTimeAfterTLI(target, dV); double flightTimeBeforeTLI = EstimateFlightTimeBeforeTLI(target, launchPos, 0d); double estimatedFlightTime = flightTimeBeforeTLI + flightTimeAfterTLI; + double expectedFlightTime = flightTime * (24 * 60 * 60); // Debug.Log(i + " " + dV + " " + flightTime + " " + flightTimeBeforeTLI + " " + flightTimeAfterTLI + " " + estimatedFlightTime); @@ -575,13 +572,13 @@ private double EstimateDV(CelestialBody target, Vector3d launchPos) lowerBound = dV; continue; } - else if (estimatedFlightTime > (flightTime * (24 * 60 * 60) + 60)) + else if (estimatedFlightTime > (expectedFlightTime + 5)) { // dV is too low, set lower bound to current guess and try again lowerBound = dV; continue; } - else if (estimatedFlightTime < (flightTime * (24 * 60 * 60) - 60)) + else if (estimatedFlightTime < (expectedFlightTime - 5)) { // dV is too high, set upper bound to current guess and try again upperBound = dV; @@ -594,7 +591,7 @@ private double EstimateDV(CelestialBody target, Vector3d launchPos) } } - if (dV == minPossibleDV || dV == maxPossibleDV) + if (Math.Abs(dV - minPossibleDV) <= 0.001 * Math.Abs(minPossibleDV) || Math.Abs(dV - maxPossibleDV) <= 0.001 * Math.Abs(maxPossibleDV)) { // dV is incorrect, the correct value is outside the initial search range dV = float.NaN; @@ -603,6 +600,7 @@ private double EstimateDV(CelestialBody target, Vector3d launchPos) return dV; } + private string FormatTime(double t) { t = Math.Round(t); @@ -631,17 +629,38 @@ void MakeMainWindow(int id) } else { + + double latitude = 0d; + Vector3d launchPos = GetLaunchPos(target.referenceBody, ref latitude); + + GUILayout.Space(4); + GUILayout.BeginHorizontal(); + GUILayout.Label("Latitude:", GUILayout.Height(15), GUILayout.ExpandWidth(false)); + GUILayout.Box(new GUIContent(String.Format("{0:0.00}\u00B0", latitude), ""), GUILayout.MinWidth(60), GUILayout.Height(20)); + GUILayout.Space(5); + bool showInfo_pressed = GUILayout.Button(" ?", GUILayout.Width(20)); + GUILayout.EndHorizontal(); + //GUILayout.Box(new GUIContent(String.Format("{0:0.00}\u00B0", latitude), "Latitude of current launch site"), GUILayout.MinWidth(100)); + if (showInfo) GUILayout.Label("Latitude of current launch site", GUILayout.ExpandWidth(true)); + + if (showInfo_pressed) + { + showInfo = !showInfo; + // Doing this forces the window to be resized + // Without it, the window will become bigger when controls expand, but never become smaller again + windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); + } + GUILayout.Space(4); GUILayout.Label("Flight Time (days)", GUILayout.ExpandWidth(true)); MakeNumberEditField(ref flightTime, ref nextTickFT, 0.1f, 0.1f); + var t = TimeSpan.FromDays(flightTime); + GUILayout.Box(new GUIContent($"{t.Days}d {t.Hours}h {t.Minutes}m {t.Seconds}s", ""), GUILayout.MinWidth(100)); GUILayout.Space(4); GUILayout.Label("Circ. Parking Orbit (km)", GUILayout.ExpandWidth(true)); MakeNumberEditField(ref tliAltitudeKM, ref nextTickTA, 5.0f, 140.0f); - double latitude = 0d; - Vector3d launchPos = GetLaunchPos(target.referenceBody, ref latitude); - OrbitData launchOrbit = CalcOrbitForTime(target, launchPos, 0d); double firstLaunchETA = EstimateLaunchTime(target, launchPos, latitude, 0d); double secondLaunchETA = EstimateLaunchTime(target, launchPos, latitude, firstLaunchETA + 3600d); @@ -650,11 +669,8 @@ void MakeMainWindow(int id) GUILayout.Space(4); GUILayout.Label("Required dV", GUILayout.ExpandWidth(true)); - GUILayout.Box(new GUIContent(String.Format("{0:0 m/s}", dV), "Required dV for the selected flight time"), GUILayout.MinWidth(100)); - - GUILayout.Space(4); - GUILayout.Label("Latitude", GUILayout.ExpandWidth(true)); - GUILayout.Box(new GUIContent(String.Format("{0:0.00}\u00B0", latitude), "Latitude of current launch site"), GUILayout.MinWidth(100)); + GUILayout.Box(new GUIContent(String.Format("{0:0.00 m/s}", dV), ""), GUILayout.MinWidth(100)); + if (showInfo) GUILayout.Label("Required delta-V for the selected flight time", GUILayout.ExpandWidth(true)); GUILayout.Space(4); GUILayout.BeginHorizontal(); @@ -665,6 +681,7 @@ void MakeMainWindow(int id) GUILayout.Space(4); GUILayout.Box(new GUIContent($"{(launchOrbit.azimuth > 90d ? -launchOrbit.inclination : launchOrbit.inclination):F2}\u00B0", "Launch to this inclination now to reach a Lunar parking orbit"), GUILayout.MinWidth(100)); + if (showInfo) GUILayout.Label("Launch to this inclination now to reach a Lunar parking orbit", GUILayout.ExpandWidth(true)); string tooltip = Math.Abs(latitude) >= target.orbit.inclination ? "Launch at this time for an Easterly launch to Lunar parking orbit" : @@ -688,12 +705,13 @@ void MakeMainWindow(int id) GUILayout.Space(4); GUILayout.BeginHorizontal(); - GUILayout.Label("First Window ", GUILayout.ExpandWidth(true)); + GUILayout.Label("First Window", GUILayout.ExpandWidth(true)); bool showParking1_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); GUILayout.EndHorizontal(); GUILayout.Space(4); GUILayout.Box(new GUIContent(FormatTime(firstLaunchETA), tooltip), GUILayout.MinWidth(100)); + if (showInfo) GUILayout.Label(tooltip, GUILayout.ExpandWidth(true)); if (showParking1_pressed) { @@ -719,6 +737,7 @@ void MakeMainWindow(int id) GUILayout.Space(4); GUILayout.Box(new GUIContent(FormatTime(secondLaunchETA), tooltip), GUILayout.MinWidth(100)); + if (showInfo) GUILayout.Label(tooltip, GUILayout.ExpandWidth(true)); if (showParking2_pressed) { From eb08dac00967ec87b8684b00c680ca0ce25723d7 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Sun, 11 May 2025 21:44:56 -0400 Subject: [PATCH 11/14] Update LunarTransferPlanner.cs wtf when did that get added? --- LunarTransferPlanner.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index b9311b4..67fb981 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -29,7 +29,6 @@ THE SOFTWARE. using System.Linq; using Contracts.Agents.Mentalities; using KerbalConstructionTime; -using System.Drawing; namespace LunarTransferPlanner { From 74eeaecba5312f1e88104a9ebccb9726b3b0fd77 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Sun, 11 May 2025 21:48:59 -0400 Subject: [PATCH 12/14] Update LunarTransferPlanner.cs --- LunarTransferPlanner.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index 67fb981..eeedab3 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -679,12 +679,12 @@ void MakeMainWindow(int id) GUILayout.Space(4); GUILayout.Box(new GUIContent($"{(launchOrbit.azimuth > 90d ? -launchOrbit.inclination : launchOrbit.inclination):F2}\u00B0", - "Launch to this inclination now to reach a Lunar parking orbit"), GUILayout.MinWidth(100)); - if (showInfo) GUILayout.Label("Launch to this inclination now to reach a Lunar parking orbit", GUILayout.ExpandWidth(true)); + ""), GUILayout.MinWidth(100)); + if (showInfo) GUILayout.Label("Launch to this inclination now to get into the right parking orbit", GUILayout.ExpandWidth(true)); string tooltip = Math.Abs(latitude) >= target.orbit.inclination ? - "Launch at this time for an Easterly launch to Lunar parking orbit" : - "Launch at this time for a low inclination launch to Lunar parking orbit"; + "Launch at this time for an Easterly launch to get into the right parking orbit" : + "Launch at this time for a low inclination launch to get into the right parking orbit"; if (showParking0_pressed) { From 2bb2cbd1ab08a912d50bbab2ecad99dce5935eff Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Sat, 17 May 2025 14:55:42 -0400 Subject: [PATCH 13/14] revert nazfib --- LunarTransferPlanner.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index eeedab3..c0e396d 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -292,19 +292,14 @@ private OrbitData CalcOrbitForTime(CelestialBody target, Vector3d launchPos, dou double targetTime = Planetarium.GetUniversalTime() + flightTime * 24d * 3600d + delayTime; Vector3d targetPos = target.getPositionAtUT(targetTime); - Vector3d upVector = QuaternionD.AngleAxis(delayTime * 360d / mainBody.rotationPeriod, EarthAxis) * (launchPos - EarthPos).normalized; + Vector3d upVector = Quaternion.AngleAxis((float)(delayTime * 360d / mainBody.rotationPeriod), EarthAxis) * (launchPos - EarthPos).normalized; Vector3d orbitNorm = Vector3d.Cross(targetPos - EarthPos, upVector).normalized; - double inclination = Math.Acos(Vector3d.Dot(orbitNorm, EarthAxis)); + double inclination = Math.Acos(Vector3d.Dot(orbitNorm, mainBody.angularVelocity.normalized)); if (inclination > Math.PI / 2) - { inclination = Math.PI - inclination; - orbitNorm *= -1; // make sure orbitNorm always points roughly northwards - } - // When checking this: remember that Unity (and KSP) use a left-handed coordinate system; therefore, the - // cross product follows the left-hand rule. - Vector3d eastVec = Vector3d.Cross(upVector, EarthAxis).normalized; + Vector3d eastVec = Vector3d.Cross(EarthAxis, upVector).normalized; Vector3d northVec = Vector3d.Cross(eastVec, upVector).normalized; Vector3d launchVec = Vector3d.Cross(upVector, orbitNorm).normalized; @@ -319,7 +314,7 @@ private double EstimateLaunchTime(CelestialBody target, Vector3d launchPos, doub double t = startTime; OrbitData launchOrbit = CalcOrbitForTime(target, launchPos, t); - if (Math.Abs(latitude) >= target.orbit.inclination) + if (latitude >= target.orbit.inclination) { // High latitude path - find the next easterly launch to the target while (Math.Abs(launchOrbit.azimuth - targetAz) > 0.01d) @@ -682,7 +677,7 @@ void MakeMainWindow(int id) ""), GUILayout.MinWidth(100)); if (showInfo) GUILayout.Label("Launch to this inclination now to get into the right parking orbit", GUILayout.ExpandWidth(true)); - string tooltip = Math.Abs(latitude) >= target.orbit.inclination ? + string tooltip = latitude >= target.orbit.inclination ? "Launch at this time for an Easterly launch to get into the right parking orbit" : "Launch at this time for a low inclination launch to get into the right parking orbit"; From 2b070a5511c728b0bbd4b485125a72f9ae215649 Mon Sep 17 00:00:00 2001 From: Clayell <125416952+Clayell@users.noreply.github.com> Date: Sat, 17 May 2025 16:06:00 -0400 Subject: [PATCH 14/14] make altitude collapsible --- LunarTransferPlanner.cs | 53 +++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/LunarTransferPlanner.cs b/LunarTransferPlanner.cs index c0e396d..fefb2bb 100644 --- a/LunarTransferPlanner.cs +++ b/LunarTransferPlanner.cs @@ -634,7 +634,7 @@ void MakeMainWindow(int id) GUILayout.Space(5); bool showInfo_pressed = GUILayout.Button(" ?", GUILayout.Width(20)); GUILayout.EndHorizontal(); - //GUILayout.Box(new GUIContent(String.Format("{0:0.00}\u00B0", latitude), "Latitude of current launch site"), GUILayout.MinWidth(100)); + if (showInfo) GUILayout.Label("Latitude of current launch site", GUILayout.ExpandWidth(true)); if (showInfo_pressed) @@ -650,10 +650,7 @@ void MakeMainWindow(int id) MakeNumberEditField(ref flightTime, ref nextTickFT, 0.1f, 0.1f); var t = TimeSpan.FromDays(flightTime); GUILayout.Box(new GUIContent($"{t.Days}d {t.Hours}h {t.Minutes}m {t.Seconds}s", ""), GUILayout.MinWidth(100)); - - GUILayout.Space(4); - GUILayout.Label("Circ. Parking Orbit (km)", GUILayout.ExpandWidth(true)); - MakeNumberEditField(ref tliAltitudeKM, ref nextTickTA, 5.0f, 140.0f); + if (showInfo) GUILayout.Label("Coast duration to the Moon after the maneuver", GUILayout.ExpandWidth(true)); OrbitData launchOrbit = CalcOrbitForTime(target, launchPos, 0d); double firstLaunchETA = EstimateLaunchTime(target, launchPos, latitude, 0d); @@ -662,24 +659,42 @@ void MakeMainWindow(int id) double dV = EstimateDV(target, launchPos); GUILayout.Space(4); - GUILayout.Label("Required dV", GUILayout.ExpandWidth(true)); + GUILayout.BeginHorizontal(); + GUILayout.Label("Required \u0394V", GUILayout.ExpandWidth(true)); + bool showAltitude_pressed = GUILayout.Button("...", GUILayout.Width(30)); + GUILayout.EndHorizontal(); + GUILayout.Box(new GUIContent(String.Format("{0:0.00 m/s}", dV), ""), GUILayout.MinWidth(100)); - if (showInfo) GUILayout.Label("Required delta-V for the selected flight time", GUILayout.ExpandWidth(true)); + if (showInfo) GUILayout.Label("Required change in velocity for the maneuver if launched now", GUILayout.ExpandWidth(true)); + + if (showAltitude_pressed) + { + showAltitude = !showAltitude; + // Doing this forces the window to be resized + // Without it, the window will become bigger when controls expand, but never become smaller again + windowRect = new Rect(windowRect.xMin, windowRect.yMin, -1, -1); + } + + if (showAltitude) + { + GUILayout.Label("Parking Orbit (km)", GUILayout.ExpandWidth(true)); + MakeNumberEditField(ref tliAltitudeKM, ref nextTickTA, 5.0f, 140.0f); + if (showInfo) GUILayout.Label("Planned altitude of the circular parking orbit before the maneuver", GUILayout.ExpandWidth(true)); + } GUILayout.Space(4); GUILayout.BeginHorizontal(); GUILayout.Label("Launch Now Incl.", GUILayout.ExpandWidth(true)); - bool showParking0_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); + bool showParking0_pressed = GUILayout.Button("...", GUILayout.Width(30)); GUILayout.EndHorizontal(); GUILayout.Space(4); - GUILayout.Box(new GUIContent($"{(launchOrbit.azimuth > 90d ? -launchOrbit.inclination : launchOrbit.inclination):F2}\u00B0", - ""), GUILayout.MinWidth(100)); + GUILayout.Box(new GUIContent($"{(launchOrbit.azimuth > 90d ? -launchOrbit.inclination : launchOrbit.inclination):F2}\u00B0",""), GUILayout.MinWidth(100)); if (showInfo) GUILayout.Label("Launch to this inclination now to get into the right parking orbit", GUILayout.ExpandWidth(true)); string tooltip = latitude >= target.orbit.inclination ? - "Launch at this time for an Easterly launch to get into the right parking orbit" : - "Launch at this time for a low inclination launch to get into the right parking orbit"; + "Launch Easterly at this time to get into the right parking orbit" : + "Launch at this time at this inclination to get into the right parking orbit"; if (showParking0_pressed) { @@ -692,7 +707,6 @@ void MakeMainWindow(int id) if (showParking0) { double timeInOrbit0 = EstimateFlightTimeBeforeTLI(target, launchPos, 0d); - GUILayout.Space(4); GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); GUILayout.Box(new GUIContent(FormatTime(timeInOrbit0), ""), GUILayout.MinWidth(100)); } @@ -700,11 +714,11 @@ void MakeMainWindow(int id) GUILayout.Space(4); GUILayout.BeginHorizontal(); GUILayout.Label("First Window", GUILayout.ExpandWidth(true)); - bool showParking1_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); + bool showParking1_pressed = GUILayout.Button("...", GUILayout.Width(30)); GUILayout.EndHorizontal(); GUILayout.Space(4); - GUILayout.Box(new GUIContent(FormatTime(firstLaunchETA), tooltip), GUILayout.MinWidth(100)); + GUILayout.Box(new GUIContent(FormatTime(firstLaunchETA), ""), GUILayout.MinWidth(100)); if (showInfo) GUILayout.Label(tooltip, GUILayout.ExpandWidth(true)); if (showParking1_pressed) @@ -718,7 +732,6 @@ void MakeMainWindow(int id) if (showParking1) { double timeInOrbit1 = EstimateFlightTimeBeforeTLI(target, launchPos, firstLaunchETA); - GUILayout.Space(4); GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); GUILayout.Box(new GUIContent(FormatTime(timeInOrbit1), ""), GUILayout.MinWidth(100)); } @@ -726,11 +739,11 @@ void MakeMainWindow(int id) GUILayout.Space(4); GUILayout.BeginHorizontal(); GUILayout.Label("Second Window", GUILayout.ExpandWidth(true)); - bool showParking2_pressed = GUILayout.Button("...", GUILayout.MinWidth(20)); + bool showParking2_pressed = GUILayout.Button("...", GUILayout.Width(30)); GUILayout.EndHorizontal(); GUILayout.Space(4); - GUILayout.Box(new GUIContent(FormatTime(secondLaunchETA), tooltip), GUILayout.MinWidth(100)); + GUILayout.Box(new GUIContent(FormatTime(secondLaunchETA), ""), GUILayout.MinWidth(100)); if (showInfo) GUILayout.Label(tooltip, GUILayout.ExpandWidth(true)); if (showParking2_pressed) @@ -744,15 +757,15 @@ void MakeMainWindow(int id) if (showParking2) { double timeInOrbit2 = EstimateFlightTimeBeforeTLI(target, launchPos, secondLaunchETA); - GUILayout.Space(4); GUILayout.Label("Time in parking orbit", GUILayout.ExpandWidth(true)); GUILayout.Box(new GUIContent(FormatTime(timeInOrbit2), "")); } + GUILayout.Space(4); GUILayout.Label("Warp Margin (sec)", GUILayout.ExpandWidth(true)); MakeNumberEditField(ref warpMargin, ref nextTickWM, 5f, 0f); - GUILayout.Space(2); + GUILayout.Space(8); GUILayout.BeginHorizontal(); GUI.enabled = KACWrapper.APIReady; bool addAlarm = GUILayout.Button("Add Alarm", GUILayout.MinWidth(80));