VOID_Data: More organizing.
[VOID.git] / VOID_Data.cs
blob:a/VOID_Data.cs -> blob:b/VOID_Data.cs
--- a/VOID_Data.cs
+++ b/VOID_Data.cs
@@ -26,7 +26,7 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using Engineer.VesselSimulator;
+using KerbalEngineer.VesselSimulator;
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -37,6 +37,16 @@
 {
 	public static class VOID_Data
 	{
+		private static Dictionary<int, IVOID_DataValue> dataValues = new Dictionary<int, IVOID_DataValue>();
+
+		public static Dictionary<int, IVOID_DataValue> DataValues
+		{
+			get
+			{
+				return dataValues;
+			}
+		}
+
 		#region Constants
 
 		private static double kerbinGee;
@@ -47,7 +57,7 @@
 			{
 				if (kerbinGee == default(double))
 				{
-					kerbinGee = core.Kerbin.gravParameter / (core.Kerbin.Radius * core.Kerbin.Radius);
+					kerbinGee = Core.HomeBody.gravParameter / (Core.HomeBody.Radius * Core.HomeBody.Radius);
 				}
 
 				return kerbinGee;
@@ -58,21 +68,47 @@
 
 		#region Core Data
 
-		public static VOID_Core core
+		public static VOIDCore Core
 		{
 			get
 			{
-				if (HighLogic.LoadedSceneIsEditor)
-				{
-					return VOID_EditorCore.Instance;
-				}
-				else
-				{
-					return VOID_Core.Instance;
+				if (!CoreInitialized)
+				{
+					return null;
+				}
+
+				switch (HighLogic.LoadedScene)
+				{
+					case GameScenes.EDITOR:
+						return (VOIDCore)VOIDCore_Editor.Instance;
+					case GameScenes.FLIGHT:
+						return (VOIDCore)VOIDCore_Flight.Instance;
+					case GameScenes.SPACECENTER:
+						return (VOIDCore)VOIDCore_SpaceCentre.Instance;
+					default:
+						return null;
 				}
 			}
 		}
 
+		public static bool CoreInitialized
+		{
+			get
+			{
+				switch (HighLogic.LoadedScene)
+				{
+					case GameScenes.EDITOR:
+						return VOIDCore_Editor.Initialized;
+					case GameScenes.FLIGHT:
+						return VOIDCore_Flight.Initialized;
+					case GameScenes.SPACECENTER:
+						return VOIDCore_SpaceCentre.Initialized;
+					default:
+						return false;
+				}
+			}
+		}
+
 		#endregion
 
 		#region Atmosphere
@@ -80,28 +116,28 @@
 		public static readonly VOID_DoubleValue atmDensity =
 			new VOID_DoubleValue(
 				"Atmosphere Density",
-				new Func<double>(() => core.vessel.atmDensity * 1000f),
+				new Func<double>(() => Core.Vessel.atmDensity * 1000f),
 				"g/m³"
 			);
 
 		public static readonly VOID_FloatValue atmLimit =
 			new VOID_FloatValue(
 				"Atmosphere Limit",
-				new Func<float>(() => core.vessel.mainBody.maxAtmosphereAltitude),
+				new Func<float>(() => Core.Vessel.mainBody.maxAtmosphereAltitude),
 				"m"
 			);
 
 		public static readonly VOID_DoubleValue atmPressure =
 			new VOID_DoubleValue(
 				"Pressure",
-				new Func<double>(() => core.vessel.staticPressure),
+				new Func<double>(() => Core.Vessel.staticPressure),
 				"atm"
 			);
 
 		public static readonly VOID_FloatValue temperature =
 			new VOID_FloatValue(
 				"Temperature",
-				new Func<float>(() => core.vessel.flightIntegrator.getExternalTemperature()),
+				new Func<float>(() => Core.Vessel.flightIntegrator.getExternalTemperature()),
 				"°C"
 			);
 
@@ -114,7 +150,7 @@
 				"Heading",
 				delegate()
 				{
-					double heading = core.vessel.getSurfaceHeading();
+					double heading = Core.Vessel.getSurfaceHeading();
 					string cardinal = VOID_Tools.get_heading_text(heading);
 
 					return string.Format(
@@ -128,7 +164,7 @@
 		public static readonly VOID_DoubleValue vesselPitch =
 			new VOID_DoubleValue(
 				"Pitch",
-				() => core.vessel.getSurfacePitch(),
+				() => Core.Vessel.getSurfacePitch(),
 				"°"
 			);
 
@@ -194,7 +230,7 @@
 		public static readonly VOID_FloatValue mainThrottle =
 			new VOID_FloatValue(
 				"Throttle",
-				new Func<float>(() => core.vessel.ctrlState.mainThrottle * 100f),
+				new Func<float>(() => Core.Vessel.ctrlState.mainThrottle * 100f),
 				"%"
 			);
 
@@ -205,7 +241,7 @@
 		public static readonly VOID_IntValue partCount =
 			new VOID_IntValue(
 				"Parts",
-				new Func<int>(() => core.vessel.Parts.Count),
+				new Func<int>(() => Core.Vessel.Parts.Count),
 				""
 			);
 
@@ -228,12 +264,12 @@
 				"Resource Mass",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.totalMass - core.LastStage.totalBaseMass;
+					if (Core.Stages == null || Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.totalResourceMass;
 				},
 				"tons"
 			);
@@ -243,12 +279,12 @@
 				"Resource Mass (Stage)",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.mass - core.LastStage.baseMass;
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.resourceMass;
 				},
 				"tons"
 			);
@@ -258,12 +294,12 @@
 				"Total Mass",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.totalMass;
+					if (Core.Stages == null || Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.totalMass;
 				},
 				"tons"
 			);
@@ -277,9 +313,9 @@
 				"DeltaV (Current Stage)",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-						return double.NaN;
-					return core.LastStage.deltaV;
+					if (Core.Stages == null || Core.LastStage == null)
+						return double.NaN;
+					return Core.LastStage.deltaV;
 				},
 				"m/s"
 			);
