/// <summary> /// Traces a space craft orbit by re-centering the world around the parent. /// </summary> public static OrbitTrace TraceSpaceCraft(SpaceCraftBase satellite) { IMassiveBody parent = satellite.GravitationalParent; DVector2 initialPosition = satellite.Position - parent.Position; var shipOffset = new DVector2(Math.Cos(satellite.Rotation) * (satellite.TotalWidth - satellite.Width), Math.Sin(satellite.Rotation) * (satellite.TotalHeight - satellite.Height)) * 0.5; initialPosition -= shipOffset; var proxyParent = new MassiveBodyProxy(DVector2.Zero, DVector2.Zero, parent); var proxySatellite = new SpaceCraftProxy(initialPosition, satellite.Velocity - parent.Velocity, satellite); int stepCount; double targetDt; double proximityDt; double orbitalDt = 0; bool isOrbiting; double orbitalTerminationRadius = 0; double altitude = proxyParent.GetRelativeHeight(proxySatellite.Position); double proximityAltitude = proxyParent.SurfaceRadius * 0.15; if (altitude < proximityAltitude) { stepCount = 1000; isOrbiting = false; proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = proximityDt; } else { stepCount = 300; isOrbiting = true; orbitalDt = GetOrbitalDt(initialPosition, proxySatellite.Velocity, out orbitalTerminationRadius); targetDt = orbitalDt; } var trace = new OrbitTrace(satellite.Position - shipOffset, altitude); // Simulate 300 orbital steps, more for proximity for (int step = 0; step < stepCount; step++) { proxySatellite.ResetAccelerations(); proxySatellite.ResolveGravitation(proxyParent); proxySatellite.ResolveAtmopsherics(proxyParent); proxySatellite.Update(targetDt); altitude = proxyParent.GetRelativeHeight(proxySatellite.Position); // Check if reference frame shifting needs to occur in atmosphere if (altitude < proxyParent.AtmosphereHeight) { double offsetFactor = 1.0 - (altitude / proxyParent.AtmosphereHeight); DVector2 difference = proxyParent.Position - proxySatellite.Position; difference.Normalize(); var surfaceNormal = new DVector2(-difference.Y, difference.X); double altitudeFromCenter = altitude + proxyParent.SurfaceRadius; // Distance of circumference at this altitude ( c= 2r * pi ) double pathCirumference = 2 * Math.PI * altitudeFromCenter; double rotationalSpeed = pathCirumference / parent.RotationPeriod; DVector2 atmopshereVelocity = surfaceNormal * rotationalSpeed; proxySatellite.ApplyFrameOffset(atmopshereVelocity * offsetFactor * targetDt); // Return early if the trace goes into a planet if (altitude <= 0) { trace.AddPoint(proxySatellite.Position + parent.Position, altitude); break; } } // Determine the correct change overs from orbital to surface proximity if (isOrbiting) { if (altitude < proximityAltitude) { proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = MathHelper.Lerp(targetDt, proximityDt, 0.75); isOrbiting = false; stepCount = 1000; } else { targetDt = MathHelper.Lerp(targetDt, orbitalDt, 0.1); } } else { if (altitude > proximityAltitude) { orbitalDt = GetOrbitalDt(proxySatellite.Position, proxySatellite.Velocity, out orbitalTerminationRadius); targetDt = MathHelper.Lerp(targetDt, orbitalDt, 0.1); isOrbiting = true; stepCount = 300; } else { proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = MathHelper.Lerp(targetDt, proximityDt, 0.75); } } // Check expensive termination conditions after half of the iterations if (isOrbiting && step > 100) { DVector2 offsetVector = proxySatellite.Position - initialPosition; double distanceFromStart = offsetVector.Length(); // Terminate and add the end point if (distanceFromStart < orbitalTerminationRadius) { trace.AddPoint(proxySatellite.Position + parent.Position, altitude); break; } } trace.AddPoint(proxySatellite.Position + parent.Position, altitude); } return trace; }
private void PredictTargetThrottle(SpaceCraftBase spaceCraft) { double optimalThrust = _currentThrust; double optimalLandingSpeed = double.PositiveInfinity; for (int i = -1; i <= 1; i++) { double thrust = 40; // double thrust = 65; if (_currentThrust > 0) { thrust = _currentThrust; } thrust += i * 0.5; if (thrust < 40 || thrust > 100) //if (thrust < 60 || thrust > 100) { continue; } IMassiveBody parent = spaceCraft.GravitationalParent; DVector2 initialPosition = spaceCraft.Position - parent.Position; var proxyParent = new MassiveBodyProxy(DVector2.Zero, DVector2.Zero, parent); var proxySatellite = new SpaceCraftProxy(initialPosition, spaceCraft.Velocity - parent.Velocity, spaceCraft); foreach (int id in _engineIds) { proxySatellite.Engines[id].Startup(); proxySatellite.Engines[id].AdjustThrottle(thrust); } proxySatellite.SetGravitationalParent(proxyParent); // Simulate until the rocket runs out of fuel or touches down for (int step = 0; step < 1000; step++) { proxySatellite.ResetAccelerations(); proxySatellite.ResolveGravitation(proxyParent); proxySatellite.ResolveAtmopsherics(proxyParent); if (proxySatellite.PropellantMass <= 0) { break; } if (proxySatellite.OnGround) { double landingSpeed = proxySatellite.RelativeVelocity.Length(); // If the rocket is in motion search for the best speed always if (_currentThrust > 0) { if (landingSpeed < optimalLandingSpeed) { optimalLandingSpeed = landingSpeed; optimalThrust = thrust; break; } } // Otherwise enforce the landing speed is very good to start else if (landingSpeed < 5) { optimalLandingSpeed = landingSpeed; optimalThrust = thrust; break; } } proxySatellite.Update(0.05); } } // Set the target engines to the optimal computed thrust if (_engineIds != null) { // Startup the required landing engines foreach (int id in _engineIds) { IEngine engine = spaceCraft.Engines[id]; engine.AdjustThrottle(optimalThrust); } if (optimalThrust > 0) { _currentThrust = optimalThrust; } } }