VOID_DataLogger: Added Apo/Peri Altitudes.
VOID_DataLogger: Added Apo/Peri Altitudes.

// VOID // VOID
// //
// VOID_Data.cs // VOID_Data.cs
// //
// Copyright © 2014, toadicus // Copyright © 2014, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// 3. Neither the name of the copyright holder nor the names of its contributors may be used // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior written permission. // to endorse or promote products derived from this software without specific prior written permission.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // 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. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
using KerbalEngineer.VesselSimulator; using KerbalEngineer.VesselSimulator;
using KSP; using KSP;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ToadicusTools; using ToadicusTools;
using UnityEngine; using UnityEngine;
   
namespace VOID namespace VOID
{ {
public static class VOID_Data public static class VOID_Data
{ {
private static Dictionary<int, IVOID_DataValue> dataValues = new Dictionary<int, IVOID_DataValue>(); private static Dictionary<int, IVOID_DataValue> dataValues = new Dictionary<int, IVOID_DataValue>();
   
public static Dictionary<int, IVOID_DataValue> DataValues public static Dictionary<int, IVOID_DataValue> DataValues
{ {
get get
{ {
return dataValues; return dataValues;
} }
} }
   
#region Constants #region Constants
   
private static double kerbinGee; private static double kerbinGee;
   
public static double KerbinGee public static double KerbinGee
{ {
get get
{ {
if (kerbinGee == default(double)) if (kerbinGee == default(double))
{ {
kerbinGee = Core.HomeBody.gravParameter / (Core.HomeBody.Radius * Core.HomeBody.Radius); kerbinGee = Core.HomeBody.gravParameter / (Core.HomeBody.Radius * Core.HomeBody.Radius);
} }
   
return kerbinGee; return kerbinGee;
} }
} }
   
#endregion #endregion
   
#region Core Data #region Core Data
   
public static VOIDCore Core public static VOIDCore Core
{ {
get get
{ {
if (!CoreInitialized) if (!CoreInitialized)
{ {
return null; return null;
} }
   
switch (HighLogic.LoadedScene) switch (HighLogic.LoadedScene)
{ {
case GameScenes.EDITOR: case GameScenes.EDITOR:
return (VOIDCore)VOIDCore_Editor.Instance; return (VOIDCore)VOIDCore_Editor.Instance;
case GameScenes.FLIGHT: case GameScenes.FLIGHT:
return (VOIDCore)VOIDCore_Flight.Instance; return (VOIDCore)VOIDCore_Flight.Instance;
case GameScenes.SPACECENTER: case GameScenes.SPACECENTER:
return (VOIDCore)VOIDCore_SpaceCentre.Instance; return (VOIDCore)VOIDCore_SpaceCentre.Instance;
default: default:
return null; return null;
} }
} }
} }
   
public static bool CoreInitialized public static bool CoreInitialized
{ {
get get
{ {
switch (HighLogic.LoadedScene) switch (HighLogic.LoadedScene)
{ {
case GameScenes.EDITOR: case GameScenes.EDITOR:
return VOIDCore_Editor.Initialized; return VOIDCore_Editor.Initialized;
case GameScenes.FLIGHT: case GameScenes.FLIGHT:
return VOIDCore_Flight.Initialized; return VOIDCore_Flight.Initialized;
case GameScenes.SPACECENTER: case GameScenes.SPACECENTER:
return VOIDCore_SpaceCentre.Initialized; return VOIDCore_SpaceCentre.Initialized;
default: default:
return false; return false;
} }
} }
} }
   
#endregion #endregion
   
#region Atmosphere #region Atmosphere
   
public static readonly VOID_DoubleValue atmDensity = public static readonly VOID_DoubleValue atmDensity =
new VOID_DoubleValue( new VOID_DoubleValue(
"Atmosphere Density", "Atmosphere Density",
new Func<double>(() => Core.Vessel.atmDensity * 1000f), new Func<double>(() => Core.Vessel.atmDensity * 1000f),
"g/m³" "g/m³"
); );
   
public static readonly VOID_FloatValue atmLimit = public static readonly VOID_FloatValue atmLimit =
new VOID_FloatValue( new VOID_FloatValue(
"Atmosphere Limit", "Atmosphere Limit",
new Func<float>(() => Core.Vessel.mainBody.maxAtmosphereAltitude), new Func<float>(() => Core.Vessel.mainBody.maxAtmosphereAltitude),
"m" "m"
); );
   
public static readonly VOID_DoubleValue atmPressure = public static readonly VOID_DoubleValue atmPressure =
new VOID_DoubleValue( new VOID_DoubleValue(
"Pressure", "Pressure",
new Func<double>(() => Core.Vessel.staticPressure), new Func<double>(() => Core.Vessel.staticPressure),
"atm" "atm"
); );
   
public static readonly VOID_FloatValue temperature = public static readonly VOID_FloatValue temperature =
new VOID_FloatValue( new VOID_FloatValue(
"Temperature", "Temperature",
new Func<float>(() => Core.Vessel.flightIntegrator.getExternalTemperature()), new Func<float>(() => Core.Vessel.flightIntegrator.getExternalTemperature()),
"°C" "°C"
); );
   
#endregion #endregion
   
#region Attitude #region Attitude
   
public static readonly VOID_StrValue vesselHeading = public static readonly VOID_StrValue vesselHeading =
new VOID_StrValue( new VOID_StrValue(
"Heading", "Heading",
delegate() delegate()
{ {
double heading = Core.Vessel.getSurfaceHeading(); double heading = Core.Vessel.getSurfaceHeading();
string cardinal = VOID_Tools.get_heading_text(heading); string cardinal = VOID_Tools.get_heading_text(heading);
   
return string.Format( return string.Format(
"{0}° {1}", "{0}° {1}",
heading.ToString("F2"), heading.ToString("F2"),
cardinal cardinal
); );
} }
); );
   
public static readonly VOID_DoubleValue vesselPitch = public static readonly VOID_DoubleValue vesselPitch =
new VOID_DoubleValue( new VOID_DoubleValue(
"Pitch", "Pitch",
() => Core.Vessel.getSurfacePitch(), () => Core.Vessel.getSurfacePitch(),
"°" "°"
); );
   
#endregion #endregion
   
#region Career #region Career
   
public static readonly VOID_StrValue fundingStatus = public static readonly VOID_StrValue fundingStatus =
new VOID_StrValue( new VOID_StrValue(
string.Intern("Funds"), string.Intern("Funds"),
delegate() delegate()
{ {
if (VOID_CareerStatus.Instance == null) if (VOID_CareerStatus.Instance == null)
{ {
return string.Empty; return string.Empty;
} }
   
return string.Format("{0} ({1})", return string.Format("{0} ({1})",
VOID_CareerStatus.Instance.currentFunds.ToString("#,#.##"), VOID_CareerStatus.Instance.currentFunds.ToString("#,#.##"),
VOID_CareerStatus.formatDelta(VOID_CareerStatus.Instance.lastFundsChange) VOID_CareerStatus.formatDelta(VOID_CareerStatus.Instance.lastFundsChange)
); );
} }
); );
   
public static readonly VOID_StrValue reputationStatus = public static readonly VOID_StrValue reputationStatus =
new VOID_StrValue( new VOID_StrValue(
string.Intern("Reputation"), string.Intern("Reputation"),
delegate() delegate()
{ {
if (VOID_CareerStatus.Instance == null) if (VOID_CareerStatus.Instance == null)
{ {
return string.Empty; return string.Empty;
} }
   
return string.Format("{0} ({1})", return string.Format("{0} ({1})",
VOID_CareerStatus.Instance.currentReputation.ToString("#,#.##"), VOID_CareerStatus.Instance.currentReputation.ToString("#,#.##"),
VOID_CareerStatus.formatDelta(VOID_CareerStatus.Instance.lastRepChange) VOID_CareerStatus.formatDelta(VOID_CareerStatus.Instance.lastRepChange)
); );
} }
); );
   
public static readonly VOID_StrValue scienceStatus = public static readonly VOID_StrValue scienceStatus =
new VOID_StrValue( new VOID_StrValue(
string.Intern("Science"), string.Intern("Science"),
delegate() delegate()
{ {
if (VOID_CareerStatus.Instance == null) if (VOID_CareerStatus.Instance == null)
{ {
return string.Empty; return string.Empty;
} }
   
return string.Format("{0} ({1})", return string.Format("{0} ({1})",
VOID_CareerStatus.Instance.currentScience.ToString("#,#.##"), VOID_CareerStatus.Instance.currentScience.ToString("#,#.##"),
VOID_CareerStatus.formatDelta(VOID_CareerStatus.Instance.lastScienceChange) VOID_CareerStatus.formatDelta(VOID_CareerStatus.Instance.lastScienceChange)
); );
} }
); );
   
#endregion #endregion
   
#region Control #region Control
   
public static readonly VOID_FloatValue mainThrottle = public static readonly VOID_FloatValue mainThrottle =
new VOID_FloatValue( new VOID_FloatValue(
"Throttle", "Throttle",
new Func<float>(() => Core.Vessel.ctrlState.mainThrottle * 100f), new Func<float>(() => Core.Vessel.ctrlState.mainThrottle * 100f),
"%" "%"
); );
   
#endregion #endregion
   
#region Engineering #region Engineering
   
public static readonly VOID_IntValue partCount = public static readonly VOID_IntValue partCount =
new VOID_IntValue( new VOID_IntValue(
"Parts", "Parts",
new Func<int>(() => Core.Vessel.Parts.Count), new Func<int>(() => Core.Vessel.Parts.Count),
"" ""
); );
   
#region Mass #region Mass
   
public static readonly VOID_StrValue comboResourceMass = public static readonly VOID_StrValue comboResourceMass =
new VOID_StrValue( new VOID_StrValue(
"Resource Mass (curr / total)", "Resource Mass (curr / total)",
delegate() delegate()
{ {
return string.Format("{0} / {1}", return string.Format("{0} / {1}",
stageResourceMass.ValueUnitString("F3"), stageResourceMass.ValueUnitString("F3"),
resourceMass.ValueUnitString("F3") resourceMass.ValueUnitString("F3")
); );
} }
); );
   
public static readonly VOID_DoubleValue resourceMass = public static readonly VOID_DoubleValue resourceMass =
new VOID_DoubleValue( new VOID_DoubleValue(
"Resource Mass", "Resource Mass",
delegate() delegate()
{ {
if (Core.Stages == null || Core.LastStage == null) if (Core.Stages == null || Core.LastStage == null)
{ {
return double.NaN; return double.NaN;
} }
   
  return Core.LastStage.totalResourceMass;
  },
  "tons"
  );
   
  public static readonly VOID_DoubleValue stageResourceMass =
  new VOID_DoubleValue(
  "Resource Mass (Stage)",
  delegate()
  {
  if (Core.LastStage == null)
  {
  return double.NaN;
  }
   
return Core.LastStage.resourceMass; return Core.LastStage.resourceMass;
},  
"tons"  
);  
   
public static readonly VOID_DoubleValue stageResourceMass =  
new VOID_DoubleValue(  
"Resource Mass (Stage)",  
delegate()  
{  
if (Core.LastStage == null)  
{  
return double.NaN;  
}  
   
return Core.LastStage.totalResourceMass;  
}, },
"tons" "tons"
); );
   
public static readonly VOID_DoubleValue totalMass = public static readonly VOID_DoubleValue totalMass =
new VOID_DoubleValue( new VOID_DoubleValue(
"Total Mass", "Total Mass",
delegate() delegate()
{ {
if (Core.Stages == null || Core.LastStage == null) if (Core.Stages == null || Core.LastStage == null)
{ {
return double.NaN; return double.NaN;
} }
   
return Core.LastStage.totalMass; return Core.LastStage.totalMass;
}, },
"tons" "tons"
); );
   
#endregion #endregion
   
#region DeltaV #region DeltaV
   
public static readonly VOID_DoubleValue stageDeltaV = public static readonly VOID_DoubleValue stageDeltaV =
new VOID_DoubleValue( new VOID_DoubleValue(
"DeltaV (Current Stage)", "DeltaV (Current Stage)",
delegate() delegate()
{ {
if (Core.Stages == null || Core.LastStage == null) if (Core.Stages == null || Core.LastStage == null)
return double.NaN; return double.NaN;
return Core.LastStage.deltaV; return Core.LastStage.deltaV;
}, },
"m/s" "m/s"
); );
   
public static readonly VOID_DoubleValue totalDeltaV = public static readonly VOID_DoubleValue totalDeltaV =
new VOID_DoubleValue( new VOID_DoubleValue(
"DeltaV (Total)", "DeltaV (Total)",
delegate() delegate()
{ {
if (Core.Stages == null || Core.LastStage == null) if (Core.Stages == null || Core.LastStage == null)
return double.NaN; return double.NaN;
return Core.LastStage.totalDeltaV; return Core.LastStage.totalDeltaV;
}, },
"m/s" "m/s"
); );
   
#endregion #endregion
   
#region Propulsion #region Propulsion
   
public static readonly VOID_StrValue currmaxThrustWeight = public static readonly VOID_StrValue currmaxThrustWeight =
new VOID_StrValue( new VOID_StrValue(
"T:W (curr/max)", "T:W (curr/max)",
delegate() delegate()
{ {
if (Core.Stages == null || Core.LastStage == null) if (Core.Stages == null || Core.LastStage == null)
return "N/A"; return "N/A";
   
return string.Format( return string.Format(
"{0} / {1}", "{0} / {1}",
(VOID_Data.currThrustWeight.Value).ToString("F2"), (VOID_Data.currThrustWeight.Value).ToString("F2"),
(VOID_Data.maxThrustWeight.Value).ToString("F2") (VOID_Data.maxThrustWeight.Value).ToString("F2")
); );
} }
); );
   
public static readonly VOID_StrValue currmaxThrust = public static readonly VOID_StrValue currmaxThrust =
new VOID_StrValue( new VOID_StrValue(
"Thrust (curr/max)", "Thrust (curr/max)",
delegate() delegate()
{ {
if (Core.Stages == null || Core.LastStage == null) if (Core.Stages == null || Core.LastStage == null)
return "N/A"; return "N/A";
   
double currThrust = Core.LastStage.actualThrust; double currThrust = Core.LastStage.actualThrust;
double maxThrust = Core.LastStage.thrust; double maxThrust = Core.LastStage.thrust;
   
return string.Format( return string.Format(
"{0} / {1}", "{0} / {1}",
currThrust.ToString("F1"), currThrust.ToString("F1"),
maxThrust.ToString("F1") maxThrust.ToString("F1")
); );
} }
); );
   
public static readonly VOID_DoubleValue stageMassFlow = public static readonly VOID_DoubleValue stageMassFlow =
new VOID_DoubleValue( new VOID_DoubleValue(
"Stage Mass Flow", "Stage Mass Flow",
delegate() delegate()
{ {
if (Core.LastStage == null) if (Core.LastStage == null)
{ {
return double.NaN; return double.NaN;
} }
   
return Core.LastStage.MassFlow(); return Core.LastStage.MassFlow();
}, },
"Mg/s" "Mg/s"
); );
   
public static readonly VOID_DoubleValue stageNominalThrust = public static readonly VOID_DoubleValue stageNominalThrust =
new VOID_DoubleValue( new VOID_DoubleValue(
"Nominal Stage Thrust", "Nominal Stage Thrust",
delegate() delegate()
{ {
if (Core.LastStage == null) if (Core.LastStage == null)
{ {
return double.NaN; return double.NaN;
} }
   
return Core.LastStage.NominalThrust(); return Core.LastStage.NominalThrust();
}, },
"kN" "kN"
); );
   
#endregion #endregion
   
#region Kinetics #region Kinetics
   
public static readonly VOID_DoubleValue currThrustWeight = public static readonly VOID_DoubleValue currThrustWeight =
new VOID_DoubleValue( new VOID_DoubleValue(
"T:W Ratio", "T:W Ratio",
delegate() delegate()
{ {
if (Core.LastStage == null) if (Core.LastStage == null)
{ {
return double.NaN; return double.NaN;
} }
   
return Core.LastStage.actualThrustToWeight; return Core.LastStage.actualThrustToWeight;
}, },
"" ""
); );
   
   
   
public static readonly VOID_DoubleValue maxThrustWeight = public static readonly VOID_DoubleValue maxThrustWeight =
new VOID_DoubleValue( new VOID_DoubleValue(
"T:W Ratio", "T:W Ratio",
delegate() delegate()
{ {
if (Core.LastStage == null) if (Core.LastStage == null)
{ {
return double.NaN; return double.NaN;
} }
   
return Core.LastStage.thrustToWeight; return Core.LastStage.thrustToWeight;
}, },
"" ""
); );
   
public static readonly VOID_DoubleValue nominalThrustWeight = public static readonly VOID_DoubleValue nominalThrustWeight =
new VOID_DoubleValue( new VOID_DoubleValue(
"Thrust-to-Weight Ratio", "Thrust-to-Weight Ratio",
delegate() delegate()
{ {
if (HighLogic.LoadedSceneIsEditor || currThrustWeight.Value == 0d) if (HighLogic.LoadedSceneIsEditor || currThrustWeight.Value == 0d)
{ {
return maxThrustWeight.Value; return maxThrustWeight.Value;
} }
   
return currThrustWeight.Value; return currThrustWeight.Value;
}, },
"" ""
); );
   
public static readonly VOID_DoubleValue surfaceThrustWeight = public static readonly VOID_DoubleValue surfaceThrustWeight =
new VOID_DoubleValue( new VOID_DoubleValue(
"Max T:W @ surface", "Max T:W @ surface",
delegate() delegate()
{ {
if (Core.Stages == null || Core.LastStage == null) if (Core.Stages == null || Core.LastStage == null)
return double.NaN; return double.NaN;
   
double maxThrust = Core.LastStage.thrust; double maxThrust = Core.LastStage.thrust;
double mass = Core.LastStage.totalMass; double mass = Core.LastStage.totalMass;
double gravity = (VOIDCore.Constant_G * Core.Vessel.mainBody.Mass) / double gravity = (VOIDCore.Constant_G * Core.Vessel.mainBody.Mass) /
(Core.Vessel.mainBody.Radius * Core.Vessel.mainBody.Radius); (Core.Vessel.mainBody.Radius * Core.Vessel.mainBody.Radius);
double weight = mass * gravity; double weight = mass * gravity;
   
return maxThrust / weight; return maxThrust / weight;
}, },
"" ""
); );
   
public static readonly VOID_Vector3dValue vesselThrustOffset = public static readonly VOID_Vector3dValue vesselThrustOffset =
new VOID_Vector3dValue( new VOID_Vector3dValue(
"Thrust Offset", "Thrust Offset",
delegate() delegate()
{ {
if (Core.Vessel == null) if (Core.Vessel == null)
{ {
return Vector3d.zero; return Vector3d.zero;
} }
   
List<PartModule> engineModules = Core.Vessel.getModulesOfType<PartModule>(); List<PartModule> engineModules = Core.Vessel.getModulesOfType<PartModule>();
   
Vector3d thrustPos = Vector3d.zero; Vector3d thrustPos = Vector3d.zero;
Vector3d thrustDir = Vector3d.zero; Vector3d thrustDir = Vector3d.zero;
float thrust = 0; float thrust = 0;
   
foreach (PartModule engine in engineModules) foreach (PartModule engine in engineModules)
{ {
float moduleThrust = 0; float moduleThrust = 0;
   
switch (engine.moduleName) switch (engine.moduleName)
{ {
case "ModuleEngines": case "ModuleEngines":
case "ModuleEnginesFX": case "ModuleEnginesFX":
break; break;
default: default:
continue; continue;
} }
   
if (!engine.isEnabled) if (!engine.isEnabled)
{ {
continue; continue;
} }
   
CenterOfThrustQuery cotQuery = new CenterOfThrustQuery(); CenterOfThrustQuery cotQuery = new CenterOfThrustQuery();
   
if (engine is ModuleEngines) if (engine is ModuleEngines)
{ {
ModuleEngines engineModule = engine as ModuleEngines; ModuleEngines engineModule = engine as ModuleEngines;
   
moduleThrust = engineModule.finalThrust; moduleThrust = engineModule.finalThrust;
   
engineModule.OnCenterOfThrustQuery(cotQuery); engineModule.OnCenterOfThrustQuery(cotQuery);
} }
else // engine is ModuleEnginesFX else // engine is ModuleEnginesFX
{ {
ModuleEnginesFX engineFXModule = engine as ModuleEnginesFX; ModuleEnginesFX engineFXModule = engine as ModuleEnginesFX;
   
moduleThrust = engineFXModule.finalThrust; moduleThrust = engineFXModule.finalThrust;
   
engineFXModule.OnCenterOfThrustQuery(cotQuery); engineFXModule.OnCenterOfThrustQuery(cotQuery);
} }
   
if (moduleThrust != 0d) if (moduleThrust != 0d)
{ {
cotQuery.thrust = moduleThrust; cotQuery.thrust = moduleThrust;
} }
   
thrustPos += cotQuery.pos * cotQuery.thrust; thrustPos += cotQuery.pos * cotQuery.thrust;
thrustDir += cotQuery.dir * cotQuery.thrust; thrustDir += cotQuery.dir * cotQuery.thrust;
thrust += cotQuery.thrust; thrust += cotQuery.thrust;
} }
   
if (thrust != 0) if (thrust != 0)
{ {
thrustPos /= thrust; thrustPos /= thrust;
thrustDir /= thrust; thrustDir /= thrust;
} }
   
Transform vesselTransform = Core.Vessel.transform; Transform vesselTransform = Core.Vessel.transform;
   
thrustPos = vesselTransform.InverseTransformPoint(thrustPos); thrustPos = vesselTransform.InverseTransformPoint(thrustPos);
thrustDir = vesselTransform.InverseTransformDirection(thrustDir); thrustDir = vesselTransform.InverseTransformDirection(thrustDir);
   
Vector3d thrustOffset = VectorTools.PointDistanceToLine( Vector3d thrustOffset = VectorTools.PointDistanceToLine(
thrustPos, thrustDir.normalized, Core.Vessel.findLocalCenterOfMass()); thrustPos, thrustDir.normalized, Core.Vessel.findLocalCenterOfMass());
   
Tools.PostDebugMessage(typeof(VOID_Data), "vesselThrustOffset:\n" + Tools.PostDebugMessage(typeof(VOID_Data), "vesselThrustOffset:\n" +
"\tthrustPos: {0}\n" + "\tthrustPos: {0}\n" +
"\tthrustDir: {1}\n" + "\tthrustDir: {1}\n" +
"\tthrustOffset: {2}\n" + "\tthrustOffset: {2}\n" +
"\tvessel.CoM: {3}", "\tvessel.CoM: {3}",
thrustPos, thrustPos,
thrustDir.normalized, thrustDir.normalized,
thrustOffset, thrustOffset,
Core.Vessel.findWorldCenterOfMass() Core.Vessel.findWorldCenterOfMass()
); );
   
return thrustOffset; return thrustOffset;
}, },
"m" "m"
); );
   
#endregion #endregion
   
#region Air Breathing #region Air Breathing
   
public static readonly VOID_StrValue intakeAirStatus = public static readonly VOID_StrValue intakeAirStatus =
new VOID_StrValue( new VOID_StrValue(
"Intake Air (Curr / Req)", "Intake Air (Curr / Req)",
delegate() delegate()
{ {
double currentAmount; double currentAmount;
double currentRequirement; double currentRequirement;
   
currentAmount = 0d; currentAmount = 0d;
currentRequirement = 0d; currentRequirement = 0d;
   
foreach (Part part in Core.Vessel.Parts) foreach (Part part in Core.Vessel.Parts)
{ {
if (part.enabled) if (part.enabled)
{ {
ModuleEngines engineModule; ModuleEngines engineModule;
ModuleEnginesFX enginesFXModule; ModuleEnginesFX enginesFXModule;
List<Propellant> propellantList = null; List<Propellant> propellantList = null;
   
if (part.tryGetFirstModuleOfType<ModuleEngines>(out engineModule)) if (part.tryGetFirstModuleOfType<ModuleEngines>(out engineModule))
{ {
propellantList = engineModule.propellants; propellantList = engineModule.propellants;
} }
else if (part.tryGetFirstModuleOfType<ModuleEnginesFX>(out enginesFXModule)) else if (part.tryGetFirstModuleOfType<ModuleEnginesFX>(out enginesFXModule))
{ {
propellantList = enginesFXModule.propellants; propellantList = enginesFXModule.propellants;
} }
   
if (propellantList != null) if (propellantList != null)
{ {
foreach (Propellant propellant in propellantList) foreach (Propellant propellant in propellantList)
{ {
if (propellant.name == "IntakeAir") if (propellant.name == "IntakeAir")
{ {
currentRequirement += propellant.currentRequirement / TimeWarp.fixedDeltaTime; currentRequirement += propellant.currentRequirement / TimeWarp.fixedDeltaTime;
break; break;
} }
} }
} }
} }
   
ModuleResourceIntake intakeModule; ModuleResourceIntake intakeModule;
   
if (part.enabled && part.tryGetFirstModuleOfType<ModuleResourceIntake>(out intakeModule)) if (part.enabled && part.tryGetFirstModuleOfType<ModuleResourceIntake>(out intakeModule))
{ {
if (intakeModule.resourceName == "IntakeAir") if (intakeModule.resourceName == "IntakeAir")
{ {
currentAmount += intakeModule.airFlow; currentAmount += intakeModule.airFlow;
} }
} }
} }
   
if (currentAmount == 0 && currentRequirement == 0) if (currentAmount == 0 && currentRequirement == 0)
{ {
return "N/A"; return "N/A";
} }
   
return string.Format("{0:F3} / {1:F3}", currentAmount, currentRequirement); return string.Format("{0:F3} / {1:F3}", currentAmount, currentRequirement);
} }
); );
   
#endregion #endregion
   
#region Crew #region Crew
   
public static readonly VOID_IntValue vesselCrewCount = public static readonly VOID_IntValue vesselCrewCount =
new VOID_IntValue( new VOID_IntValue(
"Crew Onboard", "Crew Onboard",
delegate() delegate()
{ {
if (Core.Vessel != null) if (Core.Vessel != null)
{ {
return Core.Vessel.GetCrewCount(); return Core.Vessel.GetCrewCount();
} }
else else
{ {
return 0; return 0;
} }
}, },
"" ""
); );
   
public static readonly VOID_IntValue vesselCrewCapacity = public static readonly VOID_IntValue vesselCrewCapacity =
new VOID_IntValue( new VOID_IntValue(
"Crew Capacity", "Crew Capacity",
delegate() delegate()
{ {
if (Core.Vessel != null) if (Core.Vessel != null)
{ {
return Core.Vessel.GetCrewCapacity(); return Core.Vessel.GetCrewCapacity();
} }
else else
{ {
return 0; return 0;
} }
}, },
"" ""
); );
   
#endregion #endregion
   
#endregion #endregion
   
#region Location #region Location
   
public const double kscLongitude = 285.442323427289 * Math.PI / 180d; public const double kscLongitude = 285.442323427289 * Math.PI / 180d;
public const double kscLatitude = -0.0972112860655246 * Math.PI / 180d; public const double kscLatitude = -0.0972112860655246 * Math.PI / 180d;
   
public static readonly VOID_DoubleValue downrangeDistance = public static readonly VOID_DoubleValue downrangeDistance =
new VOID_DoubleValue( new VOID_DoubleValue(
"Downrange Distance", "Downrange Distance",
delegate() delegate()
{ {
   
if (Core.Vessel == null || if (Core.Vessel == null ||
Planetarium.fetch == null || Planetarium.fetch == null ||
Core.Vessel.mainBody != Planetarium.fetch.Home) Core.Vessel.mainBody != Planetarium.fetch.Home)
{ {
return double.NaN; return double.NaN;
} }
   
double vesselLongitude = Core.Vessel.longitude * Math.PI / 180d; double vesselLongitude = Core.Vessel.longitude * Math.PI / 180d;
double vesselLatitude = Core.Vessel.latitude * Math.PI / 180d; double vesselLatitude = Core.Vessel.latitude * Math.PI / 180d;
   
double diffLon = Math.Abs(vesselLongitude - kscLongitude); double diffLon = Math.Abs(vesselLongitude - kscLongitude);
   
double cosVesselLatitude = Math.Cos(vesselLatitude); double cosVesselLatitude = Math.Cos(vesselLatitude);
double sinDiffLon = Math.Sin(diffLon); double sinDiffLon = Math.Sin(diffLon);
   
double term1 = cosVesselLatitude * sinDiffLon; double term1 = cosVesselLatitude * sinDiffLon;
   
double cosKSCLatitude = Math.Cos(kscLatitude); double cosKSCLatitude = Math.Cos(kscLatitude);
double sinVesselLatitude = Math.Sin(vesselLatitude); double sinVesselLatitude = Math.Sin(vesselLatitude);
double sinKSCLatitude = Math.Sin(kscLatitude); double sinKSCLatitude = Math.Sin(kscLatitude);
double cosDiffLon = Math.Cos(diffLon); double cosDiffLon = Math.Cos(diffLon);
   
double term2 = cosKSCLatitude * sinVesselLatitude - sinKSCLatitude * cosVesselLatitude * cosDiffLon; double term2 = cosKSCLatitude * sinVesselLatitude - sinKSCLatitude * cosVesselLatitude * cosDiffLon;
   
double term3 = sinKSCLatitude * sinVesselLatitude + cosKSCLatitude * cosVesselLatitude * cosDiffLon; double term3 = sinKSCLatitude * sinVesselLatitude + cosKSCLatitude * cosVesselLatitude * cosDiffLon;
   
double arc = Math.Atan2(Math.Sqrt(term1 * term1 + term2 * term2), term3); double arc = Math.Atan2(Math.Sqrt(term1 * term1 + term2 * term2), term3);
   
return arc * Core.Vessel.mainBody.Radius; return arc * Core.Vessel.mainBody.Radius;
}, },
"m" "m"
); );
   
public static readonly VOID_StrValue surfLatitude = public static readonly VOID_StrValue surfLatitude =
new VOID_StrValue( new VOID_StrValue(
"Latitude", "Latitude",
new Func<string>(() => VOID_Tools.GetLatitudeString(Core.Vessel)) new Func<string>(() => VOID_Tools.GetLatitudeString(Core.Vessel))
); );
   
public static readonly VOID_StrValue surfLongitude = public static readonly VOID_StrValue surfLongitude =
new VOID_StrValue( new VOID_StrValue(
"Longitude", "Longitude",
new Func<string>(() => VOID_Tools.GetLongitudeString(Core.Vessel)) new Func<string>(() => VOID_Tools.GetLongitudeString(Core.Vessel))
); );
   
public static readonly VOID_DoubleValue trueAltitude = public static readonly VOID_DoubleValue trueAltitude =
new VOID_DoubleValue( new VOID_DoubleValue(
"Altitude (true)", "Altitude (true)",
delegate() 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, // HACK: This assumes that on worlds with oceans, all water is fixed at 0 m,
// and water covers the whole surface at 0 m. // and water covers the whole surface at 0 m.
if (Core.Vessel.terrainAltitude < 0 && Core.Vessel.mainBody.ocean) if (Core.Vessel.terrainAltitude < 0 && Core.Vessel.mainBody.ocean)
alt_true = Core.Vessel.orbit.altitude; alt_true = Core.Vessel.orbit.altitude;
return alt_true; return alt_true;
}, },
"m" "m"
); );
   
#endregion #endregion
   
#region Kinematics #region Kinematics
   
public static readonly VOID_DoubleValue geeForce = public static readonly VOID_DoubleValue geeForce =
new VOID_DoubleValue( new VOID_DoubleValue(
"G-force", "G-force",
new Func<double>(() => Core.Vessel.geeForce), new Func<double>(() => Core.Vessel.geeForce),
"gees" "gees"
); );
   
public static readonly VOID_DoubleValue horzVelocity = public static readonly VOID_DoubleValue horzVelocity =
new VOID_DoubleValue( new VOID_DoubleValue(
"Horizontal speed", "Horizontal speed",
new Func<double>(() => Core.Vessel.horizontalSrfSpeed), new Func<double>(() => Core.Vessel.horizontalSrfSpeed),
"m/s" "m/s"
); );
   
public static readonly VOID_DoubleValue surfVelocity = public static readonly VOID_DoubleValue surfVelocity =
new VOID_DoubleValue( new VOID_DoubleValue(
"Surface velocity", "Surface velocity",
new Func<double>(() => Core.Vessel.srf_velocity.magnitude), new Func<double>(() => Core.Vessel.srf_velocity.magnitude),
"m/s" "m/s"
); );
   
public static readonly VOID_DoubleValue vertVelocity = public static readonly VOID_DoubleValue vertVelocity =
new VOID_DoubleValue( new VOID_DoubleValue(
"Vertical speed", "Vertical speed",
new Func<double>(() => Core.Vessel.verticalSpeed), new Func<double>(() => Core.Vessel.verticalSpeed),
"m/s" "m/s"
); );
   
public static readonly VOID_DoubleValue vesselAccel = public static readonly VOID_DoubleValue vesselAccel =
new VOID_DoubleValue( new VOID_DoubleValue(
"Acceleration", "Acceleration",
() => geeForce * KerbinGee, () => geeForce * KerbinGee,
"m/s²" "m/s²"
); );
   
public static readonly VOID_DoubleValue vesselAngularVelocity = public static readonly VOID_DoubleValue vesselAngularVelocity =
new VOID_DoubleValue( new VOID_DoubleValue(
"Angular Velocity", "Angular Velocity",
delegate() delegate()
{ {
if (Core.Vessel != null) if (Core.Vessel != null)
{ {
return Core.Vessel.angularVelocity.magnitude; return Core.Vessel.angularVelocity.magnitude;
} }
else else
{ {
return double.NaN; return double.NaN;
} }
}, },
"rad/s" "rad/s"
); );
   
#endregion #endregion
   
#region Navigation #region Navigation
   
public static int upcomingManeuverNodes public static int upcomingManeuverNodes
{ {
get get
{ {
if (Core.Vessel == null || if (Core.Vessel == null ||
Core.Vessel.patchedConicSolver == null || Core.Vessel.patchedConicSolver == null ||
Core.Vessel.patchedConicSolver.maneuverNodes == null) Core.Vessel.patchedConicSolver.maneuverNodes == null)
{ {
return 0; return 0;
} }
   
return Core.Vessel.patchedConicSolver.maneuverNodes.Count; return Core.Vessel.patchedConicSolver.maneuverNodes.Count;
} }
} }
   
public static readonly VOID_StrValue burnTimeDoneAtNode = public static readonly VOID_StrValue burnTimeDoneAtNode =
new VOID_StrValue( new VOID_StrValue(
"Full burn time to be half done at node", "Full burn time to be half done at node",
delegate() delegate()
{ {
if (Core.LastStage == null && upcomingManeuverNodes < 1) if (Core.LastStage == null && upcomingManeuverNodes < 1)
{ {
return "N/A"; return "N/A";
} }
   
ManeuverNode node = Core.Vessel.patchedConicSolver.maneuverNodes[0]; ManeuverNode node = Core.Vessel.patchedConicSolver.maneuverNodes[0];
   
if ((node.UT - Planetarium.GetUniversalTime()) < 0) if ((node.UT - Planetarium.GetUniversalTime()) < 0)
{ {
return string.Empty; return string.Empty;
} }
   
double interval = (node.UT - currentNodeBurnDuration) - Planetarium.GetUniversalTime(); double interval = (node.UT - currentNodeBurnDuration) - Planetarium.GetUniversalTime();
   
if (double.IsNaN(interval)) if (double.IsNaN(interval))
{ {
return string.Intern("NaN"); return string.Intern("NaN");
} }
   
int sign = Math.Sign(interval); int sign = Math.Sign(interval);
interval = Math.Abs(interval); interval = Math.Abs(interval);
   
string format; string format;
   
if (sign >= 0) if (sign >= 0)
{ {
format = string.Intern("T - {0}"); format = string.Intern("T - {0}");
} }
else else
{ {
format = string.Intern("T + {0}"); format = string.Intern("T + {0}");
} }
   
return string.Format(format, VOID_Tools.FormatInterval(interval)); return string.Format(format, VOID_Tools.FormatInterval(interval));
} }
); );
   
public static readonly VOID_StrValue burnTimeHalfDoneAtNode = public static readonly VOID_StrValue burnTimeHalfDoneAtNode =
new VOID_StrValue( new VOID_StrValue(
"Full burn time to be half done at node", "Full burn time to be half done at node",
delegate() delegate()
{ {
if (Core.LastStage == null && upcomingManeuverNodes < 1) if (Core.LastStage == null && upcomingManeuverNodes < 1)
{ {
return "N/A"; return "N/A";
} }
   
ManeuverNode node = Core.Vessel.patchedConicSolver.maneuverNodes[0]; ManeuverNode node = Core.Vessel.patchedConicSolver.maneuverNodes[0];
   
if ((node.UT - Planetarium.GetUniversalTime()) < 0) if ((node.UT - Planetarium.GetUniversalTime()) < 0)
{ {
return string.Empty; return string.Empty;
} }
   
double interval = (node.UT - currentNodeHalfBurnDuration) - Planetarium.GetUniversalTime(); double interval = (node.UT - currentNodeHalfBurnDuration) - Planetarium.GetUniversalTime();
   
if (double.IsNaN(interval)) if (double.IsNaN(interval))
{ {
return string.Intern("NaN"); return string.Intern("NaN");
} }
   
int sign = Math.Sign(interval); int sign = Math.Sign(interval);
interval = Math.Abs(interval); interval = Math.Abs(interval);
   
string format; string format;
   
if (sign >= 0) if (sign >= 0)
{ {
format = string.Intern("T - {0}"); format = string.Intern("T - {0}");
} }
else else
{ {
format = string.Intern("T + {0}"); format = string.Intern("T + {0}");
} }
   
return string.Format(format, VOID_Tools.FormatInterval(interval)); return string.Format(format, VOID_Tools.FormatInterval(interval));
} }
); );
   
public static readonly VOID_DoubleValue currManeuverDeltaV = public static readonly VOID_DoubleValue currManeuverDeltaV =
new VOID_DoubleValue( new VOID_DoubleValue(
"Current Maneuver Delta-V", "Current Maneuver Delta-V",
delegate() delegate()
{ {
if (upcomingManeuverNodes > 0) if (upcomingManeuverNodes > 0)
{ {
return Core.Vessel.patchedConicSolver.maneuverNodes[0].DeltaV.magnitude; return Core.Vessel.patchedConicSolver.maneuverNodes[0].DeltaV.magnitude;
} }
else else
{ {
return double.NaN; return double.NaN;
} }
}, },
"m/s" "m/s"
); );
   
public static readonly VOID_DoubleValue currManeuverDVRemaining = public static readonly VOID_DoubleValue currManeuverDVRemaining =
new VOID_DoubleValue( new VOID_DoubleValue(
"Remaining Maneuver Delta-V", "Remaining Maneuver Delta-V",
delegate() delegate()
{ {
if (upcomingManeuverNodes > 0) 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 else
{ {
return double.NaN; return double.NaN;
} }
}, },
"m/s" "m/s"
); );
   
public static readonly VOID_DoubleValue currentNodeBurnDuration = public static readonly VOID_DoubleValue currentNodeBurnDuration =
new VOID_DoubleValue( new VOID_DoubleValue(
"Total Burn Time", "Total Burn Time",
delegate() delegate()
{ {
if (currManeuverDeltaV.Value == double.NaN) if (currManeuverDeltaV.Value == double.NaN)
{ {
return double.NaN; return double.NaN;
} }
   
return realVesselBurnTime(currManeuverDeltaV.Value); return realVesselBurnTime(currManeuverDeltaV.Value);
}, },
"s" "s"
); );
   
public static readonly VOID_DoubleValue currentNodeBurnRemaining = public static readonly VOID_DoubleValue currentNodeBurnRemaining =
new VOID_DoubleValue( new VOID_DoubleValue(
"Burn Time Remaining", "Burn Time Remaining",
delegate() delegate()
{ {
if (currManeuverDVRemaining.Value == double.NaN) if (currManeuverDVRemaining.Value == double.NaN)
{ {
return double.NaN; return double.NaN;
} }
   
return realVesselBurnTime(currManeuverDVRemaining.Value); return realVesselBurnTime(currManeuverDVRemaining.Value);
}, },
"s" "s"
); );
   
public static readonly VOID_DoubleValue currentNodeHalfBurnDuration = public static readonly VOID_DoubleValue currentNodeHalfBurnDuration =
new VOID_DoubleValue( new VOID_DoubleValue(
"Half Burn Time", "Half Burn Time",
delegate() delegate()
{ {
if (currManeuverDeltaV.Value == double.NaN) if (currManeuverDeltaV.Value == double.NaN)
{ {
return double.NaN; return double.NaN;
} }
   
return realVesselBurnTime(currManeuverDeltaV.Value / 2d); return realVesselBurnTime(currManeuverDeltaV.Value / 2d);
}, },
"s" "s"
); );
   
public static readonly VOID_DoubleValue nextManeuverDeltaV = public static readonly VOID_DoubleValue nextManeuverDeltaV =
new VOID_DoubleValue( new VOID_DoubleValue(
"Current Maneuver Delta-V", "Current Maneuver Delta-V",
delegate() delegate()
{ {
if (upcomingManeuverNodes > 1) if (upcomingManeuverNodes > 1)
{ {
return Core.Vessel.patchedConicSolver.maneuverNodes[1].DeltaV.magnitude; return Core.Vessel.patchedConicSolver.maneuverNodes[1].DeltaV.magnitude;
} }
else else
{ {
return double.NaN; return double.NaN;
} }
}, },
"m/s" "m/s"
); );
   
#endregion #endregion
   
#region Orbits #region Orbits
   
public static readonly VOID_StrValue primaryName = public static readonly VOID_StrValue primaryName =
new VOID_StrValue( new VOID_StrValue(
VOID_Localization.void_primary, VOID_Localization.void_primary,
delegate() delegate()
{ {
if (Core.Vessel == null) if (Core.Vessel == null)
{ {
return string.Empty; return string.Empty;
} }
return Core.Vessel.mainBody.name; return Core.Vessel.mainBody.name;
} }
); );
   
public static readonly VOID_DoubleValue orbitAltitude = public static readonly VOID_DoubleValue orbitAltitude =
new VOID_DoubleValue( new VOID_DoubleValue(
"Altitude (ASL)", "Altitude (ASL)",
new Func<double>(() => Core.Vessel.orbit.altitude), new Func<double>(() => Core.Vessel.orbit.altitude),
"m" "m"
); );
   
public static readonly VOID_DoubleValue orbitVelocity = public static readonly VOID_DoubleValue orbitVelocity =
new VOID_DoubleValue( new VOID_DoubleValue(
VOID_Localization.void_velocity, VOID_Localization.void_velocity,
new Func<double>(() => Core.Vessel.orbit.vel.magnitude), new Func<double>(() => Core.Vessel.orbit.vel.magnitude),
"m/s" "m/s"
); );
   
public static readonly VOID_DoubleValue orbitApoAlt = public static readonly VOID_DoubleValue orbitApoAlt =
new VOID_DoubleValue( new VOID_DoubleValue(
VOID_Localization.void_apoapsis, VOID_Localization.void_apoapsis,
new Func<double>(() => Core.Vessel.orbit.ApA), new Func<double>(() => Core.Vessel.orbit.ApA),
"m" "m"
); );
   
public static readonly VOID_DoubleValue oribtPeriAlt = public static readonly VOID_DoubleValue oribtPeriAlt =
new VOID_DoubleValue( new VOID_DoubleValue(
VOID_Localization.void_periapsis, VOID_Localization.void_periapsis,
new Func<double>(() => Core.Vessel.orbit.PeA), new Func<double>(() => Core.Vessel.orbit.PeA),
"m" "m"
); );
   
public static readonly VOID_StrValue timeToApo = public static readonly VOID_StrValue timeToApo =
new VOID_StrValue( new VOID_StrValue(
"Time to Apoapsis", "Time to Apoapsis",
new Func<string>(() => VOID_Tools.FormatInterval(Core.Vessel.orbit.timeToAp)) new Func<string>(() => VOID_Tools.FormatInterval(Core.Vessel.orbit.timeToAp))
); );
   
public static readonly VOID_StrValue timeToPeri = public static readonly VOID_StrValue timeToPeri =
new VOID_StrValue( new VOID_StrValue(
"Time to Periapsis", "Time to Periapsis",
new Func<string>(() => VOID_Tools.FormatInterval(Core.Vessel.orbit.timeToPe)) new Func<string>(() => VOID_Tools.FormatInterval(Core.Vessel.orbit.timeToPe))
); );
   
public static readonly VOID_DoubleValue orbitInclination = public static readonly VOID_DoubleValue orbitInclination =
new VOID_DoubleValue( new VOID_DoubleValue(
"Inclination", "Inclination",
new Func<double>(() => Core.Vessel.orbit.inclination), new Func<double>(() => Core.Vessel.orbit.inclination),
"°" "°"
); );
   
public static readonly VOID_DoubleValue gravityAccel = public static readonly VOID_DoubleValue gravityAccel =
new VOID_DoubleValue( new VOID_DoubleValue(
"Gravity", "Gravity",
delegate() delegate()
{ {
double orbitRadius = Core.Vessel.mainBody.Radius + double orbitRadius = Core.Vessel.mainBody.Radius +
Core.Vessel.mainBody.GetAltitude(Core.Vessel.findWorldCenterOfMass()); Core.Vessel.mainBody.GetAltitude(Core.Vessel.findWorldCenterOfMass());
return (VOIDCore.Constant_G * Core.Vessel.mainBody.Mass) / return (VOIDCore.Constant_G * Core.Vessel.mainBody.Mass) /
(orbitRadius * orbitRadius); (orbitRadius * orbitRadius);
}, },
"m/s²" "m/s²"
); );
   
public static readonly VOID_StrValue orbitPeriod = public static readonly VOID_StrValue orbitPeriod =
new VOID_StrValue( new VOID_StrValue(
"Period", "Period",
new Func<string>(() => VOID_Tools.FormatInterval(Core.Vessel.orbit.period)) new Func<string>(() => VOID_Tools.FormatInterval(Core.Vessel.orbit.period))
); );
   
public static readonly VOID_DoubleValue semiMajorAxis = public static readonly VOID_DoubleValue semiMajorAxis =
new VOID_DoubleValue( new VOID_DoubleValue(
"Semi-Major Axis", "Semi-Major Axis",
new Func<double>(() => Core.Vessel.orbit.semiMajorAxis), new Func<double>(() => Core.Vessel.orbit.semiMajorAxis),
"m" "m"
); );
   
public static readonly VOID_DoubleValue eccentricity = public static readonly VOID_DoubleValue eccentricity =
new VOID_DoubleValue( new VOID_DoubleValue(
"Eccentricity", "Eccentricity",
new Func<double>(() => Core.Vessel.orbit.eccentricity), new Func<double>(() => Core.Vessel.orbit.eccentricity),
"" ""
); );
   
public static readonly VOID_DoubleValue meanAnomaly = public static readonly VOID_DoubleValue meanAnomaly =
new VOID_DoubleValue( new VOID_DoubleValue(
"Mean Anomaly", "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 = public static readonly VOID_DoubleValue trueAnomaly =
new VOID_DoubleValue( new VOID_DoubleValue(
"True Anomaly", "True Anomaly",
new Func<double>(() => Core.Vessel.orbit.trueAnomaly), new Func<double>(() => Core.Vessel.orbit.trueAnomaly),
"°" "°"
); );
   
public static readonly VOID_DoubleValue eccAnomaly = public static readonly VOID_DoubleValue eccAnomaly =
new VOID_DoubleValue( new VOID_DoubleValue(
"Eccentric Anomaly", "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 = public static readonly VOID_DoubleValue longitudeAscNode =
new VOID_DoubleValue( new VOID_DoubleValue(
"Long. Ascending Node", "Long. Ascending Node",
new Func<double>(() => Core.Vessel.orbit.LAN), new Func<double>(() => Core.Vessel.orbit.LAN),
"°" "°"
); );
   
public static readonly VOID_DoubleValue argumentPeriapsis = public static readonly VOID_DoubleValue argumentPeriapsis =
new VOID_DoubleValue( new VOID_DoubleValue(
"Argument of Periapsis", "Argument of Periapsis",
new Func<double>(() => Core.Vessel.orbit.argumentOfPeriapsis), new Func<double>(() => Core.Vessel.orbit.argumentOfPeriapsis),
"°" "°"
); );
   
public static readonly VOID_StrValue timeToAscendingNode = public static readonly VOID_StrValue timeToAscendingNode =
new VOID_StrValue( new VOID_StrValue(
"Time to Ascending Node", "Time to Ascending Node",
delegate() delegate()
{ {
double trueAnomalyAscNode = 360d - argumentPeriapsis; double trueAnomalyAscNode = 360d - argumentPeriapsis;
double dTAscNode = Core.Vessel.orbit.GetDTforTrueAnomaly( double dTAscNode = Core.Vessel.orbit.GetDTforTrueAnomaly(
trueAnomalyAscNode * Mathf.Deg2Rad, trueAnomalyAscNode * Mathf.Deg2Rad,
Core.Vessel.orbit.period Core.Vessel.orbit.period
); );
   
dTAscNode %= Core.Vessel.orbit.period; dTAscNode %= Core.Vessel.orbit.period;
   
if (dTAscNode < 0d) if (dTAscNode < 0d)
{ {
dTAscNode += Core.Vessel.orbit.period; dTAscNode += Core.Vessel.orbit.period;
} }
   
return VOID_Tools.FormatInterval(dTAscNode); return VOID_Tools.FormatInterval(dTAscNode);
} }
); );
   
public static readonly VOID_StrValue timeToDescendingNode = public static readonly VOID_StrValue timeToDescendingNode =
new VOID_StrValue( new VOID_StrValue(
"Time to Descending Node", "Time to Descending Node",
delegate() delegate()
{ {
double trueAnomalyAscNode = 180d - argumentPeriapsis; double trueAnomalyAscNode = 180d - argumentPeriapsis;
double dTDescNode = Core.Vessel.orbit.GetDTforTrueAnomaly( double dTDescNode = Core.Vessel.orbit.GetDTforTrueAnomaly(
trueAnomalyAscNode * Mathf.Deg2Rad, trueAnomalyAscNode * Mathf.Deg2Rad,
Core.Vessel.orbit.period Core.Vessel.orbit.period
); );
   
dTDescNode %= Core.Vessel.orbit.period; dTDescNode %= Core.Vessel.orbit.period;
   
if (dTDescNode < 0d) if (dTDescNode < 0d)
{ {
dTDescNode += Core.Vessel.orbit.period; dTDescNode += Core.Vessel.orbit.period;
} }
   
return VOID_Tools.FormatInterval(dTDescNode); return VOID_Tools.FormatInterval(dTDescNode);
} }
); );
   
public static readonly VOID_DoubleValue localSiderealLongitude = public static readonly VOID_DoubleValue localSiderealLongitude =
new VOID_DoubleValue( new VOID_DoubleValue(
"Local Sidereal Longitude", "Local Sidereal Longitude",
new Func<double>(() => VOID_Tools.FixDegreeDomain( new Func<double>(() => VOID_Tools.FixDegreeDomain(
Core.Vessel.longitude + Core.Vessel.orbit.referenceBody.rotationAngle)), Core.Vessel.longitude + Core.Vessel.orbit.referenceBody.rotationAngle)),
"°" "°"
); );
   
#endregion #endregion
   
#region Science #region Science
   
public static readonly VOID_StrValue expSituation = public static readonly VOID_StrValue expSituation =
new VOID_StrValue( new VOID_StrValue(
"Situation", "Situation",
new Func<string>(() => Core.Vessel.GetExperimentSituation().HumanString()) new Func<string>(() => Core.Vessel.GetExperimentSituation().HumanString())
); );
   
public static readonly VOID_StrValue currBiome = public static readonly VOID_StrValue currBiome =
new VOID_StrValue( new VOID_StrValue(
"Biome", "Biome",
delegate() delegate()
{ {
if (Core.Vessel.landedAt == string.Empty) if (Core.Vessel.landedAt == string.Empty)
{ {
return VOID_Tools.GetBiome(Core.Vessel).name; return VOID_Tools.GetBiome(Core.Vessel).name;
} }
else else
{ {
return Core.Vessel.landedAt; return Core.Vessel.landedAt;
} }
} }
); );
   
#endregion #endregion
   
#region Surface #region Surface
   
public static readonly VOID_DoubleValue terrainElevation = public static readonly VOID_DoubleValue terrainElevation =
new VOID_DoubleValue( new VOID_DoubleValue(
"Terrain elevation", "Terrain elevation",
new Func<double>(() => Core.Vessel.terrainAltitude), new Func<double>(() => Core.Vessel.terrainAltitude),
"m" "m"
); );
   
#endregion #endregion
   
private static double burnTime(double deltaV, double initialMass, double massFlow, double thrust) private static double burnTime(double deltaV, double initialMass, double massFlow, double thrust)
{ {
Tools.PostDebugMessage(typeof(VOID_Data), "calculating burnTime from:\n" + Tools.PostDebugMessage(typeof(VOID_Data), "calculating burnTime from:\n" +
"\tdeltaV: {0}\n" + "\tdeltaV: {0}\n" +
"\tinitialMass: {1}\n" + "\tinitialMass: {1}\n" +
"\tmassFlow: {2}\n" + "\tmassFlow: {2}\n" +
"\tthrust: {3}\n", "\tthrust: {3}\n",
deltaV, deltaV,
initialMass, initialMass,
massFlow, massFlow,
thrust thrust
); );
return initialMass / massFlow * (1d - Math.Exp(-deltaV * massFlow / thrust)); return initialMass / massFlow * (1d - Math.Exp(-deltaV * massFlow / thrust));
} }
   
private static double dVfromBurnTime(double time, double initialMass, double massFlow, double thrust) private static double dVfromBurnTime(double time, double initialMass, double massFlow, double thrust)
{ {
return -thrust / massFlow * Math.Log(1d - time * massFlow / initialMass); return -thrust / massFlow * Math.Log(1d - time * massFlow / initialMass);
} }
   
private static double realVesselBurnTime(double deltaV) private static double realVesselBurnTime(double deltaV)
{ {
if (Core.Stages == null || Core.Stages.Length < 1) if (Core.Stages == null || Core.Stages.Length < 1)
{ {
return double.NaN; return double.NaN;
} }
   
double burntime = 0d; double burntime = 0d;
double dVRemaining = deltaV; double dVRemaining = deltaV;
   
int stageIdx = Core.Stages.Length - 1; int stageIdx = Core.Stages.Length - 1;
   
while (dVRemaining > double.Epsilon) while (dVRemaining > double.Epsilon)
{ {
if (stageIdx < 0) if (stageIdx < 0)
{ {
return double.PositiveInfinity; return double.PositiveInfinity;
} }
   
Stage stage = Core.Stages[stageIdx]; Stage stage = Core.Stages[stageIdx];
   
if (stage.deltaV > 0) if (stage.deltaV > 0)
{ {
double stageDVUsed = Math.Min(stage.deltaV, dVRemaining); double stageDVUsed = Math.Min(stage.deltaV, dVRemaining);
   
burntime += burnTime(stageDVUsed, stage.totalMass, stage.MassFlow(), stage.NominalThrust()); burntime += burnTime(stageDVUsed, stage.totalMass, stage.MassFlow(), stage.NominalThrust());
dVRemaining -= stageDVUsed; dVRemaining -= stageDVUsed;
} }
   
stageIdx--; stageIdx--;
} }
   
return burntime; return burntime;
} }
} }
} }
   
// VOID // VOID
// //
// VOID_DataLogger.cs // VOID_DataLogger.cs
// //
// Copyright © 2014, toadicus // Copyright © 2014, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// 3. Neither the name of the copyright holder nor the names of its contributors may be used // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior written permission. // to endorse or promote products derived from this software without specific prior written permission.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // 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. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
using KSP; using KSP;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using ToadicusTools; using ToadicusTools;
using UnityEngine; using UnityEngine;
   
namespace VOID namespace VOID
{ {
public class VOID_DataLogger : VOID_WindowModule, IVOID_BehaviorModule public class VOID_DataLogger : VOID_WindowModule, IVOID_BehaviorModule
{ {
/* /*
* Fields * Fields
* */ * */
#region Fields #region Fields
   
protected bool _loggingActive; protected bool _loggingActive;
protected bool firstWrite; protected bool firstWrite;
   
[AVOID_SaveValue("waitForLaunch")] [AVOID_SaveValue("waitForLaunch")]
protected VOID_SaveValue<bool> waitForLaunch; protected VOID_SaveValue<bool> waitForLaunch;
   
[AVOID_SaveValue("logInterval")] [AVOID_SaveValue("logInterval")]
protected VOID_SaveValue<float> logInterval; protected VOID_SaveValue<float> logInterval;
protected string logIntervalStr; protected string logIntervalStr;
   
protected float csvCollectTimer; protected float csvCollectTimer;
   
protected List<byte> csvBytes; protected List<byte> csvBytes;
   
protected string _fileName; protected string _fileName;
protected FileStream _outputFile; protected FileStream _outputFile;
   
protected uint outstandingWrites; protected uint outstandingWrites;
   
protected System.Text.UTF8Encoding _utf8Encoding; protected System.Text.UTF8Encoding _utf8Encoding;
   
#endregion #endregion
   
/* /*
* Properties * Properties
* */ * */
   
#region Properties #region Properties
   
// TODO: Add configurable or incremental file names. // TODO: Add configurable or incremental file names.
protected bool loggingActive protected bool loggingActive
{ {
get get
{ {
return this._loggingActive; return this._loggingActive;
} }
set set
{ {
if (value != this._loggingActive) if (value != this._loggingActive)
{ {
if (value) if (value)
{ {
this.csvCollectTimer = 0f; this.csvCollectTimer = 0f;
} }
else else
{ {
this.CloseFileIfOpen(); this.CloseFileIfOpen();
} }
   
this._loggingActive = value; this._loggingActive = value;
} }
} }
} }
   
protected string fileName protected string fileName
{ {
get get
{ {
if (this._fileName == null || this._fileName == string.Empty) if (this._fileName == null || this._fileName == string.Empty)
{ {
this._fileName = KSP.IO.IOUtils.GetFilePathFor( this._fileName = KSP.IO.IOUtils.GetFilePathFor(
typeof(VOIDCore), typeof(VOIDCore),
string.Format( string.Format(
"{0}_{1}", "{0}_{1}",
this.Vessel.vesselName, this.Vessel.vesselName,
"data.csv" "data.csv"
), ),
null null
); );
} }
   
return this._fileName; return this._fileName;
} }
} }
   
protected FileStream outputFile protected FileStream outputFile
{ {
get get
{ {
if (this._outputFile == null) if (this._outputFile == null)
{ {
Tools.DebugLogger logger = Tools.DebugLogger.New(this); Tools.DebugLogger logger = Tools.DebugLogger.New(this);
logger.AppendFormat("Initializing output file '{0}' with mode ", this.fileName); logger.AppendFormat("Initializing output file '{0}' with mode ", this.fileName);
   
if (File.Exists(this.fileName)) if (File.Exists(this.fileName))
{ {
logger.Append("append"); logger.Append("append");
this._outputFile = new FileStream( this._outputFile = new FileStream(
this.fileName, this.fileName,
FileMode.Append, FileMode.Append,
FileAccess.Write, FileAccess.Write,
FileShare.Read, FileShare.Read,
512, 512,
true true
); );
} }
else else
{ {
logger.Append("create"); logger.Append("create");
this._outputFile = new FileStream( this._outputFile = new FileStream(
this.fileName, this.fileName,
FileMode.Create, FileMode.Create,
FileAccess.Write, FileAccess.Write,
FileShare.Read, FileShare.Read,
512, 512,
true true
); );
   
byte[] byteOrderMark = utf8Encoding.GetPreamble(); byte[] byteOrderMark = utf8Encoding.GetPreamble();
   
logger.Append(" and writing preamble"); logger.Append(" and writing preamble");
this._outputFile.Write(byteOrderMark, 0, byteOrderMark.Length); this._outputFile.Write(byteOrderMark, 0, byteOrderMark.Length);
} }
   
logger.Append('.'); logger.Append('.');
   
logger.AppendFormat(" File is {0}opened asynchronously.", this._outputFile.IsAsync ? "" : "not "); logger.AppendFormat(" File is {0}opened asynchronously.", this._outputFile.IsAsync ? "" : "not ");
   
logger.Print(); logger.Print();
} }
   
return this._outputFile; return this._outputFile;
} }
} }
   
public UTF8Encoding utf8Encoding public UTF8Encoding utf8Encoding
{ {
get get
{ {
if (this._utf8Encoding == null) if (this._utf8Encoding == null)
{ {
this._utf8Encoding = new UTF8Encoding(true); this._utf8Encoding = new UTF8Encoding(true);
} }
   
return this._utf8Encoding; return this._utf8Encoding;
} }
} }
   
#endregion #endregion
   
/* /*
* Methods * Methods
* */ * */
#region Monobehaviour Lifecycle #region Monobehaviour Lifecycle
public void Update() public void Update()
{ {
if (this.csvBytes != null && this.csvBytes.Count > 0) if (this.csvBytes != null && this.csvBytes.Count > 0)
{ {
// csvList is not empty, write it // csvList is not empty, write it
this.AsyncWriteData(); this.AsyncWriteData();
} }
   
// CSV Logging // CSV Logging
// from ISA MapSat // from ISA MapSat
if (loggingActive && (!waitForLaunch || this.Vessel.situation != Vessel.Situations.PRELAUNCH)) if (loggingActive && (!waitForLaunch || this.Vessel.situation != Vessel.Situations.PRELAUNCH))
{ {
//data logging is on //data logging is on
//increment timers //increment timers
this.csvCollectTimer += Time.deltaTime; this.csvCollectTimer += Time.deltaTime;
   
if (this.csvCollectTimer >= this.logInterval) if (this.csvCollectTimer >= this.logInterval)
{ {
//data logging is on, vessel is not prelaunch, and interval has passed //data logging is on, vessel is not prelaunch, and interval has passed
//write a line to the list //write a line to the list
this.CollectLogData(); this.CollectLogData();
} }
} }
} }
   
public void FixedUpdate() {} public void FixedUpdate() {}
   
public void OnDestroy() public void OnDestroy()
{ {
Tools.DebugLogger logger = Tools.DebugLogger.New(this); Tools.DebugLogger logger = Tools.DebugLogger.New(this);
   
logger.Append("Destroying..."); logger.Append("Destroying...");
   
this.CloseFileIfOpen(); this.CloseFileIfOpen();
   
logger.Append(" Done."); logger.Append(" Done.");
logger.Print(false); logger.Print(false);
} }
   
#endregion #endregion
   
#region VOID_Module Overrides #region VOID_Module Overrides
   
public override void LoadConfig() public override void LoadConfig()
{ {
base.LoadConfig(); base.LoadConfig();
   
this.logIntervalStr = this.logInterval.value.ToString("#.0##"); this.logIntervalStr = this.logInterval.value.ToString("#.0##");
} }
   
public override void ModuleWindow(int id) public override void ModuleWindow(int id)
{ {
GUILayout.BeginVertical(); GUILayout.BeginVertical();
   
GUILayout.Label( GUILayout.Label(
string.Format("System time: {0}", DateTime.Now.ToString("HH:mm:ss")), string.Format("System time: {0}", DateTime.Now.ToString("HH:mm:ss")),
GUILayout.ExpandWidth(true) GUILayout.ExpandWidth(true)
); );
GUILayout.Label( GUILayout.Label(
string.Format("Kerbin time: {0}", VOID_Tools.FormatDate(Planetarium.GetUniversalTime())), string.Format("Kerbin time: {0}", VOID_Tools.FormatDate(Planetarium.GetUniversalTime())),
GUILayout.ExpandWidth(true) GUILayout.ExpandWidth(true)
); );
   
GUIStyle activeLabelStyle = VOID_Styles.labelRed; GUIStyle activeLabelStyle = VOID_Styles.labelRed;
string activeLabelText = "Inactive"; string activeLabelText = "Inactive";
if (loggingActive) if (loggingActive)
{ {
activeLabelText = "Active"; activeLabelText = "Active";
activeLabelStyle = VOID_Styles.labelGreen; activeLabelStyle = VOID_Styles.labelGreen;
} }
   
this.loggingActive = GUITools.Toggle( this.loggingActive = GUITools.Toggle(
loggingActive, loggingActive,
string.Format("Data logging: {0}", activeLabelText), string.Format("Data logging: {0}", activeLabelText),
null, null,
activeLabelStyle activeLabelStyle
); );
   
this.waitForLaunch.value = GUITools.Toggle( this.waitForLaunch.value = GUITools.Toggle(
this.waitForLaunch, this.waitForLaunch,
"Wait for launch" "Wait for launch"
); );
   
GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true)); GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
   
GUILayout.Label("Interval: ", GUILayout.ExpandWidth(false)); GUILayout.Label("Interval: ", GUILayout.ExpandWidth(false));
   
logIntervalStr = GUILayout.TextField(logIntervalStr, GUILayout.ExpandWidth(true)); logIntervalStr = GUILayout.TextField(logIntervalStr, GUILayout.ExpandWidth(true));
GUILayout.Label("s", GUILayout.ExpandWidth(false)); GUILayout.Label("s", GUILayout.ExpandWidth(false));
   
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
float newLogInterval; float newLogInterval;
if (float.TryParse(logIntervalStr, out newLogInterval)) if (float.TryParse(logIntervalStr, out newLogInterval))
{ {
logInterval.value = newLogInterval; logInterval.value = newLogInterval;
this.logIntervalStr = this.logInterval.value.ToString("#.0##"); this.logIntervalStr = this.logInterval.value.ToString("#.0##");
} }
   
GUILayout.EndVertical(); GUILayout.EndVertical();
   
base.ModuleWindow(id); base.ModuleWindow(id);
} }
   
#endregion #endregion
   
#region Data Collection #region Data Collection
   
private void CollectLogData() private void CollectLogData()
{ {
if (this.csvBytes == null) if (this.csvBytes == null)
{ {
this.csvBytes = new List<byte>(); this.csvBytes = new List<byte>();
} }
   
//called if logging is on and interval has passed //called if logging is on and interval has passed
//writes one line to the csvList //writes one line to the csvList
   
StringBuilder line = new StringBuilder(); StringBuilder line = new StringBuilder();
   
if (firstWrite) if (firstWrite)
{ {
firstWrite = false; firstWrite = false;
line.Append( line.Append(
"\"Kerbin Universal Time (s)\"," + "\"Kerbin Universal Time (s)\"," +
"\"Mission Elapsed Time (s)\t\"," + "\"Mission Elapsed Time (s)\t\"," +
"\"Altitude ASL (m)\"," + "\"Altitude ASL (m)\"," +
"\"Altitude above terrain (m)\"," + "\"Altitude above terrain (m)\"," +
"\"Surface Latitude (°)\"," + "\"Surface Latitude (°)\"," +
"\"Surface Longitude (°)\"," + "\"Surface Longitude (°)\"," +
  "\"Apoapsis Altitude (m)\"" +
  "\"Periapsis Altitude (m)\"" +
"\"Orbital Velocity (m/s)\"," + "\"Orbital Velocity (m/s)\"," +
"\"Surface Velocity (m/s)\"," + "\"Surface Velocity (m/s)\"," +
"\"Vertical Speed (m/s)\"," + "\"Vertical Speed (m/s)\"," +
"\"Horizontal Speed (m/s)\"," + "\"Horizontal Speed (m/s)\"," +
"\"Gee Force (gees)\"," + "\"Gee Force (gees)\"," +
"\"Temperature (°C)\"," + "\"Temperature (°C)\"," +
"\"Gravity (m/s²)\"," + "\"Gravity (m/s²)\"," +
"\"Atmosphere Density (g/m³)\"," + "\"Atmosphere Density (g/m³)\"," +
"\"Downrange Distance (m)\"," + "\"Downrange Distance (m)\"," +
"\n" "\n"
); );
} }
   
// Universal time // Universal time
line.Append(Planetarium.GetUniversalTime().ToString("F2")); line.Append(Planetarium.GetUniversalTime().ToString("F2"));
line.Append(','); line.Append(',');
   
//Mission time //Mission time
line.Append(Vessel.missionTime.ToString("F3")); line.Append(Vessel.missionTime.ToString("F3"));
line.Append(','); line.Append(',');
   
//Altitude ASL //Altitude ASL
line.Append(VOID_Data.orbitAltitude.Value.ToString("F3")); line.Append(VOID_Data.orbitAltitude.Value.ToString("F3"));
line.Append(','); line.Append(',');
   
//Altitude (true) //Altitude (true)
line.Append(VOID_Data.trueAltitude.Value.ToString("F3")); line.Append(VOID_Data.trueAltitude.Value.ToString("F3"));
line.Append(','); line.Append(',');
   
// Surface Latitude // Surface Latitude
line.Append('"'); line.Append('"');
line.Append(VOID_Data.surfLatitude.Value); line.Append(VOID_Data.surfLatitude.Value);
line.Append('"'); line.Append('"');
line.Append(','); line.Append(',');
   
// Surface Longitude // Surface Longitude
line.Append('"'); line.Append('"');
line.Append(VOID_Data.surfLongitude.Value); line.Append(VOID_Data.surfLongitude.Value);
line.Append('"'); line.Append('"');
line.Append(','); line.Append(',');
   
//Orbital velocity //Orbital velocity
line.Append(VOID_Data.orbitVelocity.Value.ToString("F3")); line.Append(VOID_Data.orbitVelocity.Value.ToString("F3"));
line.Append(','); line.Append(',');
   
  // Apoapsis Altitude
  line.Append(VOID_Data.orbitApoAlt.Value.ToString("G3"));
  line.Append(',');
   
  // Periapsis Altitude
  line.Append(VOID_Data.oribtPeriAlt.Value.ToString("G3"));
  line.Append(',');
   
//surface velocity //surface velocity
line.Append(VOID_Data.surfVelocity.Value.ToString("F3")); line.Append(VOID_Data.surfVelocity.Value.ToString("F3"));
line.Append(','); line.Append(',');
   
//vertical speed //vertical speed
line.Append(VOID_Data.vertVelocity.Value.ToString("F3")); line.Append(VOID_Data.vertVelocity.Value.ToString("F3"));
line.Append(','); line.Append(',');
   
//horizontal speed //horizontal speed
line.Append(VOID_Data.horzVelocity.Value.ToString("F3")); line.Append(VOID_Data.horzVelocity.Value.ToString("F3"));
line.Append(','); line.Append(',');
   
//gee force //gee force
line.Append(VOID_Data.geeForce.Value.ToString("F3")); line.Append(VOID_Data.geeForce.Value.ToString("F3"));
line.Append(','); line.Append(',');
   
//temperature //temperature
line.Append(VOID_Data.temperature.Value.ToString("F2")); line.Append(VOID_Data.temperature.Value.ToString("F2"));
line.Append(','); line.Append(',');
   
//gravity //gravity
line.Append(VOID_Data.gravityAccel.Value.ToString("F3")); line.Append(VOID_Data.gravityAccel.Value.ToString("F3"));
line.Append(','); line.Append(',');
   
//atm density //atm density
line.Append(VOID_Data.atmDensity.Value.ToString("G3")); line.Append(VOID_Data.atmDensity.Value.ToString("G3"));
line.Append(','); line.Append(',');
   
// Downrange Distance // Downrange Distance
line.Append((VOID_Data.downrangeDistance.Value.ToString("G3"))); line.Append((VOID_Data.downrangeDistance.Value.ToString("G3")));
   
line.Append('\n'); line.Append('\n');
   
csvBytes.AddRange(this.utf8Encoding.GetBytes(line.ToString())); csvBytes.AddRange(this.utf8Encoding.GetBytes(line.ToString()));
   
this.csvCollectTimer = 0f; this.csvCollectTimer = 0f;
} }
   
#endregion #endregion
   
#region File IO Methods #region File IO Methods
   
protected void AsyncWriteCallback(IAsyncResult result) protected void AsyncWriteCallback(IAsyncResult result)
{ {
Tools.PostDebugMessage(this, "Got async callback, IsCompleted = {0}", result.IsCompleted); Tools.PostDebugMessage(this, "Got async callback, IsCompleted = {0}", result.IsCompleted);
   
this.outputFile.EndWrite(result); this.outputFile.EndWrite(result);
this.outstandingWrites--; this.outstandingWrites--;
} }
   
private void AsyncWriteData() private void AsyncWriteData()
{ {
WriteState state = new WriteState(); WriteState state = new WriteState();
   
state.bytes = this.csvBytes.ToArray(); state.bytes = this.csvBytes.ToArray();
state.stream = this.outputFile; state.stream = this.outputFile;
   
this.outstandingWrites++; this.outstandingWrites++;
var writeCallback = new AsyncCallback(this.AsyncWriteCallback); var writeCallback = new AsyncCallback(this.AsyncWriteCallback);
   
this.outputFile.BeginWrite(state.bytes, 0, state.bytes.Length, writeCallback, state); this.outputFile.BeginWrite(state.bytes, 0, state.bytes.Length, writeCallback, state);
   
this.csvBytes.Clear(); this.csvBytes.Clear();
} }
   
private void CloseFileIfOpen() private void CloseFileIfOpen()
{ {
Tools.DebugLogger logger = Tools.DebugLogger.New(this); Tools.DebugLogger logger = Tools.DebugLogger.New(this);
   
logger.AppendFormat("Cleaning up file {0}...", this.fileName); logger.AppendFormat("Cleaning up file {0}...", this.fileName);
   
if (this.csvBytes != null && this.csvBytes.Count > 0) if (this.csvBytes != null && this.csvBytes.Count > 0)
{ {
logger.Append(" Writing remaining data..."); logger.Append(" Writing remaining data...");
this.AsyncWriteData(); this.AsyncWriteData();
} }
   
logger.Append(" Waiting for writes to finish."); logger.Append(" Waiting for writes to finish.");
while (this.outstandingWrites > 0) while (this.outstandingWrites > 0)
{ {
logger.Append('.'); logger.Append('.');
System.Threading.Thread.Sleep(10); System.Threading.Thread.Sleep(10);
} }
   
if (this._outputFile != null) if (this._outputFile != null)
{ {
this._outputFile.Close(); this._outputFile.Close();
this._outputFile = null; this._outputFile = null;
logger.Append(" File closed."); logger.Append(" File closed.");
} }
   
logger.Print(false); logger.Print(false);
} }
   
#endregion #endregion
   
#region Constructors & Destructors #region Constructors & Destructors
   
public VOID_DataLogger() public VOID_DataLogger()
{ {
this.Name = "CSV Data Logger"; this.Name = "CSV Data Logger";
   
this.loggingActive = false; this.loggingActive = false;
this.firstWrite = true; this.firstWrite = true;
   
this.waitForLaunch = (VOID_SaveValue<bool>)true; this.waitForLaunch = (VOID_SaveValue<bool>)true;
   
this.logInterval = (VOID_SaveValue<float>)0.5f; this.logInterval = (VOID_SaveValue<float>)0.5f;
this.csvCollectTimer = (VOID_SaveValue<float>)0f; this.csvCollectTimer = (VOID_SaveValue<float>)0f;
   
this.outstandingWrites = 0; this.outstandingWrites = 0;
   
this.WindowPos.x = Screen.width - 520f; this.WindowPos.x = Screen.width - 520f;
this.WindowPos.y = 85f; this.WindowPos.y = 85f;
   
this.core.onApplicationQuit += delegate(object sender) this.core.onApplicationQuit += delegate(object sender)
{ {
this.CloseFileIfOpen(); this.CloseFileIfOpen();
}; };
} }
   
~VOID_DataLogger() ~VOID_DataLogger()
{ {
this.OnDestroy(); this.OnDestroy();
} }
   
#endregion #endregion
   
#region Subclasses #region Subclasses
   
private class WriteState private class WriteState
{ {
public byte[] bytes; public byte[] bytes;
public FileStream stream; public FileStream stream;
} }
   
#endregion #endregion
} }
} }
   
   
// VOID // VOID
// //
// VOID_VesselInfo.cs // VOID_VesselInfo.cs
// //
// Copyright © 2014, toadicus // Copyright © 2014, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// 3. Neither the name of the copyright holder nor the names of its contributors may be used // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior written permission. // to endorse or promote products derived from this software without specific prior written permission.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // 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. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
using KerbalEngineer.VesselSimulator; using KerbalEngineer.VesselSimulator;
using KerbalEngineer.Extensions; using KerbalEngineer.Extensions;
using KSP; using KSP;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ToadicusTools; using ToadicusTools;
using UnityEngine; using UnityEngine;
   