@@ -289,9 +325,9 @@
 				"DeltaV (Total)",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-						return double.NaN;
-					return core.LastStage.totalDeltaV;
+					if (Core.Stages == null || Core.LastStage == null)
+						return double.NaN;
+					return Core.LastStage.totalDeltaV;
 				},
 				"m/s"
 			);
@@ -305,7 +341,7 @@
 				"T:W (curr/max)",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
+					if (Core.Stages == null || Core.LastStage == null)
 						return "N/A";
 
 					return string.Format(
@@ -321,11 +357,11 @@
 				"Thrust (curr/max)",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
+					if (Core.Stages == null || Core.LastStage == null)
 						return "N/A";
 
-					double currThrust = core.LastStage.actualThrust;
-					double maxThrust = core.LastStage.thrust;
+					double currThrust = Core.LastStage.actualThrust;
+					double maxThrust = Core.LastStage.thrust;
 
 					return string.Format(
 						"{0} / {1}",
@@ -340,24 +376,12 @@
 				"Stage Mass Flow",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					double stageIsp = core.LastStage.isp;
-					double stageThrust = stageNominalThrust;
-
-					Tools.PostDebugMessage(typeof(VOID_Data), "calculating stageMassFlow from:\n" +
-						"\tstageIsp: {0}\n" +
-						"\tstageThrust: {1}\n" +
-						"\tKerbinGee: {2}\n",
-						stageIsp,
-						stageThrust,
-						KerbinGee
-					);
-
-					return stageThrust / (stageIsp * KerbinGee);
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.MassFlow();
 				},
 				"Mg/s"
 			);
@@ -367,19 +391,12 @@
 				"Nominal Stage Thrust",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					if (core.LastStage.actualThrust == 0d)
-					{
-						return core.LastStage.thrust;
-					}
-					else
-					{
-						return core.LastStage.actualThrust;
-					}
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.NominalThrust();
 				},
 				"kN"
 			);
@@ -393,12 +410,12 @@
 				"T:W Ratio",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.actualThrustToWeight;
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.actualThrustToWeight;
 				},
 				""
 			);
@@ -410,12 +427,12 @@
 				"T:W Ratio",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.thrustToWeight;
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.thrustToWeight;
 				},
 				""
 			);
@@ -440,13 +457,13 @@
 				"Max T:W @ surface",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-						return double.NaN;
-
-					double maxThrust = core.LastStage.thrust;
-					double mass = core.LastStage.totalMass;
-					double gravity = (VOID_Core.Constant_G * core.vessel.mainBody.Mass) /
-					               (core.vessel.mainBody.Radius * core.vessel.mainBody.Radius);
+					if (Core.Stages == null || Core.LastStage == null)
+						return double.NaN;
+
+					double maxThrust = Core.LastStage.thrust;
+					double mass = Core.LastStage.totalMass;
+					double gravity = (VOIDCore.Constant_G * Core.Vessel.mainBody.Mass) /
+					                 (Core.Vessel.mainBody.Radius * Core.Vessel.mainBody.Radius);
 					double weight = mass * gravity;
 
 					return maxThrust / weight;
