Skip to content

Commit

Permalink
Rename to Two-Impulse Maneuver and support fixed time
Browse files Browse the repository at this point in the history
Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
  • Loading branch information
lamont-granquist committed Oct 22, 2023
1 parent cba3f84 commit ed96b5d
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 78 deletions.
4 changes: 2 additions & 2 deletions Localization/en-us.cfg
Expand Up @@ -114,10 +114,10 @@ Localization
#MechJeb_adv_Exception4 = Invalid point selected.

//Hohmann transfer
#MechJeb_Hohm_title = bi-impulsive (Hohmann) transfer to target
#MechJeb_Hohm_title = two impulse (Hohmann) transfer to target
#MechJeb_Hohm_intercept_only = no insertion burn (impact/flyby)
#MechJeb_Hohm_simpleTransfer = coplanar maneuver
#MechJeb_Hohm_Label1 = lag time offset
#MechJeb_Hohm_Label1 = rendezvous time offset

#MechJeb_Hohm_Exception1 = must select a target for the maneuver.
#MechJeb_Hohm_Exception2 = target for maneuver must be in the same sphere of influence.
Expand Down
41 changes: 23 additions & 18 deletions MechJeb2/Maneuver/OperationTransfer.cs
Expand Up @@ -61,7 +61,6 @@ public override void DoParametersGUI(Orbit o, double universalTime, MechJebModul
GUILayout.EndHorizontal();
if (Rendezvous)
GuiUtils.SimpleTextBox(Localizer.Format("#MechJeb_Hohm_Label1"), LagTime, "sec"); //fractional target period offset

_timeSelector.DoChooseTimeGUI();
}