namespace VOID namespace VOID
{ {
public class VOID_VesselInfo : VOID_WindowModule public class VOID_VesselInfo : VOID_WindowModule
{ {
public VOID_VesselInfo() : base() public VOID_VesselInfo() : base()
{ {
this.Name = "Vessel Information"; this.Name = "Vessel Information";
   
this.WindowPos.x = Screen.width - 260; this.WindowPos.x = Screen.width - 260;
this.WindowPos.y = 450; this.WindowPos.y = 450;
} }
   
public override void ModuleWindow(int id) public override void ModuleWindow(int id)
{ {
if ((TimeWarp.WarpMode == TimeWarp.Modes.LOW) || (TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate)) if ((TimeWarp.WarpMode == TimeWarp.Modes.LOW) || (TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate))
{ {
SimManager.RequestSimulation(); SimManager.RequestSimulation();
} }
   
GUILayout.BeginVertical(); GUILayout.BeginVertical();
   
GUILayout.Label( GUILayout.Label(
Vessel.vesselName, Vessel.vesselName,
VOID_Styles.labelCenterBold, VOID_Styles.labelCenterBold,
GUILayout.ExpandWidth(true)); GUILayout.ExpandWidth(true));
   
VOID_Data.geeForce.DoGUIHorizontal ("F2"); VOID_Data.geeForce.DoGUIHorizontal ("F2");
   
VOID_Data.partCount.DoGUIHorizontal (); VOID_Data.partCount.DoGUIHorizontal ();
   
VOID_Data.totalMass.DoGUIHorizontal ("F3"); VOID_Data.totalMass.DoGUIHorizontal ("F3");
   
VOID_Data.stageResourceMass.DoGUIHorizontal("F2"); VOID_Data.stageResourceMass.DoGUIHorizontal("F3");
   
VOID_Data.resourceMass.DoGUIHorizontal("F2"); VOID_Data.resourceMass.DoGUIHorizontal("F3");
   
VOID_Data.stageDeltaV.DoGUIHorizontal (3, false); VOID_Data.stageDeltaV.DoGUIHorizontal (3, false);
   
VOID_Data.totalDeltaV.DoGUIHorizontal (3, false); VOID_Data.totalDeltaV.DoGUIHorizontal (3, false);
   
VOID_Data.mainThrottle.DoGUIHorizontal ("F0"); VOID_Data.mainThrottle.DoGUIHorizontal ("F0");
   
VOID_Data.currmaxThrust.DoGUIHorizontal (); VOID_Data.currmaxThrust.DoGUIHorizontal ();
   
VOID_Data.currmaxThrustWeight.DoGUIHorizontal (); VOID_Data.currmaxThrustWeight.DoGUIHorizontal ();
   
VOID_Data.surfaceThrustWeight.DoGUIHorizontal ("F2"); VOID_Data.surfaceThrustWeight.DoGUIHorizontal ("F2");
   
VOID_Data.intakeAirStatus.DoGUIHorizontal(); VOID_Data.intakeAirStatus.DoGUIHorizontal();
   
GUILayout.EndVertical(); GUILayout.EndVertical();
   
base.ModuleWindow(id); base.ModuleWindow(id);
} }
} }
} }