/// <summary> /// Traces a space craft orbit by re-centering the world around the parent. /// </summary> public static void TraceSpaceCraft(SpaceCraftBase satellite, OrbitTrace trace) { IMassiveBody parent = satellite.GravitationalParent; DVector2 initialPosition = satellite.Position - parent.Position; var shipOffset = new DVector2(Math.Cos(satellite.Pitch) * (satellite.TotalWidth - satellite.Width), Math.Sin(satellite.Pitch) * (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); proxySatellite.SetGravitationalParent(proxyParent); int stepCount; double targetDt; double proximityDt; double orbitalDt = 0; bool isOrbiting; double altitude = proxyParent.GetRelativeHeight(proxySatellite.Position); double proximityAltitude = proxyParent.SurfaceRadius * 0.15; if (altitude < proximityAltitude) { stepCount = 1100; isOrbiting = false; proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = proximityDt; } else { stepCount = 600; isOrbiting = true; orbitalDt = GetOrbitalDt(initialPosition.Length(), parent.Mass, proxySatellite.Velocity.Length()); targetDt = orbitalDt; } // Assumes that the parent is (0,0) which will be true in this case double previousAngle = proxySatellite.Position.Angle(); double totalAngularDisplacement = 0; trace.Reset(satellite.Position - shipOffset); // Update steps based on orbit vs atmosphere for (int step = 0; step < stepCount; step++) { proxySatellite.ResetAccelerations(); proxySatellite.ResolveGravitation(proxyParent); proxySatellite.ResolveAtmopsherics(proxyParent); proxySatellite.Update(targetDt); double currentAngle = proxySatellite.Position.Angle(); totalAngularDisplacement += DeltaAngle(currentAngle, previousAngle); // Made a full orbit if (totalAngularDisplacement > AngularCutoff) { break; } previousAngle = currentAngle; altitude = proxyParent.GetRelativeHeight(proxySatellite.Position); double velocity = proxySatellite.GetRelativeVelocity().Length(); double offsetFactor = 0.0; //if (altitude < proxyParent.AtmosphereHeight*2) if (altitude < proxyParent.AtmosphereHeight * 4) { if (velocity < 3000) { offsetFactor = 1.0; } else if (velocity < 4000) { offsetFactor = 1.0 - velocity / 3000.0; } } // Check if reference frame shifting needs to occur in atmosphere if (offsetFactor > 0.0001) { 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= r * 2pi ) double pathCirumference = Constants.TwoPi * 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.Length(), parent.Mass, proxySatellite.Velocity.Length()); targetDt = MathHelper.Lerp(targetDt, orbitalDt, 0.1); isOrbiting = true; stepCount = 600; } else { proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = MathHelper.Lerp(targetDt, proximityDt, 0.75); } } trace.AddPoint(proxySatellite.Position + parent.Position, altitude); } }
private void PredictTargetThrottle(SpaceCraftBase spaceCraft) { double optimalThrust = _currentThrust; double optimalLandingSpeed = double.PositiveInfinity; for (int i = -1; i <= 1; i++) { double thrust = _currentThrust; thrust += i; if (thrust < 40 || 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 (landingSpeed < optimalLandingSpeed) { 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); } _currentThrust = optimalThrust; } }
/// <summary> /// Traces a space craft orbit by re-centering the world around the parent. /// </summary> public static void TraceSpaceCraft(SpaceCraftBase satellite, OrbitTrace trace) { IMassiveBody parent = satellite.GravitationalParent; DVector2 initialPosition = satellite.Position - parent.Position; var shipOffset = new DVector2(Math.Cos(satellite.Pitch) * (satellite.TotalWidth - satellite.Width), Math.Sin(satellite.Pitch) * (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); proxySatellite.SetGravitationalParent(proxyParent); 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 = 1100; isOrbiting = false; proximityDt = GetProximityDt(altitude, proximityAltitude); targetDt = proximityDt; } else { stepCount = 600; isOrbiting = true; orbitalDt = GetOrbitalDt(initialPosition, proxySatellite.Velocity, out orbitalTerminationRadius); targetDt = orbitalDt; } trace.Reset(satellite.Position - shipOffset); // 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); double velocity = proxySatellite.GetRelativeVelocity().Length(); double offsetFactor = 0.0; if (altitude < proxyParent.AtmosphereHeight * 2) { if (velocity < 3000) { offsetFactor = 1.0; } else if (velocity < 4000) { offsetFactor = 1.0 - velocity / 3000.0; } } // Check if reference frame shifting needs to occur in atmosphere if (offsetFactor > 0.0001) { 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 = 600; } 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); } }