Expand All @@ -75,22 +74,6 @@ protected override List<ManeuverParameters> MakeNodesImpl(Orbit o, double univer
new OperationException(
Localizer.Format("#MechJeb_Hohm_Exception2")); //target for bi-impulsive transfer must be in the same sphere of influence.

bool anExists = o.AscendingNodeExists(target.TargetOrbit);
bool dnExists = o.DescendingNodeExists(target.TargetOrbit);

switch (_timeSelector.TimeReference)
{
case TimeReference.REL_ASCENDING when !anExists:
throw new OperationException(Localizer.Format("#MechJeb_Hohm_Exception3")); //ascending node with target doesn't exist.
case TimeReference.REL_DESCENDING when !dnExists:
throw new OperationException(Localizer.Format("#MechJeb_Hohm_Exception4")); //descending node with target doesn't exist.
case TimeReference.REL_NEAREST_AD when !(anExists || dnExists):
throw new OperationException(
Localizer.Format("#MechJeb_Hohm_Exception5")); //neither ascending nor descending node with target exists.
}

double ut = _timeSelector.ComputeManeuverTime(o, universalTime, target);

if (target.Target is CelestialBody && Capture && PlanCapture)
ErrorMessage =
"Insertion burn to a celestial with an SOI is not supported by this maneuver. A Transfer-to-Moon maneuver needs to be written to properly support this case.";
Expand All @@ -99,8 +82,30 @@ protected override List<ManeuverParameters> MakeNodesImpl(Orbit o, double univer

double lagTime = Rendezvous ? LagTime.val : 0;

bool fixedTime = false;

if (_timeSelector.TimeReference != TimeReference.COMPUTED)
{
bool anExists = o.AscendingNodeExists(target.TargetOrbit);
bool dnExists = o.DescendingNodeExists(target.TargetOrbit);

if (_timeSelector.TimeReference == TimeReference.REL_ASCENDING && !anExists)
throw new OperationException(Localizer.Format("#MechJeb_Hohm_Exception3")); //ascending node with target doesn't exist.

if (_timeSelector.TimeReference == TimeReference.REL_DESCENDING && !dnExists)
throw new OperationException(Localizer.Format("#MechJeb_Hohm_Exception4")); //descending node with target doesn't exist.

if (_timeSelector.TimeReference == TimeReference.REL_NEAREST_AD && !(anExists || dnExists))
throw new OperationException(
Localizer.Format("#MechJeb_Hohm_Exception5")); //neither ascending nor descending node with target exists.

universalTime = _timeSelector.ComputeManeuverTime(o, universalTime, target);
fixedTime = true;
}

(Vector3d dV1, double ut1, Vector3d dV2, double ut2) =
OrbitalManeuverCalculator.DeltaVAndTimeForHohmannTransfer(o, targetOrbit, ut, lagTime, Coplanar, Rendezvous, Capture);
OrbitalManeuverCalculator.DeltaVAndTimeForHohmannTransfer(o, targetOrbit, universalTime, lagTime, fixedTime, Coplanar, Rendezvous,
Capture);

if (Capture && PlanCapture)
return new List<ManeuverParameters> { new ManeuverParameters(dV1, ut1), new ManeuverParameters(dV2, ut2) };
Expand Down
2 changes: 1 addition & 1 deletion MechJeb2/MechJeb2.csproj
Expand Up @@ -114,7 +114,7 @@
<Compile Include="MechJebLib\Core\TwoBody\Shepperd.cs"/>
<Compile Include="MechJebLib\Core\TwoBody\Farnocchia.cs"/>
<Compile Include="MechJebLib\Maneuvers\ChangeOrbitalElement.cs"/>
<Compile Include="MechJebLib\Maneuvers\CoplanarTransfer.cs"/>
<Compile Include="MechJebLib\Maneuvers\TwoImpulseTransfer.cs"/>
<Compile Include="MechJebLib\Maneuvers\ReturnFromMoon.cs"/>
<Compile Include="MechJebLib\Maneuvers\Simple.cs"/>
<Compile Include="MechJebLib\Primitives\Dual.cs"/>
Expand Down
Expand Up @@ -7,7 +7,7 @@

namespace MechJebLib.Maneuvers
{
public class CoplanarTransfer
public static class TwoImpulseTransfer
{
private struct Args
{
Expand Down Expand Up @@ -54,8 +54,8 @@ private static void NLPFunction(double[] x, ref double func, object obj)
func = dv1.magnitude;
}

public static (V3 dv1, double dt1, V3 dv2, double dt2) Maneuver(double mu, V3 r1, V3 v1, V3 r2, V3 v2, double dtguess, double offsetGuess,
bool coplanar = true, bool rendezvous = true, bool capture = true, double dtmin = double.NegativeInfinity,
private static (V3 dv1, double dt1, V3 dv2, double dt2) Maneuver(double mu, V3 r1, V3 v1, V3 r2, V3 v2, double dtguess, double offsetGuess,
bool coplanar = true, bool capture = true, double dtmin = double.NegativeInfinity,
double dtmax = double.PositiveInfinity,
double ttmin = double.NegativeInfinity, double ttmax = double.PositiveInfinity, double offsetMin = double.NegativeInfinity,
double offsetMax = double.PositiveInfinity, bool optguard = false)
Expand Down Expand Up @@ -138,7 +138,7 @@ private static void NLPFunction(double[] x, ref double func, object obj)

if (rep.terminationtype < 0)
throw new Exception(
$"CoplanarTransfer.Maneuver({mu}, {r1}, {v1}, {r2}, {v2}): CG solver terminated abnormally: {rep.terminationtype}"
$"TwoImpulseTransfer.Maneuver({mu}, {r1}, {v1}, {r2}, {v2}): CG solver terminated abnormally: {rep.terminationtype}"
);

if (optguard)
Expand All @@ -157,64 +157,82 @@ private static void NLPFunction(double[] x, ref double func, object obj)
return (dv1 * scale.VelocityScale, x[0] * scale.TimeScale, dv2 * scale.VelocityScale, (x[0] + x[1]) * scale.TimeScale);
}

public static (V3 dv1, double dt1, V3 dv2, double dt2) NextManeuver(double mu, V3 r1, V3 v1, V3 r2, V3 v2, int maxiter = 50,
double lagTime = double.NaN, bool coplanar = true, bool rendezvous = true, bool capture = true, bool optguard = false)
private static (V3 dv1, double dt1, V3 dv2, double dt2) ManeuverInternal(double mu, V3 r1, V3 v1, V3 r2, V3 v2, double dtguess,
double lagTime = double.NaN, bool coplanar = true, bool rendezvous = true, bool capture = true,
bool fixedtime = false, bool optguard = false)
{
double synodicPeriod = Maths.SynodicPeriod(mu, r1, v1, r2, v2);
double targetPeriod = Maths.PeriodFromStateVectors(mu, r2, v2);
double dtguess = 0;
V3 dv1, dv2;
double dt1, dt2;

for (int iter = 0; iter < maxiter; iter++)
double dtmin = double.NegativeInfinity;
double dtmax = double.PositiveInfinity;

if (fixedtime)
{
V3 dv1, dv2;
double dt1, dt2;
double offsetGuess = 0;
dtmin = dtguess;
dtmax = dtguess;
}

if (rendezvous)
if (rendezvous)
{
double offsetGuess = 0;
double offsetMin = 0;
double offsetMax = 0;
if (lagTime.IsFinite())
{
double offsetMin = 0;
double offsetMax = 0;
if (lagTime.IsFinite())
{
offsetMin = -lagTime;
offsetMax = -lagTime;
offsetGuess = -lagTime;
}

(dv1, dt1, dv2, dt2) =
Maneuver(mu, r1, v1, r2, v2, dtguess, offsetGuess, offsetMin: offsetMin, offsetMax: offsetMax, coplanar: coplanar,
rendezvous: true, capture: capture,
optguard: optguard);
offsetMin = -lagTime;
offsetMax = -lagTime;
offsetGuess = -lagTime;
}

else
(dv1, dt1, dv2, dt2) =
Maneuver(mu, r1, v1, r2, v2, dtguess, offsetGuess, dtmin: dtmin, dtmax: dtmax, offsetMin: offsetMin, offsetMax: offsetMax, coplanar: coplanar, capture: capture, optguard: optguard);
}
else
{
(dv1, dt1, dv2, dt2) =
Maneuver(mu, r1, v1, r2, v2, dtguess, 0, dtmin: dtmin, dtmax: dtmax , coplanar: coplanar, capture: capture, optguard: optguard);

// we have to try the other side of the target orbit since we might get eg. the DN instead of the AN when the AN is closer
// (this may be insufficient and may need more of a search box but then we're O(N^2) and i think basinhopping or porkchop
// plots will be the better solution)
double targetPeriod = Maths.PeriodFromStateVectors(mu, r2, v2);

(V3 a, double b, V3 c, double d) =
Maneuver(mu, r1, v1, r2, v2, dtguess, targetPeriod * 0.5, coplanar: coplanar, capture: capture, optguard: optguard);

if (b > 0 && (b < dt1 || dt1 < 0))
{
(dv1, dt1, dv2, dt2) =
Maneuver(mu, r1, v1, r2, v2, dtguess, offsetGuess, coplanar, false, capture,
optguard: optguard);

// we have to try the other side of the target orbit since we might get eg. the DN instead of the AN when the AN is closer
// (this may be insufficient and may need more of a search box but then we're O(N^2) and i think basinhopping or porkchop
// plots will be the better solution)

(V3 a, double b, V3 c, double d) =
Maneuver(mu, r1, v1, r2, v2, dtguess, targetPeriod * 0.5, coplanar, optguard: optguard,
rendezvous: false, capture: capture);
if (b > 0 && (b < dt1 || dt1 < 0))
{
dv1 = a;
dt1 = b;
dv2 = c;
dt2 = d;
}
dv1 = a;
dt1 = b;
dv2 = c;
dt2 = d;
}
}

return (dv1, dt1, dv2, dt2);
}

public static (V3 dv1, double dt1, V3 dv2, double dt2) NextManeuver(double mu, V3 r1, V3 v1, V3 r2, V3 v2, int maxiter = 50,
double lagTime = double.NaN, bool coplanar = true, bool rendezvous = true, bool capture = true,
bool fixedTime = false, bool optguard = false)
{
double synodicPeriod = Maths.SynodicPeriod(mu, r1, v1, r2, v2);

if (fixedTime)
return ManeuverInternal(mu, r1, v1, r2, v2, dtguess: 0, coplanar: coplanar, rendezvous: rendezvous, capture: capture, optguard: optguard, lagTime:lagTime, fixedtime: true);

double dtguess = 0;
for (int iter = 0; iter < maxiter; iter++)
{
(V3 dv1, double dt1, V3 dv2, double dt2) = ManeuverInternal(mu, r1, v1, r2, v2, dtguess: dtguess, coplanar: coplanar, rendezvous: rendezvous, capture: capture, optguard: optguard, lagTime:lagTime);

if (dt1 > 0)
return (dv1, dt1, dv2, dt2);
dtguess += synodicPeriod * 0.10;
}

throw new MechJebLibException($"CoplanarTransfer.NextManeuver({mu}, {r1}, {v1}, {v2}, {r2}): too many iterations");
throw new MechJebLibException($"TwoImpulseTransfer.NextManeuver({mu}, {r1}, {v1}, {v2}, {r2}): too many iterations");
}
}
}
4 changes: 2 additions & 2 deletions MechJeb2/OrbitalManeuverCalculator.cs
Expand Up @@ -154,14 +154,14 @@ public static Vector3d DeltaVAndTimeToMatchPlanesDescending(Orbit o, Orbit targe
//Assumes o and target are in approximately the same plane, and orbiting in the same direction.
//Also assumes that o is a perfectly circular orbit (though result should be OK for small eccentricity).
public static ( Vector3d dV1, double UT1, Vector3d dV2, double UT2) DeltaVAndTimeForHohmannTransfer(Orbit o, Orbit target, double ut,
double lagTime = double.NaN,
double lagTime = double.NaN, bool fixedTime = false,
bool coplanar = true, bool rendezvous = true, bool capture = true)
{
(V3 r1, V3 v1) = o.RightHandedStateVectorsAtUT(ut);
(V3 r2, V3 v2) = target.RightHandedStateVectorsAtUT(ut);

(V3 dv1, double dt1, V3 dv2, double dt2) =
CoplanarTransfer.NextManeuver(o.referenceBody.gravParameter, r1, v1, r2, v2, lagTime: lagTime, coplanar: coplanar,
TwoImpulseTransfer.NextManeuver(o.referenceBody.gravParameter, r1, v1, r2, v2, lagTime: lagTime, coplanar: coplanar,
rendezvous: rendezvous, capture: capture);

return (dv1.V3ToWorld(), ut + dt1, dv2.V3ToWorld(), ut + dt2);
Expand Down
Expand Up @@ -11,11 +11,11 @@

namespace MechJebLibTest.ManeuversTests
{
public class CoplanarTransferTests
public class TwoImpulseTransferTests
{
private readonly ITestOutputHelper _testOutputHelper;

public CoplanarTransferTests(ITestOutputHelper testOutputHelper)
public TwoImpulseTransferTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
Expand Down Expand Up @@ -50,16 +50,16 @@ private void HohmannTest()
if (synodicPeriod > 1000)
continue;

(V3 dv1, double dt1, V3 dv2, double dt2) = CoplanarTransfer.NextManeuver(mu, r1, v1, r2, v2);
(V3 dv1, double dt1, V3 dv2, double dt2) = TwoImpulseTransfer.NextManeuver(mu, r1, v1, r2, v2);

(double dv1Hoh, double dv2Hoh, double ttHoh, double _) = Maths.HohmannTransferParameters(mu, r1, r2);

dv1.magnitude.ShouldEqual(Abs(dv1Hoh), 1e-6);
dv2.magnitude.ShouldEqual(Abs(dv2Hoh), 1e-6);
(dt2-dt1).ShouldEqual(ttHoh, 1e-3);
(dt2 - dt1).ShouldEqual(ttHoh, 1e-3);

(V3 rburn1, V3 vburn1) = Shepperd.Solve(mu, dt1, r1, v1);
(V3 rburn2, V3 vburn2) = Shepperd.Solve(mu, dt2-dt1, rburn1, vburn1 + dv1);
(V3 rburn2, V3 vburn2) = Shepperd.Solve(mu, dt2 - dt1, rburn1, vburn1 + dv1);
(V3 rf, V3 vf) = Shepperd.Solve(mu, dt2, r2, v2);

rf.ShouldEqual(rburn2, 1e-6);
Expand All @@ -77,7 +77,7 @@ private void GeoTestFixed()
double nu = 3.24639265358979;
(V3 r2, V3 v2) = Maths.StateVectorsFromKeplerian(mu, 42164000, 0, 0, 0, 0, nu);

(V3 dv1, double dt1, V3 dv2, double dt2) = CoplanarTransfer.NextManeuver(mu, r1, v1, r2, v2, coplanar: false);
(V3 dv1, double dt1, V3 dv2, double dt2) = TwoImpulseTransfer.NextManeuver(mu, r1, v1, r2, v2, coplanar: false);
double dv = dv1.magnitude + dv2.magnitude;
(V3 rburn1, V3 vburn1) = Shepperd.Solve(mu, dt1, r1, v1);
(V3 rburn2, V3 vburn2) = Shepperd.Solve(mu, dt2, rburn1, vburn1 + dv1);
Expand All @@ -100,7 +100,7 @@ private void GeoTestFree()

(V3 r2, V3 v2) = Maths.StateVectorsFromKeplerian(mu, 42164000, 0, 0, 0, 0, 0);

(V3 dv1, double dt1, V3 dv2, double dt2) = CoplanarTransfer.NextManeuver(mu, r1, v1, r2, v2, coplanar: false, rendezvous: false);
(V3 dv1, double dt1, V3 dv2, double dt2) = TwoImpulseTransfer.NextManeuver(mu, r1, v1, r2, v2, coplanar: false, rendezvous: false);
double dv = dv1.magnitude + dv2.magnitude;
(V3 rburn1, V3 vburn1) = Shepperd.Solve(mu, dt1, r1, v1);
(V3 rburn2, V3 vburn2) = Shepperd.Solve(mu, dt2, rburn1, vburn1 + dv1);
Expand Down
2 changes: 1 addition & 1 deletion MechJebLibTest/MechJebLibTest.csproj
Expand Up @@ -59,7 +59,7 @@
<Compile Include="AssertionExtensions.cs"/>
<Compile Include="ControlTests\PIDLoopTests.cs"/>
<Compile Include="ManeuversTests\ChangeOrbitalElementTests.cs"/>
<Compile Include="ManeuversTests\CoplanarTransferTests.cs"/>
<Compile Include="ManeuversTests\TwoImpulseTransferTests.cs"/>
<Compile Include="ManeuversTests\ReturnFromMoonTests.cs"/>
<Compile Include="ManeuversTests\Simple.cs"/>
<Compile Include="MathsTests\BisectionTests.cs"/>
Expand Down

0 comments on commit ed96b5d

Please sign in to comment.