Skip to content

Commit

Permalink
Assorted fixes and requested changes
Browse files Browse the repository at this point in the history
- Handle low-inclination (<30°) retrograde orbits
- Reset display when a transfer expires
- Calculate ejection burns in foreground so sorting works on first load
- United States customary units option
- Fix issue with target not appearing in list
- Another patch to try to suppress phantom maneuver nodes
  • Loading branch information
HebaruSan committed Feb 8, 2017
1 parent aa6f1f0 commit e98b16f
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 95 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ Click the wrench to open and close the settings panel:
| Automatically focus destination | When this is enabled, clicking the maneuver icon will change the map view focus. If the default maneuvers create an encounter with the desired body, then that body will be focused so you can fine tune your arrival; otherwise the destination's parent body will be focused so you can establish the encounter. |
| Automatically edit ejection node | When this is enabled, clicking the maneuver icon will leave the first node open for editing. |
| Automatically edit plane change node | If you enable this, then the second node will be opened for editing instead of the first. |
| Units: Metric | Shows delta V in m/s (meters per second) |
| Units: United States Customary | Shows delta V in mph (miles per hour) |

## Known limitations

Expand All @@ -72,7 +74,6 @@ Click the wrench to open and close the settings panel:

- Useful numbers for launches/landed
- Return-to-LKO burns from Mun and Minmus
- Allow low-inclination retrograde orbits
- Represent starting point with ITargetable
- Split CalculateEjectionBurn into transfer versus ejection functions

Expand Down
25 changes: 15 additions & 10 deletions src/AstrogationModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ public AstrogationModel(CelestialBody b = null, Vessel v = null)
public bool badInclination {
get {
// Orbit.inclination is in degrees
return vessel != null
&& Math.Abs(vessel.orbit.inclination * Mathf.Deg2Rad) > maxInclination;
// A bad inclination is one that is closer to Tau/4 than the limit is
return vessel?.orbit != null
&& Math.Abs(0.25 * Tau - Math.Abs(vessel.orbit.inclination * Mathf.Deg2Rad)) < 0.25 * Tau - maxInclination;
}
}

Expand All @@ -66,7 +67,7 @@ public bool badInclination {
public bool retrogradeOrbit {
get {
return vessel != null
&& Math.Abs(vessel.orbit.inclination * Mathf.Deg2Rad) > 0.5 * Math.PI;
&& Math.Abs(vessel.orbit.inclination * Mathf.Deg2Rad) > 0.25 * Tau;
}
}

Expand Down Expand Up @@ -148,6 +149,7 @@ private void CreateTransfers(CelestialBody body, Vessel vessel)
int discoveryOrder = 0;

CelestialBody origin = StartBody(body, vessel);
CelestialBody targetBody = FlightGlobals.fetch.VesselTarget as CelestialBody;

for (CelestialBody b = origin, toSkip = null;
b != null;
Expand All @@ -156,31 +158,34 @@ private void CreateTransfers(CelestialBody body, Vessel vessel)
// Skip the first body unless we can actually transfer to its children
// (i.e., we have a vessel)
if (vessel != null || toSkip != null) {
DbgFmt("Plotting transfers around {0}", b.theName);
DbgFmt("Checking transfers around {0}", b.theName);

int numBodies = b.orbitingBodies.Count;
for (int i = 0; i < numBodies; ++i) {
CelestialBody satellite = b.orbitingBodies[i];
if (satellite != toSkip) {
DbgFmt("Plotting transfer to {0}", satellite.theName);
DbgFmt("Allocating transfer to {0}", satellite.theName);
transfers.Add(new TransferModel(origin, satellite, vessel, ++discoveryOrder));
DbgFmt("Finalized transfer to {0}", satellite.theName);

if (satellite == FlightGlobals.fetch.VesselTarget as CelestialBody) {
foundTarget = true;
}
if (toSkip == FlightGlobals.fetch.VesselTarget as CelestialBody) {
if (satellite == targetBody) {
DbgFmt("Found target as satellite");
foundTarget = true;
}
}
}
DbgFmt("Exhausted transfers around {0}", b.theName);
}

if (toSkip == targetBody && targetBody != null) {
DbgFmt("Found target as toSkip");
foundTarget = true;
}
}

if (!foundTarget
&& FlightGlobals.ActiveVessel != null
&& FlightGlobals.fetch.VesselTarget != null) {
DbgFmt("Allocating transfer to {0}", FlightGlobals.fetch.VesselTarget.GetName());
transfers.Insert(0, new TransferModel(origin, FlightGlobals.fetch.VesselTarget, vessel, -1));
}

Expand Down
11 changes: 2 additions & 9 deletions src/AstrogationView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private void createRows()
Settings.Instance.DescendingSort
);
for (int i = 0; i < transfers.Count; ++i) {
AddChild(new TransferView(transfers[i]));
AddChild(new TransferView(transfers[i], resetCallback));
}
}