@@ -459,12 +476,12 @@
 				"Thrust Offset",
 				delegate()
 				{
-					if (core.vessel == null)
+					if (Core.Vessel == null)
 					{
 						return Vector3d.zero;
 					}
 
-					List<PartModule> engineModules = core.vessel.getModulesOfType<PartModule>();
+					List<PartModule> engineModules = Core.Vessel.getModulesOfType<PartModule>();
 
 					Vector3d thrustPos = Vector3d.zero;
 					Vector3d thrustDir = Vector3d.zero;
@@ -523,23 +540,23 @@
 						thrustDir /= thrust;
 					}
 
-					Transform vesselTransform = core.vessel.transform;
+					Transform vesselTransform = Core.Vessel.transform;
 
 					thrustPos = vesselTransform.InverseTransformPoint(thrustPos);
 					thrustDir = vesselTransform.InverseTransformDirection(thrustDir);
 
 					Vector3d thrustOffset = VectorTools.PointDistanceToLine(
-						                      thrustPos, thrustDir.normalized, core.vessel.findLocalCenterOfMass());
+						                        thrustPos, thrustDir.normalized, Core.Vessel.findLocalCenterOfMass());
 
 					Tools.PostDebugMessage(typeof(VOID_Data), "vesselThrustOffset:\n" +
-						"\tthrustPos: {0}\n" +
-						"\tthrustDir: {1}\n" +
-						"\tthrustOffset: {2}\n" +
-						"\tvessel.CoM: {3}",
+					"\tthrustPos: {0}\n" +
+					"\tthrustDir: {1}\n" +
+					"\tthrustOffset: {2}\n" +
+					"\tvessel.CoM: {3}",
 						thrustPos,
 						thrustDir.normalized,
 						thrustOffset,
-						core.vessel.findWorldCenterOfMass()
+						Core.Vessel.findWorldCenterOfMass()
 					);
 
 					return thrustOffset;
@@ -562,7 +579,7 @@
 					currentAmount = 0d;
 					currentRequirement = 0d;
 