Expand All @@ -191,8 +191,7 @@ private bool ErrorCondition {
|| model.transfers.Count == 0
|| model.badInclination
|| model.hyperbolicOrbit
|| model.notOrbiting
|| model.retrogradeOrbit;
|| model.notOrbiting;
}
}

Expand All @@ -209,12 +208,6 @@ private string subTitle {
"{0} is landed. Launch to orbit to see transfer info.",
TheName(model.vessel)
);
} else if (model.retrogradeOrbit) {
return string.Format(
"Orbit is retrograde, must be prograde with inclination below {1:0.}°",
Math.Abs(model.vessel.orbit.inclination),
AstrogationModel.maxInclination * Mathf.Rad2Deg
);
} else if (model.badInclination) {
return string.Format(
"Inclination is {0:0.0}°, accuracy too low past {1:0.}°",
Expand Down
51 changes: 31 additions & 20 deletions src/Astrogator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ private void StartLoadingModel(CelestialBody b = null, Vessel v = null, bool fro
model.Reset(b, v);
}

// Do the easy calculations in the foreground so the view can sort properly right away
CalculateEjectionBurns();

DbgFmt("Delegating load to background");

BackgroundWorker bgworker = new BackgroundWorker();
Expand All @@ -232,32 +235,39 @@ private void bw_LoadModel(object sender, DoWorkEventArgs e)
{
lock (bgLoadMutex) {
DbgFmt("Beginning background model load");
CalculatePlaneChangeBurns();
DbgFmt("Finished background model load");
}
}

private void CalculateEjectionBurns()
{
// Blast through the ejection burns so the popup has numbers ASAP
for (int i = 0; i < model.transfers.Count; ++i) {
try {
model.transfers[i].CalculateEjectionBurn();
} catch (Exception ex) {
DbgExc("Problem with background load of ejection burn", ex);
}
}
}

// Blast through the ejection burns so the popup has numbers ASAP
private void CalculatePlaneChangeBurns()
{
if (flightReady
&& Settings.Instance.GeneratePlaneChangeBurns
&& Settings.Instance.AddPlaneChangeDeltaV) {
for (int i = 0; i < model.transfers.Count; ++i) {
try {
model.transfers[i].CalculateEjectionBurn();
Thread.Sleep(200);
model.transfers[i].CalculatePlaneChangeBurn();
} catch (Exception ex) {
DbgExc("Problem with background load of ejection burn", ex);
}
}
// Now get the plane change burns if we need them for the on-screen numbers.
if (flightReady
&& Settings.Instance.GeneratePlaneChangeBurns
&& Settings.Instance.AddPlaneChangeDeltaV) {
for (int i = 0; i < model.transfers.Count; ++i) {
try {
Thread.Sleep(200);
model.transfers[i].CalculatePlaneChangeBurn();
} catch (Exception ex) {
DbgExc("Problem with background load of plane change burn", ex);

// If a route calculation crashes, it can leave behind a temporary node.
ClearManeuverNodes();
}
DbgExc("Problem with background load of plane change burn", ex);

// If a route calculation crashes, it can leave behind a temporary node.
ClearManeuverNodes();
}
}
DbgFmt("Finished background model load");
}
}

Expand Down Expand Up @@ -369,6 +379,7 @@ private void OnTargetChanged()
// Refresh the model so it can reflect the latest target data
if (model != null) {
if (!model.HasDestination(FlightGlobals.fetch.VesselTarget)) {
DbgFmt("Reloading model and view on target change");
StartLoadingModel(model.body, model.vessel);
ResetView();
}
Expand Down
80 changes: 61 additions & 19 deletions src/PhysicsTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,23 @@ public static double DeltaVToOrbit(CelestialBody body, Vessel vessel)
/// </returns>
public static double AbsolutePhaseAngle(Orbit o, double t)
{
// Longitude of Ascending Node: Angle between an absolute direction and the ascending node.
// Argument of Periapsis: Angle between ascending node and periapsis.
// True Anomaly: Angle between periapsis and current location of the body.
// Sum: Angle describing absolute location of the body.
// Only the True Anomaly is in radians by default.

// https://upload.wikimedia.org/wikipedia/commons/e/eb/Orbit1.svg
// The ArgOfPer and TruAno move you the normal amount at Inc=0,
// zero at Inc=PI/2, and backwards at Inc=PI.
// That's a cosine as far as I can tell.
double cosInc = Math.Cos(o.inclination * Mathf.Deg2Rad);
return clamp(
Mathf.Deg2Rad * (
o.LAN
+ o.argumentOfPeriapsis
+ o.argumentOfPeriapsis * cosInc
)
+ o.TrueAnomalyAtUT(t)
+ o.TrueAnomalyAtUT(t) * cosInc
);
}

Expand Down Expand Up @@ -136,7 +147,7 @@ public static double EjectionAngle(CelestialBody parent, double periapsis, doubl

// Angle between parent's prograde and the vessel's prograde at burn
// Should be between PI/2 and PI
return 1.5*Math.PI - theta;
return 0.75 * Tau - theta;
}

/// <returns>
Expand Down Expand Up @@ -202,16 +213,31 @@ public static double BurnToEscape(CelestialBody parent, Orbit fromOrbit, double
/// </returns>
public static double TimeAtAngleFromMidnight(Orbit parentOrbit, Orbit satOrbit, double minTime, double angle)
{
double satTrueAnomaly = clamp(
Mathf.Deg2Rad * (
parentOrbit.LAN
+ parentOrbit.argumentOfPeriapsis
- satOrbit.LAN
- satOrbit.argumentOfPeriapsis
)
+ parentOrbit.TrueAnomalyAtUT(minTime)
+ angle
);
double satTrueAnomaly;
if (satOrbit.GetRelativeInclination(parentOrbit) < 90f) {
satTrueAnomaly = clamp(
Mathf.Deg2Rad * (
parentOrbit.LAN
+ parentOrbit.argumentOfPeriapsis
- satOrbit.LAN
- satOrbit.argumentOfPeriapsis
)
+ parentOrbit.TrueAnomalyAtUT(minTime)
+ angle
);
} else {
satTrueAnomaly = clamp(
Mathf.Deg2Rad * (
- parentOrbit.LAN
- parentOrbit.argumentOfPeriapsis
+ satOrbit.LAN
- satOrbit.argumentOfPeriapsis
)
- parentOrbit.TrueAnomalyAtUT(minTime)
+ angle
+ Math.PI
);
}
double nextTime = satOrbit.GetUTforTrueAnomaly(satTrueAnomaly, Planetarium.GetUniversalTime());
int numOrbits = (int)Math.Ceiling((minTime - nextTime) / satOrbit.period);
return nextTime + numOrbits * satOrbit.period;
Expand Down Expand Up @@ -347,8 +373,16 @@ public static double TimeOfPlaneChange(Orbit currentOrbit, Orbit targetOrbit, do
/// </returns>
public static double PlaneChangeDeltaV(Orbit currentOrbit, Orbit targetOrbit, double nodeTime, bool ascendingNode)
{
return (ascendingNode ? -1.0 : 1.0)
* DeltaVToMatchPlanes(currentOrbit, targetOrbit, nodeTime).magnitude;
// DeltaVToMatchPlanes is precise, but it tries to flip orbits if they're going
// in opposite directions.
// In fact, we don't care if one is prograde and the other retrograde.
if (currentOrbit.GetRelativeInclination(targetOrbit) < 90f) {
return (ascendingNode ? -1.0 : 1.0)
* DeltaVToMatchPlanes(currentOrbit, targetOrbit, nodeTime).magnitude;
} else {
return (ascendingNode ? 1.0 : -1.0)
* DeltaVToMatchPlanes(currentOrbit, targetOrbit, nodeTime).magnitude;
}
}

/// <summary>
Expand All @@ -363,10 +397,18 @@ public static double PlaneChangeDeltaV(Orbit currentOrbit, Orbit targetOrbit, do
/// </returns>
public static Vector3d DeltaVToMatchPlanes(Orbit o, Orbit target, double burnUT)
{
Vector3d desiredHorizontal = Vector3d.Cross(target.SwappedOrbitNormal(), o.Up(burnUT));
Vector3d actualHorizontalVelocity = Vector3d.Exclude(o.Up(burnUT), o.SwappedOrbitalVelocityAtUT(burnUT));
Vector3d desiredHorizontalVelocity = actualHorizontalVelocity.magnitude * desiredHorizontal;
return desiredHorizontalVelocity - actualHorizontalVelocity;
if (o.GetRelativeInclination(target) < 90f) {
Vector3d desiredHorizontal = Vector3d.Cross(target.SwappedOrbitNormal(), o.Up(burnUT));
Vector3d actualHorizontalVelocity = Vector3d.Exclude(o.Up(burnUT), o.SwappedOrbitalVelocityAtUT(burnUT));
Vector3d desiredHorizontalVelocity = actualHorizontalVelocity.magnitude * desiredHorizontal;
return desiredHorizontalVelocity - actualHorizontalVelocity;
} else {
// Try to match a retrograde orbit with a prograde one
Vector3d desiredHorizontal = Vector3d.Cross(o.Up(burnUT), target.SwappedOrbitNormal());
Vector3d actualHorizontalVelocity = Vector3d.Exclude(o.Up(burnUT), o.SwappedOrbitalVelocityAtUT(burnUT));
Vector3d desiredHorizontalVelocity = actualHorizontalVelocity.magnitude * desiredHorizontal;
return desiredHorizontalVelocity - actualHorizontalVelocity;
}
}

}
Expand Down
22 changes: 22 additions & 0 deletions src/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ private const string
ShowSettingsKey = "ShowSettings",
TransferSortKey = "TransferSort",
DescendingSortKey = "DescendingSort",
DisplayUnitsKey = "DisplayUnits",

GeneratePlaneChangeBurnsKey = "GeneratePlaneChangeBurns",
AddPlaneChangeDeltaVKey = "AddPlaneChangeDeltaV",
Expand Down Expand Up @@ -316,6 +317,27 @@ public bool DescendingSort {
}
}

/// <summary>
/// Unit system for display of physical quantities.
/// </summary>
public DisplayUnitsEnum DisplayUnits {
get {
DisplayUnitsEnum du = DisplayUnitsEnum.Metric;
string savedValue = "";
if (config.TryGetValue(DisplayUnitsKey, ref savedValue)) {
du = ParseEnum<DisplayUnitsEnum>(savedValue, DisplayUnitsEnum.Metric);
}
return du;
}
set {
if (config.HasValue(DisplayUnitsKey)) {
config.SetValue(DisplayUnitsKey, value.ToString());
} else {
config.AddValue(DisplayUnitsKey, value.ToString());
}
}
}

}

}
18 changes: 18 additions & 0 deletions src/SettingsView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,24 @@ public SettingsView()
(bool b) => { Settings.Instance.AutoEditPlaneChangeNode = b; }
));

AddChild(LabelWithStyleAndSize(
"Units:",
midHdrStyle,
mainWindowMinWidth, rowHeight
));

AddChild(new DialogGUIToggle(
() => Settings.Instance.DisplayUnits == DisplayUnitsEnum.Metric,
"Metric",
(bool b) => { if (b) Settings.Instance.DisplayUnits = DisplayUnitsEnum.Metric; }
));

AddChild(new DialogGUIToggle(
() => Settings.Instance.DisplayUnits == DisplayUnitsEnum.UnitedStatesCustomary,
"United States Customary",
(bool b) => { if (b) Settings.Instance.DisplayUnits = DisplayUnitsEnum.UnitedStatesCustomary; }
));

} catch (Exception ex) {
DbgExc("Problem constructing settings view", ex);
}
Expand Down
Loading

0 comments on commit e98b16f

Please sign in to comment.