-					foreach (Part part in core.vessel.Parts)
+					foreach (Part part in Core.Vessel.Parts)
 					{
 						if (part.enabled)
 						{
@@ -575,9 +592,9 @@
 								propellantList = engineModule.propellants;
 							}
 							else if (part.tryGetFirstModuleOfType<ModuleEnginesFX>(out enginesFXModule))
-								{
-									propellantList = enginesFXModule.propellants;
-								}
+							{
+								propellantList = enginesFXModule.propellants;
+							}
 
 							if (propellantList != null)
 							{
@@ -621,9 +638,9 @@
 				"Crew Onboard",
 				delegate()
 				{
-					if (core.vessel != null)
-					{
-						return core.vessel.GetCrewCount();
+					if (Core.Vessel != null)
+					{
+						return Core.Vessel.GetCrewCount();
 					}
 					else
 					{
@@ -638,9 +655,9 @@
 				"Crew Capacity",
 				delegate()
 				{
-					if (core.vessel != null)
-					{
-						return core.vessel.GetCrewCapacity();
+					if (Core.Vessel != null)
+					{
+						return Core.Vessel.GetCrewCapacity();
 					}
 					else
 					{
@@ -656,41 +673,44 @@
 
 		#region Location
 
+		public const double kscLongitude = 285.442323427289 * Math.PI / 180d;
+		public const double kscLatitude = -0.0972112860655246 * Math.PI / 180d;
+
 		public static readonly VOID_DoubleValue downrangeDistance =
 			new VOID_DoubleValue(
 				"Downrange Distance",
 				delegate()
 				{
 
-					if (core.vessel == null ||
-					  Planetarium.fetch == null ||
-					  core.vessel.mainBody != Planetarium.fetch.Home)
-					{
-						return double.NaN;
-					}
-
-					double vesselLongitude = core.vessel.longitude * Math.PI / 180d;
-					double vesselLatitude = core.vessel.latitude * Math.PI / 180d;
-
-					const double kscLongitude = 285.442323427289 * Math.PI / 180d;
-					const double kscLatitude = -0.0972112860655246 * Math.PI / 180d;
-
-					double diffLon = vesselLongitude - kscLongitude;
-					double diffLat = vesselLatitude - kscLatitude;
-
-					double sinHalfDiffLat = Math.Sin(diffLat / 2d);
-					double sinHalfDiffLon = Math.Sin(diffLon / 2d);
-
-					double cosVesselLon = Math.Cos(vesselLongitude);
-					double cosKSCLon = Math.Cos(kscLongitude);
-
-					double haversine =
-						sinHalfDiffLat * sinHalfDiffLat +
-						cosVesselLon * cosKSCLon * sinHalfDiffLon * sinHalfDiffLon;
-
-					double arc = 2d * Math.Atan2(Math.Sqrt(haversine), Math.Sqrt(1d - haversine));
-
-					return core.vessel.mainBody.Radius * arc;
+					if (Core.Vessel == null ||
+					    Planetarium.fetch == null ||
+					    Core.Vessel.mainBody != Planetarium.fetch.Home)
+					{
+						return double.NaN;
+					}
+
+					double vesselLongitude = Core.Vessel.longitude * Math.PI / 180d;
+					double vesselLatitude = Core.Vessel.latitude * Math.PI / 180d;
+
+					double diffLon = Math.Abs(vesselLongitude - kscLongitude);
+
+					double cosVesselLatitude = Math.Cos(vesselLatitude);
+					double sinDiffLon = Math.Sin(diffLon);
+
+					double term1 = cosVesselLatitude * sinDiffLon;
+
+					double cosKSCLatitude = Math.Cos(kscLatitude);
+					double sinVesselLatitude = Math.Sin(vesselLatitude);
+					double sinKSCLatitude = Math.Sin(kscLatitude);
+					double cosDiffLon = Math.Cos(diffLon);
+
+					double term2 = cosKSCLatitude * sinVesselLatitude - sinKSCLatitude * cosVesselLatitude * cosDiffLon;
+
+					double term3 = sinKSCLatitude * sinVesselLatitude + cosKSCLatitude * cosVesselLatitude * cosDiffLon;
+
+					double arc = Math.Atan2(Math.Sqrt(term1 * term1 + term2 * term2), term3);
+
+					return arc * Core.Vessel.mainBody.Radius;
 				},
 				"m"
 			);
@@ -698,13 +718,13 @@
 		public static readonly VOID_StrValue surfLatitude =
 			new VOID_StrValue(
 				"Latitude",
-				new Func<string>(() => VOID_Tools.GetLatitudeString(core.vessel))
+				new Func<string>(() => VOID_Tools.GetLatitudeString(Core.Vessel))
 			);
 
 		public static readonly VOID_StrValue surfLongitude =
 			new VOID_StrValue(
 				"Longitude",
-				new Func<string>(() => VOID_Tools.GetLongitudeString(core.vessel))
+				new Func<string>(() => VOID_Tools.GetLongitudeString(Core.Vessel))
 			);
 
 		public static readonly VOID_DoubleValue trueAltitude =
@@ -712,11 +732,11 @@
 				"Altitude (true)",
 				delegate()
 				{
-					double alt_true = core.vessel.orbit.altitude - core.vessel.terrainAltitude;
+					double alt_true = Core.Vessel.orbit.altitude - Core.Vessel.terrainAltitude;
 					// HACK: This assumes that on worlds with oceans, all water is fixed at 0 m,
 					// and water covers the whole surface at 0 m.
-					if (core.vessel.terrainAltitude < 0 && core.vessel.mainBody.ocean)
-						alt_true = core.vessel.orbit.altitude;
+					if (Core.Vessel.terrainAltitude < 0 && Core.Vessel.mainBody.ocean)
+						alt_true = Core.Vessel.orbit.altitude;
 					return alt_true;
 				},
 				"m"
@@ -729,28 +749,28 @@
 		public static readonly VOID_DoubleValue geeForce =
 			new VOID_DoubleValue(
 				"G-force",
-				new Func<double>(() => core.vessel.geeForce),
+				new Func<double>(() => Core.Vessel.geeForce),
 				"gees"
 			);
 
 		public static readonly VOID_DoubleValue horzVelocity =
 			new VOID_DoubleValue(
 				"Horizontal speed",
-				new Func<double>(() => core.vessel.horizontalSrfSpeed),
+				new Func<double>(() => Core.Vessel.horizontalSrfSpeed),
 				"m/s"
 			);
 
 		public static readonly VOID_DoubleValue surfVelocity =
 			new VOID_DoubleValue(
 				"Surface velocity",
-				new Func<double>(() => core.vessel.srf_velocity.magnitude),
+				new Func<double>(() => Core.Vessel.srf_velocity.magnitude),
 				"m/s"
 			);
 
 		public static readonly VOID_DoubleValue vertVelocity =
 			new VOID_DoubleValue(
 				"Vertical speed",
-				new Func<double>(() => core.vessel.verticalSpeed),
+				new Func<double>(() => Core.Vessel.verticalSpeed),
 				"m/s"
 			);
 
@@ -766,9 +786,9 @@
 				"Angular Velocity",
 				delegate()
 				{
-					if (core.vessel != null)
-					{
-						return core.vessel.angularVelocity.magnitude;
+					if (Core.Vessel != null)
+					{
+						return Core.Vessel.angularVelocity.magnitude;
 					}
 					else
 					{
@@ -786,14 +806,14 @@
 		{
 			get
 			{
-				if (core.vessel == null ||
-				    core.vessel.patchedConicSolver == null ||
-				    core.vessel.patchedConicSolver.maneuverNodes == null)
+				if (Core.Vessel == null ||
+				    Core.Vessel.patchedConicSolver == null ||
+				    Core.Vessel.patchedConicSolver.maneuverNodes == null)
 				{
 					return 0;
 				}
 
-				return core.vessel.patchedConicSolver.maneuverNodes.Count;
+				return Core.Vessel.patchedConicSolver.maneuverNodes.Count;
 			}
 		}
 
@@ -802,12 +822,12 @@
 				"Full burn time to be half done at node",
 				delegate()
 				{
-					if (core.LastStage == null && upcomingManeuverNodes < 1)
+					if (Core.LastStage == null && upcomingManeuverNodes < 1)
 					{
 						return "N/A";
 					}
 
-					ManeuverNode node = core.vessel.patchedConicSolver.maneuverNodes[0];
+					ManeuverNode node = Core.Vessel.patchedConicSolver.maneuverNodes[0];
 
 					if ((node.UT - Planetarium.GetUniversalTime()) < 0)
 					{
@@ -835,7 +855,7 @@
 						format = string.Intern("T + {0}");
 					}
 
-					return string.Format(format, VOID_Tools.ConvertInterval(interval));
+					return string.Format(format, VOID_Tools.FormatInterval(interval));
 				}
 			);
 
@@ -844,12 +864,12 @@
 				"Full burn time to be half done at node",
 				delegate()
 				{
-					if (core.LastStage == null && upcomingManeuverNodes < 1)
+					if (Core.LastStage == null && upcomingManeuverNodes < 1)
 					{
 						return "N/A";
 					}
 
-					ManeuverNode node = core.vessel.patchedConicSolver.maneuverNodes[0];
+					ManeuverNode node = Core.Vessel.patchedConicSolver.maneuverNodes[0];
 
 					if ((node.UT - Planetarium.GetUniversalTime()) < 0)
 					{
@@ -877,7 +897,7 @@
 						format = string.Intern("T + {0}");
 					}
 
-					return string.Format(format, VOID_Tools.ConvertInterval(interval));
+					return string.Format(format, VOID_Tools.FormatInterval(interval));
 				}
 			);
 
@@ -888,7 +908,7 @@
 				{
 					if (upcomingManeuverNodes > 0)
 					{
-						return core.vessel.patchedConicSolver.maneuverNodes[0].DeltaV.magnitude;
+						return Core.Vessel.patchedConicSolver.maneuverNodes[0].DeltaV.magnitude;
 					}
 					else
 					{
@@ -905,7 +925,7 @@
 				{
 					if (upcomingManeuverNodes > 0)
 					{
-						return core.vessel.patchedConicSolver.maneuverNodes[0].GetBurnVector(core.vessel.orbit).magnitude;
+						return Core.Vessel.patchedConicSolver.maneuverNodes[0].GetBurnVector(Core.Vessel.orbit).magnitude;
 					}
 					else
 					{
@@ -920,14 +940,12 @@
 				"Total Burn Time",
 				delegate()
 				{
-					if (core.LastStage == null || currManeuverDeltaV.Value == double.NaN)
-					{
-						return double.NaN;
-					}
-
-					double stageThrust = stageNominalThrust;
-
-					return burnTime(currManeuverDeltaV.Value, totalMass, stageMassFlow, stageThrust);
+					if (currManeuverDeltaV.Value == double.NaN)
+					{
+						return double.NaN;
+					}
+
+					return realVesselBurnTime(currManeuverDeltaV.Value);
 				},
 				"s"
 			);
@@ -937,14 +955,12 @@
 				"Burn Time Remaining",
 				delegate()
 				{
-					if (core.LastStage == null || currManeuverDVRemaining == double.NaN)
-					{
-						return double.NaN;
-					}
-
-					double stageThrust = stageNominalThrust;
-
-					return burnTime(currManeuverDVRemaining, totalMass, stageMassFlow, stageThrust);
+					if (currManeuverDVRemaining.Value == double.NaN)
+					{
+						return double.NaN;
+					}
+
+					return realVesselBurnTime(currManeuverDVRemaining.Value);
 				},
 				"s"
 			);
@@ -954,14 +970,12 @@
 				"Half Burn Time",
 				delegate()
 				{
-					if (core.LastStage == null || currManeuverDeltaV.Value == double.NaN)
-					{
-						return double.NaN;
-					}
-
-					double stageThrust = stageNominalThrust;
-
-					return burnTime(currManeuverDeltaV.Value / 2d, totalMass, stageMassFlow, stageThrust);
+					if (currManeuverDeltaV.Value == double.NaN)
+					{
+						return double.NaN;
+					}
+
+					return realVesselBurnTime(currManeuverDeltaV.Value / 2d);
 				},
 				"s"
 			);
@@ -973,7 +987,7 @@
 				{
 					if (upcomingManeuverNodes > 1)
 					{
-						return core.vessel.patchedConicSolver.maneuverNodes[1].DeltaV.magnitude;
+						return Core.Vessel.patchedConicSolver.maneuverNodes[1].DeltaV.magnitude;
 					}
 					else
 					{
@@ -992,58 +1006,58 @@
 				VOID_Localization.void_primary,
 				delegate()
 				{
-					if (core.vessel == null)
+					if (Core.Vessel == null)
 					{
 						return string.Empty;
 					}
-					return core.vessel.mainBody.name;
+					return Core.Vessel.mainBody.name;
 				}
 			);
 
 		public static readonly VOID_DoubleValue orbitAltitude =
 			new VOID_DoubleValue(
 				"Altitude (ASL)",
-				new Func<double>(() => core.vessel.orbit.altitude),
+				new Func<double>(() => Core.Vessel.orbit.altitude),
 				"m"
 			);
 
 		public static readonly VOID_DoubleValue orbitVelocity =
 			new VOID_DoubleValue(
 				VOID_Localization.void_velocity,
-				new Func<double>(() => core.vessel.orbit.vel.magnitude),
+				new Func<double>(() => Core.Vessel.orbit.vel.magnitude),
 				"m/s"
 			);
 
 		public static readonly VOID_DoubleValue orbitApoAlt =
 			new VOID_DoubleValue(
 				VOID_Localization.void_apoapsis,
-				new Func<double>(() => core.vessel.orbit.ApA),
+				new Func<double>(() => Core.Vessel.orbit.ApA),
 				"m"
 			);
 
 		public static readonly VOID_DoubleValue oribtPeriAlt =
 			new VOID_DoubleValue(
 				VOID_Localization.void_periapsis,
-				new Func<double>(() => core.vessel.orbit.PeA),
+				new Func<double>(() => Core.Vessel.orbit.PeA),
 				"m"
 			);
 
 		public static readonly VOID_StrValue timeToApo =
 			new VOID_StrValue(
 				"Time to Apoapsis",
-				new Func<string>(() => VOID_Tools.ConvertInterval(core.vessel.orbit.timeToAp))
+				new Func<string>(() => VOID_Tools.FormatInterval(Core.Vessel.orbit.timeToAp))
 			);
 
 		public static readonly VOID_StrValue timeToPeri =
 			new VOID_StrValue(
 				"Time to Periapsis",
-				new Func<string>(() => VOID_Tools.ConvertInterval(core.vessel.orbit.timeToPe))
+				new Func<string>(() => VOID_Tools.FormatInterval(Core.Vessel.orbit.timeToPe))
 			);
 
 		public static readonly VOID_DoubleValue orbitInclination =
 			new VOID_DoubleValue(
 				"Inclination",
-				new Func<double>(() => core.vessel.orbit.inclination),
+				new Func<double>(() => Core.Vessel.orbit.inclination),
 				"°"
 			);
 
@@ -1052,9 +1066,9 @@
 				"Gravity",
 				delegate()
 				{
-					double orbitRadius = core.vessel.mainBody.Radius +
-					                   core.vessel.mainBody.GetAltitude(core.vessel.findWorldCenterOfMass());
-					return (VOID_Core.Constant_G * core.vessel.mainBody.Mass) /
+					double orbitRadius = Core.Vessel.mainBody.Radius +
+					                     Core.Vessel.mainBody.GetAltitude(Core.Vessel.findWorldCenterOfMass());
+					return (VOIDCore.Constant_G * Core.Vessel.mainBody.Mass) /
 					(orbitRadius * orbitRadius);
 				},
 				"m/s²"
@@ -1063,63 +1077,107 @@
 		public static readonly VOID_StrValue orbitPeriod =
 			new VOID_StrValue(
 				"Period",
-				new Func<string>(() => VOID_Tools.ConvertInterval(core.vessel.orbit.period))
+				new Func<string>(() => VOID_Tools.FormatInterval(Core.Vessel.orbit.period))
 			);
 
 		public static readonly VOID_DoubleValue semiMajorAxis =
 			new VOID_DoubleValue(
 				"Semi-Major Axis",
-				new Func<double>(() => core.vessel.orbit.semiMajorAxis),
+				new Func<double>(() => Core.Vessel.orbit.semiMajorAxis),
 				"m"
 			);
 
 		public static readonly VOID_DoubleValue eccentricity =
 			new VOID_DoubleValue(
 				"Eccentricity",
-				new Func<double>(() => core.vessel.orbit.eccentricity),
+				new Func<double>(() => Core.Vessel.orbit.eccentricity),
 				""
 			);
 
 		public static readonly VOID_DoubleValue meanAnomaly =
 			new VOID_DoubleValue(
 				"Mean Anomaly",
-				new Func<double>(() => core.vessel.orbit.meanAnomaly * 180d / Math.PI),
+				new Func<double>(() => Core.Vessel.orbit.meanAnomaly * 180d / Math.PI),
 				"°"
 			);
 
 		public static readonly VOID_DoubleValue trueAnomaly = 
 			new VOID_DoubleValue(
 				"True Anomaly",
-				new Func<double>(() => core.vessel.orbit.trueAnomaly),
+				new Func<double>(() => Core.Vessel.orbit.trueAnomaly),
 				"°"
 			);
 
 		public static readonly VOID_DoubleValue eccAnomaly =
 			new VOID_DoubleValue(
 				"Eccentric Anomaly",
-				new Func<double>(() => core.vessel.orbit.eccentricAnomaly * 180d / Math.PI),
+				new Func<double>(() => Core.Vessel.orbit.eccentricAnomaly * 180d / Math.PI),
 				"°"
 			);
 
 		public static readonly VOID_DoubleValue longitudeAscNode =
 			new VOID_DoubleValue(
 				"Long. Ascending Node",
-				new Func<double>(() => core.vessel.orbit.LAN),
+				new Func<double>(() => Core.Vessel.orbit.LAN),
 				"°"
 			);
 
 		public static readonly VOID_DoubleValue argumentPeriapsis =
 			new VOID_DoubleValue(
 				"Argument of Periapsis",
-				new Func<double>(() => core.vessel.orbit.argumentOfPeriapsis),
+				new Func<double>(() => Core.Vessel.orbit.argumentOfPeriapsis),
 				"°"
+			);
+
+		public static readonly VOID_StrValue timeToAscendingNode =
+			new VOID_StrValue(
+				"Time to Ascending Node",
+				delegate()
+				{
+					double trueAnomalyAscNode = 360d - argumentPeriapsis;
+					double dTAscNode = Core.Vessel.orbit.GetDTforTrueAnomaly(
+						                   trueAnomalyAscNode * Mathf.Deg2Rad,
+						                   Core.Vessel.orbit.period
+					                   );
+
+					dTAscNode %= Core.Vessel.orbit.period;
+
+					if (dTAscNode < 0d)
+					{
+						dTAscNode += Core.Vessel.orbit.period;
+					}
+
+					return VOID_Tools.FormatInterval(dTAscNode);
+				}
+			);
+
+		public static readonly VOID_StrValue timeToDescendingNode =
+			new VOID_StrValue(
+				"Time to Descending Node",
+				delegate()
+				{
+					double trueAnomalyAscNode = 180d - argumentPeriapsis;
+					double dTDescNode = Core.Vessel.orbit.GetDTforTrueAnomaly(
+						                    trueAnomalyAscNode * Mathf.Deg2Rad,
+						                    Core.Vessel.orbit.period
+					                    );
+
+					dTDescNode %= Core.Vessel.orbit.period;
+
+					if (dTDescNode < 0d)
+					{
+						dTDescNode += Core.Vessel.orbit.period;
+					}
+
+					return VOID_Tools.FormatInterval(dTDescNode);
+				}
 			);
 
 		public static readonly VOID_DoubleValue localSiderealLongitude =
 			new VOID_DoubleValue(
 				"Local Sidereal Longitude",
 				new Func<double>(() => VOID_Tools.FixDegreeDomain(
-						core.vessel.longitude + core.vessel.orbit.referenceBody.rotationAngle)),
+					Core.Vessel.longitude + Core.Vessel.orbit.referenceBody.rotationAngle)),
 				"°"
 			);
 
@@ -1130,13 +1188,23 @@
 		public static readonly VOID_StrValue expSituation =
 			new VOID_StrValue(
 				"Situation",
-				new Func<string>(() => core.vessel.GetExperimentSituation().HumanString())
+				new Func<string>(() => Core.Vessel.GetExperimentSituation().HumanString())
 			);
 
 		public static readonly VOID_StrValue currBiome =
 			new VOID_StrValue(
 				"Biome",
-				new Func<string>(() => VOID_Tools.GetBiome(core.vessel).name)
+				delegate()
+				{
+					if (Core.Vessel.landedAt == string.Empty)
+					{
+						return VOID_Tools.GetBiome(Core.Vessel).name;
+					}
+					else
+					{
+						return Core.Vessel.landedAt;
+					}
+				}
 			);
 
 		#endregion
@@ -1146,7 +1214,7 @@
 		public static readonly VOID_DoubleValue terrainElevation =
 			new VOID_DoubleValue(
 				"Terrain elevation",
-				new Func<double>(() => core.vessel.terrainAltitude),
+				new Func<double>(() => Core.Vessel.terrainAltitude),
 				"m"
 			);
 
@@ -1155,10 +1223,10 @@
 		private static double burnTime(double deltaV, double initialMass, double massFlow, double thrust)
 		{
 			Tools.PostDebugMessage(typeof(VOID_Data), "calculating burnTime from:\n" +
-				"\tdeltaV: {0}\n" +
-				"\tinitialMass: {1}\n" +
-				"\tmassFlow: {2}\n" +
-				"\tthrust: {3}\n",
+			"\tdeltaV: {0}\n" +
+			"\tinitialMass: {1}\n" +
+			"\tmassFlow: {2}\n" +
+			"\tthrust: {3}\n",
 				deltaV,
 				initialMass,
 				massFlow,
@@ -1166,7 +1234,46 @@
 			);
 			return initialMass / massFlow * (1d - Math.Exp(-deltaV * massFlow / thrust));
 		}
+
+		private static double dVfromBurnTime(double time, double initialMass, double massFlow, double thrust)
+		{
+			return -thrust / massFlow * Math.Log(1d - time * massFlow / initialMass);
+		}
+
+		private static double realVesselBurnTime(double deltaV)
+		{
+			if (Core.Stages == null || Core.Stages.Length < 1)
+			{
+				return double.NaN;
+			}
+
+			double burntime = 0d;
+			double dVRemaining = deltaV;
+
+			int stageIdx = Core.Stages.Length - 1;
+
+			while (dVRemaining > double.Epsilon)
+			{
+				if (stageIdx < 0)
+				{
+					return double.PositiveInfinity;
+				}
+
+				Stage stage = Core.Stages[stageIdx];
+
+				if (stage.deltaV > 0)
+				{
+					double stageDVUsed = Math.Min(stage.deltaV, dVRemaining);
+
+					burntime += burnTime(stageDVUsed, stage.totalMass, stage.MassFlow(), stage.NominalThrust());
+					dVRemaining -= stageDVUsed;
+				}
+
+				stageIdx--;
+			}
+
+			return burntime;
+		}
 	}
 }
 
-