void DriveVerticalAscent(FlightCtrlState s) { if (vesselState.altitudeASL > ascentPath.VerticalAscentEnd()) { mode = AscentMode.GRAVITY_TURN; } if (autoThrottle && orbit.ApA > desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; } //during the vertical ascent we just thrust straight up at max throttle if (forceRoll && vesselState.altitudeTrue > 50) { // pre-align roll unless correctiveSteering is active as it would just interfere with that core.attitude.attitudeTo(90 - desiredInclination, 90, verticalRoll, this); } else { core.attitude.attitudeTo(Vector3d.up, AttitudeReference.SURFACE_NORTH, this); } if (autoThrottle) { core.thrust.targetThrottle = 1.0F; } if (!vessel.LiftedOff()) { status = "Awaiting liftoff"; } else { status = "Vertical ascent"; } }
void DriveVerticalAscent(FlightCtrlState s) { if (vesselState.altitudeASL > ascentPath.VerticalAscentEnd()) { mode = AscentMode.GRAVITY_TURN; } if (autoThrottle && orbit.ApA > desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; } //during the vertical ascent we just thrust straight up at max throttle core.attitude.attitudeTo(Vector3d.up, AttitudeReference.SURFACE_NORTH, this); if (autoThrottle) { core.thrust.targetThrottle = 1.0F; } if (!vessel.LiftedOff()) { status = "Awaiting liftoff"; } else { status = "Vertical ascent"; } }
void DriveAscent(FlightCtrlState s) { if (timedLaunch) { Debug.Log("Awaiting Liftoff"); status = "Awaiting liftoff"; core.attitude.AxisControl(false, false, false); return; } DriveDeployableComponents(s); if (ascentPath.DriveAscent(s)) { if (GameSettings.VERBOSE_DEBUG_LOG) { Debug.Log("Remaining in Ascent"); } status = ascentPath.status; } else { if (GameSettings.VERBOSE_DEBUG_LOG) { Debug.Log("Ascend -> Circularize"); } mode = AscentMode.CIRCULARIZE; } }
void DriveVerticalAscent(FlightCtrlState s) { if (!IsVerticalAscent(vesselState.altitudeTrue, vesselState.speedSurface)) { mode = AscentMode.GRAVITY_TURN; } if (autopilot.autoThrottle && orbit.ApA > autopilot.desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; } //during the vertical ascent we just thrust straight up at max throttle attitudeTo(90); core.attitude.AxisControl(!vessel.Landed, !vessel.Landed, !vessel.Landed && vesselState.altitudeBottom > 50); if (autopilot.autoThrottle) { core.thrust.targetThrottle = 1.0F; } if (!vessel.LiftedOff() || vessel.Landed) { status = Localizer.Format("#MechJeb_Ascent_status6"); //"Awaiting liftoff" } else { status = Localizer.Format("#MechJeb_Ascent_status9"); //"Vertical ascent" } }
void DriveVerticalAscent(FlightCtrlState s) { if (!IsVerticalAscent(vesselState.altitudeASL, vesselState.speedSurface)) { mode = AscentMode.INITIATE_TURN; } if (autopilot.autoThrottle && orbit.ApA > intermediateAltitude) { mode = AscentMode.GRAVITY_TURN; } //during the vertical ascent we just thrust straight up at max throttle attitudeTo(90); core.attitude.AxisControl(!vessel.Landed, !vessel.Landed, !vessel.Landed && vesselState.altitudeBottom > 50); if (autopilot.autoThrottle) { core.thrust.targetThrottle = 1.0F; } if (!vessel.LiftedOff() || vessel.Landed) { status = "Awaiting liftoff"; } else { status = "Vertical ascent"; } }
void DriveInitiateTurn(FlightCtrlState s) { //stop the intermediate "burn" when our apoapsis reaches the desired altitude if (orbit.ApA > autopilot.desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } if ((90 - turnStartPitch) >= srfvelPitch()) { mode = AscentMode.GRAVITY_TURN; return; } //if we've fallen below the turn start altitude, go back to vertical ascent if (IsVerticalAscent(vesselState.altitudeTrue, vesselState.speedSurface)) { mode = AscentMode.VERTICAL_ASCENT; return; } attitudeTo(90 - turnStartPitch); // do Throttle control and set status if we are not fine tuning if (!ControlThrottle()) { status = "Initiate breathing gravity turn"; } }
void DriveHoldAP(FlightCtrlState s) { //stop the intermediate "burn" when our apoapsis reaches the desired altitude if (orbit.ApA > autopilot.desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } // generally this is in response to typing numbers into the intermediate altitude text box and // an accidental transition to later in the state machine if (orbit.ApA < 0.98 * intermediateAltitude) { mode = AscentMode.GRAVITY_TURN; return; } attitudeTo(0); /* FIXME: corrective steering */ if (fixedTimeToAp() < holdAPTime) { core.thrust.targetThrottle = 1.0F; } else { core.thrust.targetThrottle = 0.1F; } status = "Holding AP"; }
private void DriveVerticalAscent(FlightCtrlState s) { //during the vertical ascent we just thrust straight up at max throttle attitudeTo(90); if (autopilot.autoThrottle) { core.thrust.targetThrottle = 1.0F; } core.attitude.AxisControl(!vessel.Landed, !vessel.Landed, !vessel.Landed && vesselState.altitudeBottom > 50); if (!vessel.LiftedOff() || vessel.Landed) { status = "Awaiting liftoff"; } else { if (autopilot.MET > pitchStartTime) { mode = AscentMode.INITIATE_TURN; return; } double dt = pitchStartTime - autopilot.MET; status = String.Format("Vertical ascent {0:F2} s", dt); } }
void DriveAscent(FlightCtrlState s) { if (timedLaunch) { Debug.Log("Awaiting Liftoff"); status = Localizer.Format("#MechJeb_Ascent_status6");//"Awaiting liftoff" // kill the optimizer if it is running. core.guidance.enabled = false; core.attitude.AxisControl(false, false, false); return; } DriveDeployableComponents(s); if (ascentPath.DriveAscent(s)) { if (GameSettings.VERBOSE_DEBUG_LOG) { Debug.Log("Remaining in Ascent"); } status = ascentPath.status; } else { if (GameSettings.VERBOSE_DEBUG_LOG) { Debug.Log("Ascend -> Circularize"); } mode = AscentMode.CIRCULARIZE; } }
public override void OnModuleEnabled() { mode = AscentMode.VERTICAL_ASCENT; placedCircularizeNode = false; core.attitude.users.Add(this); core.thrust.users.Add(this); }
void DriveGravityTurn(FlightCtrlState s) { //stop the gravity turn when our apoapsis reaches the desired altitude if (autopilot.autoThrottle && orbit.ApA > autopilot.desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } //if we've fallen below the turn start altitude, go back to vertical ascent if (IsVerticalAscent(vesselState.altitudeASL, vesselState.speedSurface)) { mode = AscentMode.VERTICAL_ASCENT; return; } if (autopilot.autoThrottle) { core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, autopilot.desiredOrbitAltitude + mainBody.Radius); if (core.thrust.targetThrottle < 1.0F) { // follow surface velocity to reduce flipping attitudeTo(srfvelPitch()); status = "Fine tuning apoapsis"; return; } } double desiredFlightPathAngle = FlightPathAngle(vesselState.altitudeASL, vesselState.speedSurface); if (autopilot.correctiveSteering) { double actualFlightPathAngle = Math.Atan2(vesselState.speedVertical, vesselState.speedSurfaceHorizontal) * UtilMath.Rad2Deg; /* form an isosceles triangle with unit vectors pointing in the desired and actual flight path angle directions and find the length of the base */ double velocityError = 2 * Math.Sin(UtilMath.Deg2Rad * (desiredFlightPathAngle - actualFlightPathAngle) / 2); double difficulty = vesselState.surfaceVelocity.magnitude * 0.02 / vesselState.ThrustAccel(core.thrust.targetThrottle); difficulty = MuUtils.Clamp(difficulty, 0.1, 1.0); double steerOffset = autopilot.correctiveSteeringGain * difficulty * velocityError; double steerAngle = MuUtils.Clamp(Math.Asin(steerOffset) * UtilMath.Rad2Deg, -30, 30); desiredFlightPathAngle = MuUtils.Clamp(desiredFlightPathAngle + steerAngle, -90, 90); } attitudeTo(desiredFlightPathAngle); status = "Gravity turn"; }
public override void OnModuleEnabled() { mode = AscentMode.PRELAUNCH; placedCircularizeNode = false; launchLatitude = vesselState.latitude; core.attitude.users.Add(this); core.thrust.users.Add(this); if (autostage) { core.staging.users.Add(this); } }
void DriveCoastToApoapsis(FlightCtrlState s) { core.thrust.targetThrottle = 0; double circularSpeed = OrbitalManeuverCalculator.CircularOrbitSpeed(mainBody, orbit.ApR); double apoapsisSpeed = orbit.SwappedOrbitalVelocityAtUT(orbit.NextApoapsisTime(vesselState.time)).magnitude; double circularizeBurnTime = (circularSpeed - apoapsisSpeed) / vesselState.limitedMaxThrustAccel; //Once we get above the atmosphere, plan and execute the circularization maneuver. //For orbits near the edge of the atmosphere, we can't wait until we break the atmosphere //to start the burn, so we also compare the timeToAp with the expected circularization burn time. //if ((vesselState.altitudeASL > mainBody.RealMaxAtmosphereAltitude()) // || (vesselState.limitedMaxThrustAccel > 0 && orbit.timeToAp < circularizeBurnTime / 1.8)) // Sarbian : removed the special case for now. Some ship where turning whil still in atmosphere if (vesselState.altitudeASL > mainBody.RealMaxAtmosphereAltitude()) { mode = AscentMode.CIRCULARIZE; core.warp.MinimumWarp(); return; } //if our apoapsis has fallen too far, resume the gravity turn if (orbit.ApA < desiredOrbitAltitude - 1000.0) { mode = AscentMode.GRAVITY_TURN; core.warp.MinimumWarp(); return; } //point prograde and thrust gently if our apoapsis falls below the target core.attitude.attitudeTo(Vector3d.forward, AttitudeReference.ORBIT, this); core.thrust.targetThrottle = 0; if (autoThrottle && orbit.ApA < desiredOrbitAltitude) { core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, desiredOrbitAltitude + mainBody.Radius); } if (core.node.autowarp) { //warp at x2 physical warp: core.warp.WarpPhysicsAtRate(2); } status = "Coasting to edge of atmosphere"; }
void DriveVerticalAscent(FlightCtrlState s) { if (timedLaunch) { status = "Awaiting liftoff"; core.attitude.AxisControl(false, false, false); return; } if (!ascentPath.IsVerticalAscent(vesselState.altitudeASL, vesselState.speedSurface)) { mode = AscentMode.GRAVITY_TURN; } if (autoThrottle && orbit.ApA > desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; } //during the vertical ascent we just thrust straight up at max throttle if (forceRoll) { // pre-align roll unless correctiveSteering is active as it would just interfere with that double desiredHeading = OrbitalManeuverCalculator.HeadingForLaunchInclination(vessel.mainBody, desiredInclination, launchLatitude, OrbitalManeuverCalculator.CircularOrbitSpeed(vessel.mainBody, desiredOrbitAltitude + mainBody.Radius)); core.attitude.attitudeTo(desiredHeading, 90, verticalRoll, this); } else { core.attitude.attitudeTo(Vector3d.up, AttitudeReference.SURFACE_NORTH, this); } core.attitude.AxisControl(!vessel.Landed, !vessel.Landed, !vessel.Landed && vesselState.altitudeBottom > 50); if (autoThrottle) { core.thrust.targetThrottle = 1.0F; } if (!vessel.LiftedOff() || vessel.Landed) { status = "Awaiting liftoff"; } else { status = "Vertical ascent"; } }
private void DriveInitiateTurn(FlightCtrlState s) { double dt = autopilot.MET - pitchStartTime; double theta = dt * pitchRate; double pitch_program = 90 - theta; double pitch; if (!mainBody.atmosphere) { mode = AscentMode.GUIDANCE; return; } if (pitch_program > srfvelPitch()) { pitch = srfvelPitch(); status = Localizer.Format("#MechJeb_Ascent_status14", String.Format("{0:F}", pitch - core.guidance.pitch));//Gravity Turn <<1>>° to guidance } else { pitch = pitch_program; status = Localizer.Format("#MechJeb_Ascent_status15", String.Format("{0:F}", pitch - core.guidance.pitch));//Pitch program <<1>>° to guidance } if (pitch <= core.guidance.pitch && core.guidance.isStable()) { mode = AscentMode.GUIDANCE; return; } if ((vesselState.maxDynamicPressure > 0) && (vesselState.maxDynamicPressure * 0.90 > vesselState.dynamicPressure)) { mode = AscentMode.GUIDANCE; return; } if (mainBody.atmosphere && vesselState.maxDynamicPressure > 0) { // from 95% to 90% of dynamic pressure apply a "fade" from the pitch selected above to the guidance pitch double fade = MuUtils.Clamp((0.95 - vesselState.dynamicPressure / vesselState.maxDynamicPressure) * 20.0, 0.0, 1.0); pitch = fade * core.guidance.pitch + (1.0 - fade) * pitch; } attitudeTo(pitch, core.guidance.heading); }
void DriveGravityTurn(FlightCtrlState s) { //stop the intermediate "burn" when our apoapsis reaches the desired altitude if (orbit.ApA > autopilot.desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } // srfvelPitch == zero AoA attitudeTo(srfvelPitch()); // do Throttle control and set status if we are not fine tuning if (!ControlThrottle()) { status = "Breathing Gravity turn"; } }
void DriveCoastToApoapsis(FlightCtrlState s) { core.thrust.targetThrottle = 0; double circularSpeed = OrbitalManeuverCalculator.CircularOrbitSpeed(mainBody, orbit.ApR); double apoapsisSpeed = orbit.SwappedOrbitalVelocityAtUT(orbit.NextApoapsisTime(vesselState.time)).magnitude; double circularizeBurnTime = (circularSpeed - apoapsisSpeed) / vesselState.limitedMaxThrustAccel; if (vesselState.altitudeASL > mainBody.RealMaxAtmosphereAltitude()) { mode = AscentMode.EXIT; core.warp.MinimumWarp(); return; } //if our apoapsis has fallen too far, resume the gravity turn if (orbit.ApA < autopilot.desiredOrbitAltitude - 1000.0) { mode = AscentMode.HOLD_AP; core.warp.MinimumWarp(); return; } core.thrust.targetThrottle = 0; // follow surface velocity to reduce flipping attitudeTo(srfvelPitch()); if (autopilot.autoThrottle && orbit.ApA < autopilot.desiredOrbitAltitude) { core.warp.WarpPhysicsAtRate(1); core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, autopilot.desiredOrbitAltitude + mainBody.Radius); } else { if (core.node.autowarp) { //warp at x2 physical warp: core.warp.WarpPhysicsAtRate(2); } } status = "Coasting to edge of atmosphere"; }
private void DriveInitiateTurn(FlightCtrlState s) { if (autopilot.autoThrottle) { core.thrust.targetThrottle = 1.0F; } if (autopilot.MET > pitchEndTime) { mode = AscentMode.GRAVITY_TURN; return; } double dt = autopilot.MET - pitchStartTime; double theta = dt * pitchRate; attitudeTo(Math.Min(90, 90 - theta + pitchBias)); status = String.Format("Pitch program {0:F2} s", pitchEndTime - pitchStartTime - dt); }
void DriveInitiateTurn(FlightCtrlState s) { //stop the intermediate "burn" when our apoapsis reaches the desired altitude if (orbit.ApA > autopilot.desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } if ((90 - turnStartPitch) >= srfvelPitch()) { mode = AscentMode.GRAVITY_TURN; return; } if (orbit.ApA > intermediateAltitude) { mode = AscentMode.GRAVITY_TURN; return; } //if we've fallen below the turn start altitude, go back to vertical ascent if (IsVerticalAscent(vesselState.altitudeTrue, vesselState.speedSurface)) { mode = AscentMode.VERTICAL_ASCENT; return; } attitudeTo(90 - turnStartPitch); if (autopilot.autoThrottle) { core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, intermediateAltitude + mainBody.Radius); if (core.thrust.targetThrottle < 1.0F) { status = "Fine tuning intermediate altitude"; return; } } status = "Initiate gravity turn"; }
private void DriveGuidance(FlightCtrlState s) { if (core.guidance.status == PVGStatus.FINISHED) { mode = AscentMode.EXIT; return; } if (!core.guidance.isStable()) { double pitch = Math.Min(Math.Min(90, srfvelPitch()), vesselState.vesselPitch); attitudeTo(pitch, srfvelHeading()); status = Localizer.Format("#MechJeb_Ascent_status16");//"WARNING: Unstable Guidance" } else { status = Localizer.Format("#MechJeb_Ascent_status17");//"Stable Guidance" attitudeTo(core.guidance.pitch, core.guidance.heading); } }
void DriveRetractSolarPanels(FlightCtrlState s) { if (autoThrottle) { core.thrust.targetThrottle = 0.0f; } if (timedLaunch && tMinus > 10.0) { status = "Awaiting liftoff"; return; } status = "Retracting solar panels"; core.solarpanel.RetractAll(); if (core.solarpanel.AllRetracted()) { mode = AscentMode.VERTICAL_ASCENT; } }
public override void OnModuleEnabled() { if (core.solarpanel.autodeploySolarPanels && mainBody.atmosphere) { mode = AscentMode.RETRACT_SOLAR_PANELS; } else { mode = AscentMode.VERTICAL_ASCENT; } placedCircularizeNode = false; core.attitude.users.Add(this); core.thrust.users.Add(this); if (autostage) { core.staging.users.Add(this); } }
void DrivePrelaunch(FlightCtrlState s) { if (vessel.LiftedOff() && !vessel.Landed) { status = Localizer.Format("#MechJeb_Ascent_status4");//"Vessel is not landed, skipping pre-launch" mode = AscentMode.ASCEND; return; } if (autoThrottle) { Debug.Log("prelaunch killing throttle"); core.thrust.ThrustOff(); } core.attitude.AxisControl(false, false, false); if (timedLaunch && tMinus > 10.0) { status = Localizer.Format("#MechJeb_Ascent_status1");//"Pre Launch" return; } if (autodeploySolarPanels && mainBody.atmosphere) { core.solarpanel.RetractAll(); if (core.solarpanel.AllRetracted()) { Debug.Log("Prelaunch -> Ascend"); mode = AscentMode.ASCEND; } else { status = Localizer.Format("#MechJeb_Ascent_status5");//"Retracting solar panels" } } else { mode = AscentMode.ASCEND; } }
private void DriveGravityTurn(FlightCtrlState s) { if (autopilot.autoThrottle) { core.thrust.targetThrottle = 1.0F; } if (autopilot.MET < pitchEndTime) { /* this can happen when users update the endtime box */ mode = AscentMode.INITIATE_TURN; return; } if (stages[0].h >= h_burnout) { status = "Angular momentum target achieved"; core.thrust.targetThrottle = 0.0F; mode = AscentMode.EXIT; return; } if (saneGuidance && guidanceEnabled) { if (terminalGuidance) { status = "Locked Terminal Guidance"; } else { status = "Stable PEG Guidance"; } attitudeTo(guidancePitch); } else { // srfvelPitch == zero AoA status = "Unguided Gravity Turn"; attitudeTo(Math.Min(90, srfvelPitch() + pitchBias)); } }
void DriveRetractSolarPanels(FlightCtrlState s) { if (autoThrottle) { core.thrust.targetThrottle = 0.0f; } core.attitude.AxisControl(false, false, false); if (timedLaunch && tMinus > 10.0) { status = "Pre Launch"; return; } status = "Retracting solar panels"; core.solarpanel.RetractAll(); if (core.solarpanel.AllRetracted()) { mode = AscentMode.VERTICAL_ASCENT; } }
void DriveCoastToApoapsis(FlightCtrlState s) { core.thrust.targetThrottle = 0; double apoapsisSpeed = orbit.SwappedOrbitalVelocityAtUT(orbit.NextApoapsisTime(vesselState.time)).magnitude; if (vesselState.altitudeASL > mainBody.RealMaxAtmosphereAltitude()) { mode = AscentMode.EXIT; core.warp.MinimumWarp(); return; } //if our apoapsis has fallen too far, resume the gravity turn if (orbit.ApA < autopilot.desiredOrbitAltitude - 1000.0) { mode = AscentMode.GRAVITY_TURN; core.warp.MinimumWarp(); return; } core.thrust.targetThrottle = 0; // follow surface velocity to reduce flipping attitudeTo(srfvelPitch()); if (autopilot.autoThrottle && orbit.ApA < autopilot.desiredOrbitAltitude) { core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, autopilot.desiredOrbitAltitude + mainBody.Radius); } if (core.node.autowarp) { //warp at x2 physical warp: core.warp.WarpPhysicsAtRate(2); } status = Localizer.Format("#MechJeb_Ascent_status14");//"Coasting to edge of atmosphere" }
public override void OnModuleEnabled() { // since we cannot serialize enums, we serialize ascentPathIdx instead, but this bypasses the code in the property, so on module // enabling, we force that value back through the property to enforce sanity. doWiring(); ascentPath.enabled = true; mode = AscentMode.PRELAUNCH; placedCircularizeNode = false; launchLatitude = vesselState.latitude; core.attitude.users.Add(this); core.thrust.users.Add(this); if (autostage) { core.staging.users.Add(this); } status = Localizer.Format("#MechJeb_Ascent_status1");//"Pre Launch" }
private void DriveVerticalAscent(FlightCtrlState s) { //during the vertical ascent we just thrust straight up at max throttle attitudeTo(90, core.guidance.heading); core.attitude.AxisControl(!vessel.Landed, !vessel.Landed, !vessel.Landed && (vesselState.altitudeBottom > 50)); if (!vessel.LiftedOff() || vessel.Landed) { status = Localizer.Format("#MechJeb_Ascent_status12");//"Awaiting liftoff" } else { if (vesselState.surfaceVelocity.magnitude > pitchStartVelocity) { mode = AscentMode.INITIATE_TURN; pitchStartTime = autopilot.MET; return; } double dv = pitchStartVelocity - vesselState.surfaceVelocity.magnitude; status = Localizer.Format("#MechJeb_Ascent_status13", String.Format("{0:F2}", dv));//Vertical ascent <<1>>m/s to go } }
void DriveGravityTurn(FlightCtrlState s) { //stop the intermediate "burn" when our apoapsis reaches the desired altitude if (orbit.ApA > autopilot.desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } if (fixedTimeToAp() < holdAPTime && maxholdAPTime > holdAPTime) { mode = AscentMode.HOLD_AP; return; } maxholdAPTime = Math.Max(maxholdAPTime, fixedTimeToAp()); // fade pitch from AoA at 90% of intermediateAltitude to 0 at 95% of intermediateAltitude double pitchfade = MuUtils.Clamp(- 20 * vesselState.altitudeASL / intermediateAltitude + 19, 0.0, 1.0); // srfvelPitch == zero AoA attitudeTo(srfvelPitch() * pitchfade); if (autopilot.autoThrottle) { core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, intermediateAltitude + mainBody.Radius); if (core.thrust.targetThrottle < 1.0F) { status = "Fine tuning intermediate altitude"; return; } } status = "Gravity turn"; }
//programmatic interface to the module. public void launchTo(double orbitAltitude, double orbitInclination) { this.enabled = true; core.controlClaim(this); desiredOrbitAltitude = orbitAltitude; desiredInclination = orbitInclination; //lift off if we haven't yet: if (Staging.CurrentStage == Staging.StageCount) { Staging.ActivateNextStage(); } mode = AscentMode.VERTICAL_ASCENT; }
void DriveVerticalAscent(FlightCtrlState s) { if (vesselState.altitudeASL > ascentPath.VerticalAscentEnd()) mode = AscentMode.GRAVITY_TURN; if (autoThrottle && orbit.ApA > desiredOrbitAltitude) mode = AscentMode.COAST_TO_APOAPSIS; //during the vertical ascent we just thrust straight up at max throttle core.attitude.attitudeTo(Vector3d.up, AttitudeReference.SURFACE_NORTH, this); if (autoThrottle) core.thrust.targetThrottle = 1.0F; if (!vessel.LiftedOff()) status = "Awaiting liftoff"; else status = "Vertical ascent"; }
void DriveGravityTurn(FlightCtrlState s) { //stop the gravity turn when our apoapsis reaches the desired altitude if (autoThrottle && orbit.ApA > desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } //if we've fallen below the turn start altitude, go back to vertical ascent if (vesselState.altitudeASL < ascentPath.VerticalAscentEnd()) { mode = AscentMode.VERTICAL_ASCENT; return; } if (autoThrottle) { core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, desiredOrbitAltitude + mainBody.Radius); if (core.thrust.targetThrottle < 1.0F) { //when we are bringing down the throttle to make the apoapsis accurate, we're liable to point in weird //directions because thrust goes down and so "difficulty" goes up. so just burn prograde core.attitude.attitudeTo(Vector3d.forward, AttitudeReference.ORBIT, this); status = "Fine tuning apoapsis"; return; } } //transition gradually from the rotating to the non-rotating reference frame. this calculation ensures that //when our maximum possible apoapsis, given our orbital energy, is desiredOrbitalRadius, then we are //fully in the non-rotating reference frame and thus doing the correct calculations to get the right inclination double GM = mainBody.gravParameter; double potentialDifferenceWithApoapsis = GM / vesselState.radius - GM / (mainBody.Radius + desiredOrbitAltitude); double verticalSpeedForDesiredApoapsis = Math.Sqrt(2 * potentialDifferenceWithApoapsis); double referenceFrameBlend = Mathf.Clamp((float)(vesselState.speedOrbital / verticalSpeedForDesiredApoapsis), 0.0F, 1.0F); Vector3d actualVelocityUnit = ((1 - referenceFrameBlend) * vessel.srf_velocity.normalized + referenceFrameBlend * vessel.obt_velocity.normalized).normalized; double desiredHeading = Math.PI / 180 * OrbitalManeuverCalculator.HeadingForInclination(desiredInclination, vesselState.latitude); Vector3d desiredHeadingVector = Math.Sin(desiredHeading) * vesselState.east + Math.Cos(desiredHeading) * vesselState.north; double desiredFlightPathAngle = ascentPath.FlightPathAngle(vesselState.altitudeASL); Vector3d desiredVelocityUnit = Math.Cos(desiredFlightPathAngle * Math.PI / 180) * desiredHeadingVector + Math.Sin(desiredFlightPathAngle * Math.PI / 180) * vesselState.up; Vector3d desiredThrustVector = desiredVelocityUnit; if (correctiveSteering) { Vector3d velocityError = (desiredVelocityUnit - actualVelocityUnit); const double Kp = 5.0; //control gain //"difficulty" scales the controller gain to account for the difficulty of changing a large velocity vector given our current thrust double difficulty = vessel.srf_velocity.magnitude / (50 + 10 * vesselState.ThrustAccel(core.thrust.targetThrottle)); if (difficulty > 5) difficulty = 5; if (vesselState.limitedMaxThrustAccel == 0) difficulty = 1.0; //so we don't freak out over having no thrust between stages Vector3d steerOffset = Kp * difficulty * velocityError; //limit the amount of steering to 10 degrees. Furthemore, never steer to a FPA of > 90 (that is, never lean backward) double maxOffset = 10 * Math.PI / 180; if (desiredFlightPathAngle > 80) maxOffset = (90 - desiredFlightPathAngle) * Math.PI / 180; if (steerOffset.magnitude > maxOffset) steerOffset = maxOffset * steerOffset.normalized; desiredThrustVector += steerOffset; } desiredThrustVector = desiredThrustVector.normalized; core.attitude.attitudeTo(desiredThrustVector, AttitudeReference.INERTIAL, this); status = "Gravity turn"; }
public override void OnModuleEnabled() { if (autodeploySolarPanels && mainBody.atmosphere) mode = AscentMode.RETRACT_SOLAR_PANELS; else mode = AscentMode.VERTICAL_ASCENT; placedCircularizeNode = false; core.attitude.users.Add(this); core.thrust.users.Add(this); if (autostage) core.staging.users.Add(this); }
public override void OnModuleEnabled() { mode = AscentMode.VERTICAL_ASCENT; placedCircularizeNode = false; core.attitude.users.Add(this); core.thrust.users.Add(this); if (autostage) core.staging.users.Add(this); }
void DriveCoastToApoapsis(FlightCtrlState s) { core.thrust.targetThrottle = 0; double circularSpeed = OrbitalManeuverCalculator.CircularOrbitSpeed(mainBody, orbit.ApR); double apoapsisSpeed = orbit.SwappedOrbitalVelocityAtUT(orbit.NextApoapsisTime(vesselState.time)).magnitude; double circularizeBurnTime = (circularSpeed - apoapsisSpeed) / vesselState.limitedMaxThrustAccel; //Once we get above the atmosphere, plan and execute the circularization maneuver. //For orbits near the edge of the atmosphere, we can't wait until we break the atmosphere //to start the burn, so we also compare the timeToAp with the expected circularization burn time. //if ((vesselState.altitudeASL > mainBody.RealMaxAtmosphereAltitude()) // || (vesselState.limitedMaxThrustAccel > 0 && orbit.timeToAp < circularizeBurnTime / 1.8)) // Sarbian : removed the special case for now. Some ship where turning while still in atmosphere if (vesselState.altitudeASL > mainBody.RealMaxAtmosphereAltitude()) { if (autodeploySolarPanels) core.solarpanel.ExtendAll(); mode = AscentMode.CIRCULARIZE; core.warp.MinimumWarp(); return; } //if our apoapsis has fallen too far, resume the gravity turn if (orbit.ApA < desiredOrbitAltitude - 1000.0) { mode = AscentMode.GRAVITY_TURN; core.warp.MinimumWarp(); return; } //point prograde and thrust gently if our apoapsis falls below the target //core.attitude.attitudeTo(Vector3d.forward, AttitudeReference.ORBIT, this); // Actually I have a better idea: Don't initiate orientation changes when there's a chance that our main engine // might reignite. There won't be enough control authority to counteract that much momentum change. // - Starwaster core.thrust.targetThrottle = 0; double desiredHeading = Math.PI / 180 * OrbitalManeuverCalculator.HeadingForInclination(desiredInclination, vesselState.latitude); Vector3d desiredHeadingVector = Math.Sin(desiredHeading) * vesselState.east + Math.Cos(desiredHeading) * vesselState.north; double desiredFlightPathAngle = ascentPath.FlightPathAngle(vesselState.altitudeASL, vesselState.speedSurface); Vector3d desiredThrustVector = Math.Cos(desiredFlightPathAngle * Math.PI / 180) * desiredHeadingVector + Math.Sin(desiredFlightPathAngle * Math.PI / 180) * vesselState.up; core.attitude.attitudeTo(desiredThrustVector.normalized, AttitudeReference.INERTIAL, this); if (autoThrottle && orbit.ApA < desiredOrbitAltitude) { core.attitude.attitudeTo(Vector3d.forward, AttitudeReference.INERTIAL, this); core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, desiredOrbitAltitude + mainBody.Radius); } if (core.node.autowarp) { //warp at x2 physical warp: core.warp.WarpPhysicsAtRate(2); } status = "Coasting to edge of atmosphere"; }
void driveRendezvousStuff(FlightCtrlState s) { if (mode != AscentMode.DISENGAGED && mode != AscentMode.ON_PAD) { totalDVExpended += vesselState.deltaT * vesselState.thrustAccel(s.mainThrottle); gravityLosses += vesselState.deltaT * Vector3d.Dot(-vesselState.velocityVesselSurfaceUnit, vesselState.gravityForce); gravityLosses -= vesselState.deltaT * Vector3d.Dot(vesselState.velocityVesselSurfaceUnit, vesselState.up * vesselState.radius * Math.Pow(1 / part.vessel.mainBody.rotationPeriod, 2)); steeringLosses += vesselState.deltaT * vesselState.thrustAccel(s.mainThrottle) * (1 - Vector3.Dot(vesselState.velocityVesselSurfaceUnit, vesselState.forward)); dragLosses += vesselState.deltaT * ARUtils.computeDragAccel(vesselState.CoM, vesselState.velocityVesselOrbit, vesselState.massDrag / vesselState.mass, part.vessel.mainBody).magnitude; double netVelocity = totalDVExpended - gravityLosses - steeringLosses - dragLosses; double targetCircularPeriod = 2 * Math.PI * Math.Sqrt(Math.Pow(part.vessel.mainBody.Radius + desiredOrbitAltitude, 3) / part.vessel.mainBody.gravParameter); double longitudeTraversed = (vesselState.longitude - launchLongitude) + 360 * (vesselState.time - launchTime) / part.vessel.mainBody.rotationPeriod; launchPhaseAngle = ARUtils.clampDegrees(360 * (vesselState.time - launchTime) / targetCircularPeriod - longitudeTraversed); } if (mode == AscentMode.ON_PAD && timeIgnitionForRendezvous && rendezvousTarget != null) { double phaseAngle = ARUtils.clampDegrees(vesselState.longitude - part.vessel.mainBody.GetLongitude(rendezvousTarget.transform.position)); double[] warpLookaheadTimes = new double[] { 10, 15, 20, 25, 50, 100, 1000, 10000 }; rendezvousIgnitionCountdown = ARUtils.clampDegrees(phaseAngle - predictedLaunchPhaseAngle) / 360 * rendezvousTarget.orbit.period; if (rendezvousIgnitionCountdown < 0.0 && rendezvousIgnitionCountdown > -1.0) { mode = AscentMode.VERTICAL_ASCENT; Staging.ActivateNextStage(); } else { if (rendezvousIgnitionCountdown < 0) { rendezvousIgnitionCountdown = rendezvousTarget.orbit.period / 2; rendezvousIgnitionCountdownHolding = true; } else { rendezvousIgnitionCountdownHolding = false; } core.warpTo(this, rendezvousIgnitionCountdown, warpLookaheadTimes); } } }
void driveCoastToApoapsis(FlightCtrlState s) { s.mainThrottle = 0.0F; if (Vector3d.Dot(vesselState.velocityVesselOrbit, vesselState.up) < 0) //if we have started to descend, i.e. reached apoapsis, circularize { mode = AscentMode.CIRCULARIZE; if (TimeWarp.CurrentRateIndex != 0) TimeWarp.SetRate(0, false); return; } if (part.vessel.orbit.ApA < desiredOrbitAltitude - 1000.0) { mode = AscentMode.GRAVITY_TURN; core.attitudeTo(Vector3.forward, MechJebCore.AttitudeReference.ORBIT, this); return; } //if we are running physics, we can do burns if the apoapsis falls, and we can maintain our orientation in preparation for circularization //if our Ap is too low and we are pointing reasonably along our velocity double[] lookaheadTimes = new double[] { 0, 32.5, 35, 50, 75, 500, 10000, 100000 }; if (part.vessel.orbit.ApA < desiredOrbitAltitude - 10) { if ((TimeWarp.WarpMode == TimeWarp.Modes.LOW) || (TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate)) { //lastAccelerationTime = vesselState.time; core.attitudeTo(Vector3.forward, MechJebCore.AttitudeReference.ORBIT, this); if (Vector3d.Dot(vesselState.forward, vesselState.velocityVesselOrbitUnit) > 0.75) { s.mainThrottle = throttleToRaiseApoapsis(part.vessel.orbit.ApR, desiredOrbitAltitude + part.vessel.mainBody.Radius); } else { s.mainThrottle = 0.0F; } return; } else { core.warpPhysics(this); } } else if (autoWarpToApoapsis) { //if we're allowed to autowarp, do so core.warpTo(this, part.vessel.orbit.timeToAp, lookaheadTimes); } //if we are out of the atmosphere and have a high enough apoapsis and aren't near apoapsis, play dead to //prevent acceleration from interfering with time warp. otherwise point in the direction that we will //want to point at the start of the circularization burn if (autoWarpToApoapsis && vesselState.altitudeASL > part.vessel.mainBody.maxAtmosphereAltitude && part.vessel.orbit.timeToAp > lookaheadTimes[2] && part.vessel.orbit.ApA > desiredOrbitAltitude - 10) { //don't steer core.attitudeDeactivate(this); } else { double expectedApoapsisThrottle = circularizationBurnThrottle(expectedSpeedAtApoapsis(), circularSpeedAtRadius(part.vessel.orbit.ApR)); double desiredThrustPitch = thrustPitchForZeroVerticalAcc(part.vessel.orbit.ApR, expectedSpeedAtApoapsis(), vesselState.thrustAccel(expectedApoapsisThrottle), 0); Vector3d groundHeading = Vector3d.Exclude(vesselState.up, part.vessel.orbit.GetVel()).normalized; Vector3d desiredThrustVector = (Math.Cos(desiredThrustPitch) * groundHeading + Math.Sin(desiredThrustPitch) * vesselState.up); core.attitudeTo(desiredThrustVector, MechJebCore.AttitudeReference.INERTIAL, this); return; } }
void driveGravityTurn(FlightCtrlState s) { //stop the gravity turn when our apoapsis reaches the desired altitude if (seizeThrottle && part.vessel.orbit.ApA > desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; //lastAccelerationTime = vesselState.time; core.attitudeTo(Vector3d.forward, MechJebCore.AttitudeReference.ORBIT, this); return; } if (vesselState.altitudeASL < gravityTurnStartAltitude) { mode = AscentMode.VERTICAL_ASCENT; core.attitudeTo(Vector3d.up, MechJebCore.AttitudeReference.SURFACE_NORTH, this); return; } if (seizeThrottle) { float gentleThrottle = throttleToRaiseApoapsis(part.vessel.orbit.ApR, desiredOrbitAltitude + part.vessel.mainBody.Radius); if (gentleThrottle < 1.0F) { //when we are bringing down the throttle to make the apoapsis accurate, we're liable to point in weird //directions because thrust goes down and so "difficulty" goes up. so just burn prograde s.mainThrottle = gentleThrottle; core.attitudeTo(Vector3d.forward, MechJebCore.AttitudeReference.ORBIT, this); return; } else { s.mainThrottle = efficientThrottle(); } } //transition gradually from the rotating to the non-rotating reference frame. this calculation ensures that //when our maximum possible apoapsis, given our orbital energy, is desiredOrbitalRadius, then we are //fully in the non-rotating reference frame and thus doing the correct calculations to get the right inclination double GM = vesselState.localg * vesselState.radius * vesselState.radius; double potentialDifferenceWithApoapsis = GM / vesselState.radius - GM / (part.vessel.mainBody.Radius + desiredOrbitAltitude); double verticalSpeedForDesiredApoapsis = Math.Sqrt(2 * potentialDifferenceWithApoapsis); //double referenceFrameBlend = Mathf.Clamp((float)(1.5 * vesselState.speedOrbital / verticalSpeedForDesiredApoapsis - 0.5), 0.0F, 1.0F); double referenceFrameBlend = Mathf.Clamp((float)(vesselState.speedOrbital / verticalSpeedForDesiredApoapsis), 0.0F, 1.0F); Vector3d actualVelocityUnit = ((1 - referenceFrameBlend) * vesselState.velocityVesselSurfaceUnit + referenceFrameBlend * vesselState.velocityVesselOrbitUnit).normalized; //minus sign is there because somewhere a sign got flipped in integrating with MechJeb double surfaceAngle = -desiredSurfaceAngle(vesselState.latitude * Math.PI / 180.0, referenceFrameBlend); Vector3d desiredSurfaceHeading = Math.Cos(surfaceAngle) * vesselState.east + Math.Sin(surfaceAngle) * vesselState.north; double desPitch = desiredPitch(vesselState.altitudeASL); //double desPitch = 90; Vector3d desiredVelocityUnit = Math.Cos(desPitch * Math.PI / 180) * desiredSurfaceHeading + Math.Sin(desPitch * Math.PI / 180) * vesselState.up; Vector3d velocityError = (desiredVelocityUnit - actualVelocityUnit); //"difficulty" accounts for the difficulty of changing a large velocity vector given our current thrust double difficulty = vesselState.velocityVesselSurface.magnitude / (50 + 10 * vesselState.thrustAccel(s.mainThrottle)); if (difficulty > 5) difficulty = 5; if (vesselState.maxThrustAccel == 0) difficulty = 1.0; //so we don't freak out over having no thrust between stages Vector3d desiredThrustVector; desiredThrustVector = desiredVelocityUnit; Vector3d steerOffset = velocityKP * difficulty * velocityError; double maxOffset = 10 * Math.PI / 180; if (desPitch > 80) maxOffset = (90 - desPitch) * Math.PI / 180; if (steerOffset.magnitude > maxOffset) steerOffset = maxOffset * steerOffset.normalized; desiredThrustVector += steerOffset; desiredThrustVector = desiredThrustVector.normalized; core.attitudeTo(desiredThrustVector, MechJebCore.AttitudeReference.INERTIAL, this); }
void driveCircularizationBurn(FlightCtrlState s) { if (part.vessel.orbit.ApA < desiredOrbitAltitude - 1000.0) { mode = AscentMode.GRAVITY_TURN; core.attitudeTo(Vector3.forward, MechJebCore.AttitudeReference.ORBIT, this); return; } double orbitalRadius = (vesselState.CoM - part.vessel.mainBody.position).magnitude; double circularSpeed = Math.Sqrt(orbitalRadius * vesselState.localg); //we start the circularization burn at apoapsis, and the orbit is about as circular as it is going to get when the //periapsis has moved closer to us than the apoapsis if (Math.Min(part.vessel.orbit.timeToPe, part.vessel.orbit.period - part.vessel.orbit.timeToPe) < Math.Min(part.vessel.orbit.timeToAp, part.vessel.orbit.period - part.vessel.orbit.timeToAp) || vesselState.speedOrbital > circularSpeed + 1.0) //makes sure we don't keep burning forever if we are somehow way off course { predictedLaunchPhaseAngle = launchPhaseAngle; predictedLaunchPhaseAngleString = String.Format("{0:00}", predictedLaunchPhaseAngle); mecoTime = vesselState.time; s.mainThrottle = 0.0F; turnOffSteering(); core.controlRelease(this); //if (!showStats) enabled = false; return; } //During circularization we think in the *non-rotating* frame, but allow ourselves to discuss centrifugal acceleration //as the tendency for the altitude to rise because of our large horizontal velocity. //To get a circular orbit we need to reach orbital horizontal speed while simultaneously getting zero vertical speed. //We compute the thrust vector needed to get zero vertical acceleration, taking into account gravity and centrifugal //acceleration. If we currently have a nonzero vertical velocity we then adjust the thrust vector to bring that to zero s.mainThrottle = (float)circularizationBurnThrottle(vesselState.speedOrbital, circularSpeed); double thrustAcceleration = vesselState.thrustAccel(s.mainThrottle); Vector3d groundHeading = Vector3d.Exclude(vesselState.up, vesselState.velocityVesselOrbit).normalized; double horizontalSpeed = Vector3d.Dot(groundHeading, vesselState.velocityVesselOrbit); double verticalSpeed = Vector3d.Dot(vesselState.up, vesselState.velocityVesselOrbit); //test the following modification: pitch = 0 if throttle < 1 double desiredThrustPitch = thrustPitchForZeroVerticalAcc(orbitalRadius, horizontalSpeed, thrustAcceleration, verticalSpeed); //Vector3d desiredThrustVector = Math.Cos(desiredThrustPitch) * groundHeading + Math.Sin(desiredThrustPitch) * vesselState.up; Vector3d desiredThrustVector = Math.Cos(desiredThrustPitch) * Vector3d.forward + Math.Sin(desiredThrustPitch) * Vector3d.up; core.attitudeTo(desiredThrustVector, MechJebCore.AttitudeReference.ORBIT_HORIZONTAL, this); }
protected override void WindowGUI(int windowID) { GUILayout.BeginVertical(); GUILayout.BeginHorizontal(); GUIStyle s = new GUIStyle(GUI.skin.button); if (mode == AscentMode.DISENGAGED) { s.normal.textColor = Color.green; if (GUILayout.Button("Engage", s)) { initializeInternals(); core.controlClaim(this); if (autoStage) { core.autoStageActivate(this, autoStageDelay, autoStageLimit); } mode = AscentMode.ON_PAD; } } else { s.normal.textColor = Color.red; if (GUILayout.Button("Disengage", s)) { core.controlRelease(this); //enabled = false; } } if (minimized) { if (GUILayout.Button("Maximize")) { minimized = false; } } else { if (GUILayout.Button("Minimize")) { minimized = true; } } showHelpWindow = GUILayout.Toggle(showHelpWindow, "?", new GUIStyle(GUI.skin.button)); GUILayout.EndHorizontal(); if (!minimized) { if (seizeThrottle) { desiredOrbitAltitude = ARUtils.doGUITextInput("Orbit altitude: ", 100.0F, desiredOrbitAltitudeKmString, 30.0F, "km", 30.0F, out desiredOrbitAltitudeKmString, desiredOrbitAltitude, 1000.0); } else { GUILayout.Label(String.Format("Apoapsis: {0:0.0} km", part.vessel.orbit.ApA / 1000.0)); if (part.vessel.orbit.PeA > 0) { GUILayout.Label(String.Format("Periapsis: {0:0.0} km", part.vessel.orbit.PeA / 1000.0)); } else { GUILayout.Label("Periapsis: <0 km"); } if (GUILayout.Button("Circularize")) { mode = AscentMode.COAST_TO_APOAPSIS; desiredOrbitAltitude = part.vessel.orbit.ApA; } } GUIStyle angleStyle = new GUIStyle(GUI.skin.button); angleStyle.padding.top = angleStyle.padding.bottom = 3; if (headingNotInc) { GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true)); if (GUILayout.Button("Heading:", angleStyle, GUILayout.Width(100.0F))) { headingNotInc = false; } double parsedHeading; if (Double.TryParse(launchHeadingString, out parsedHeading)) { if (parsedHeading != launchHeading) { launchHeading = parsedHeading; double launchSurfaceAngle = ARUtils.clampDegrees(90 - launchHeading); desiredInclination = 180 / Math.PI * Math.Sign(launchSurfaceAngle) * Math.Acos(Math.Cos(launchSurfaceAngle * Math.PI / 180) * Math.Cos(vesselState.latitude * Math.PI / 180)); desiredInclinationString = String.Format("{0:0}", desiredInclination); } } launchHeadingString = GUILayout.TextField(launchHeadingString, GUILayout.MinWidth(30.0F)); GUILayout.Label("°", GUILayout.Width(30.0F)); GUILayout.EndHorizontal(); } else { GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true)); double parsedInc; if (Double.TryParse(desiredInclinationString, out parsedInc)) desiredInclination = parsedInc; if (GUILayout.Button("Inclination:", angleStyle, GUILayout.Width(100.0F))) { headingNotInc = true; double cosSurfaceAngle = Math.Cos(desiredInclination * Math.PI / 180) / Math.Cos(vesselState.latitude * Math.PI / 180); cosSurfaceAngle = Mathf.Clamp((float)cosSurfaceAngle, -1.0F, 1.0F); double surfaceAngle = 180 / Math.PI * Math.Sign(desiredInclination) * Math.Acos(cosSurfaceAngle); launchHeading = (360 + 90 - surfaceAngle) % 360; launchHeadingString = String.Format("{0:0}", launchHeading); } desiredInclinationString = GUILayout.TextField(desiredInclinationString, GUILayout.MinWidth(30.0F)); GUILayout.Label("°", GUILayout.Width(30.0F)); GUILayout.EndHorizontal(); //desiredInclination = ARUtils.doGUITextInput("Orbit inclination: ", 100.0F, desiredInclinationString, 30.0F, "°", 30.0F, // out desiredInclinationString, desiredInclination); } seizeThrottle = GUILayout.Toggle(seizeThrottle, "Auto-throttle?"); autoStage = GUILayout.Toggle(autoStage, "Auto-stage?"); if (autoStage) { autoStageDelay = ARUtils.doGUITextInput("Stage delay: ", 100.0F, autoStageDelayString, 30.0F, "s", 30.0F, out autoStageDelayString, autoStageDelay); autoStageLimit = ARUtils.doGUITextInput("Stop at stage #", 100.0F, autoStageLimitString, 30.0F, "", 30.0F, out autoStageLimitString, autoStageLimit); } bool newAutoWarp = GUILayout.Toggle(autoWarpToApoapsis, "Auto-warp toward apoapsis?", new GUIStyle(GUI.skin.toggle)); if (autoWarpToApoapsis && !newAutoWarp && TimeWarp.CurrentRateIndex != 0) TimeWarp.SetRate(0, true); autoWarpToApoapsis = newAutoWarp; GUILayout.BeginHorizontal(); GUIStyle stateStyle = new GUIStyle(GUI.skin.label); stateStyle.normal.textColor = Color.green; if (mode == AscentMode.DISENGAGED) stateStyle.normal.textColor = Color.red; GUILayout.Label("State: ", GUILayout.Width(60)); GUILayout.Label(modeStrings[(int)mode], stateStyle); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); showPathWindow = GUILayout.Toggle(showPathWindow, "Edit path", new GUIStyle(GUI.skin.button)); showStats = GUILayout.Toggle(showStats, "Stats", new GUIStyle(GUI.skin.button)); GUILayout.EndHorizontal(); if (part.vessel.Landed) { timeIgnitionForRendezvous = GUILayout.Toggle(timeIgnitionForRendezvous, "Time launch to rendezvous?"); if (timeIgnitionForRendezvous) { predictedLaunchPhaseAngle = ARUtils.doGUITextInput("Known LPA:", 100.0F, predictedLaunchPhaseAngleString, 30.0F, "°", 30.0F, out predictedLaunchPhaseAngleString, predictedLaunchPhaseAngle); GUILayout.Label("Target vessel: " + (rendezvousTarget == null ? "none" : rendezvousTarget.vesselName)); choosingRendezvousTarget = GUILayout.Toggle(choosingRendezvousTarget, "Choose target", new GUIStyle(GUI.skin.button)); if (choosingRendezvousTarget) { rendezvousTargetScrollPos = GUILayout.BeginScrollView(rendezvousTargetScrollPos, GUILayout.Height(200)); GUILayout.BeginVertical(); foreach (Vessel v in FlightGlobals.Vessels) { if (v != part.vessel && !v.Landed && !v.Splashed && v.mainBody == part.vessel.mainBody) { if (GUILayout.Button(v.vesselName + String.Format(" @ {0:0}km", part.vessel.mainBody.GetAltitude(v.transform.position) / 1000.0))) { rendezvousTarget = v; choosingRendezvousTarget = false; } } } GUILayout.EndVertical(); GUILayout.EndScrollView(); } if (rendezvousTarget != null) { if (mode == AscentMode.DISENGAGED) { GUILayout.Label("Engage to begin countdown"); } else if (mode == AscentMode.ON_PAD) { GUILayout.Label(String.Format("T-{0:0} seconds and " + (rendezvousIgnitionCountdownHolding ? "holding" : "counting"), Math.Ceiling(rendezvousIgnitionCountdown))); } } } } } GUILayout.EndVertical(); GUI.DragWindow(); //make window draggable }
//programmatic interface to the module. public void launchTo(double orbitAltitude, double orbitInclination) { this.enabled = true; core.controlClaim(this); desiredOrbitAltitude = orbitAltitude; desiredInclination = orbitInclination; desiredOrbitAltitudeKmString = (desiredOrbitAltitude / 1000.0).ToString(); desiredInclinationString = desiredInclination.ToString(); //currently this only gets called from lua scripts, and the scripter probably //wants a fully automatic launch, so turn on all the auto stuff autoStage = true; seizeThrottle = true; autoWarpToApoapsis = true; //lift off if we haven't yet: core.launch(this); mode = AscentMode.VERTICAL_ASCENT; }
/////////////////////////////////////// // FLYING THE ROCKET ////////////////// /////////////////////////////////////// //called every frame or so; this is where we manipulate the flight controls public override void drive(FlightCtrlState s) { if (mode == AscentMode.DISENGAGED) return; if (mode == AscentMode.ON_PAD && Staging.CurrentStage <= Staging.LastStage) mode = AscentMode.VERTICAL_ASCENT; driveAutoStaging(); //given the mode, determine what direction the rocket should be pointing switch (mode) { case AscentMode.VERTICAL_ASCENT: driveVerticalAscent(s); break; case AscentMode.GRAVITY_TURN: driveGravityTurn(s); break; case AscentMode.COAST_TO_APOAPSIS: driveCoastToApoapsis(s); break; case AscentMode.CIRCULARIZE: driveCircularizationBurn(s); break; } driveLimitOverheat(s); driveRendezvousStuff(s); }
void DriveVerticalAscent(FlightCtrlState s) { if (timedLaunch) { status = "Awaiting liftoff"; core.attitude.AxisControl(false, false, false); return; } if (!ascentPath.IsVerticalAscent(vesselState.altitudeASL, vesselState.speedSurface)) mode = AscentMode.GRAVITY_TURN; if (autoThrottle && orbit.ApA > desiredOrbitAltitude) mode = AscentMode.COAST_TO_APOAPSIS; //during the vertical ascent we just thrust straight up at max throttle if (forceRoll) { // pre-align roll unless correctiveSteering is active as it would just interfere with that double desiredHeading = OrbitalManeuverCalculator.HeadingForLaunchInclination(vessel.mainBody, desiredInclination, launchLatitude, OrbitalManeuverCalculator.CircularOrbitSpeed(vessel.mainBody, desiredOrbitAltitude + mainBody.Radius)); core.attitude.attitudeTo(desiredHeading, 90, verticalRoll, this); } else { core.attitude.attitudeTo(Vector3d.up, AttitudeReference.SURFACE_NORTH, this); } core.attitude.AxisControl(!vessel.Landed, !vessel.Landed, !vessel.Landed && vesselState.altitudeBottom > 50); if (autoThrottle) core.thrust.targetThrottle = 1.0F; if (!vessel.LiftedOff() || vessel.Landed) status = "Awaiting liftoff"; else status = "Vertical ascent"; }
void DriveRetractSolarPanels(FlightCtrlState s) { if (autoThrottle) core.thrust.targetThrottle = 0.0f; if (timedLaunch && tMinus > 10.0) { status = "Awaiting liftoff"; return; } status = "Retracting solar panels"; core.solarpanel.RetractAll(); if (core.solarpanel.AllRetracted()) mode = AscentMode.VERTICAL_ASCENT; }
void turnOffSteering() { if (mode != AscentMode.DISENGAGED) { if (autoStage) { core.autoStageDeactivate(this); } if (mode != AscentMode.ON_PAD) { FlightInputHandler.SetNeutralControls(); //makes sure we leave throttle at zero } mode = AscentMode.DISENGAGED; core.attitudeDeactivate(this); } }
/////////////////////////////////////// // FLYING THE ROCKET ////////////////// /////////////////////////////////////// //called every frame or so; this is where we manipulate the flight controls public override void drive(FlightCtrlState s) { if (mode == AscentMode.DISENGAGED) return; if (mode == AscentMode.ON_PAD && Staging.CurrentStage <= Staging.lastStage) mode = AscentMode.VERTICAL_ASCENT; switch (mode) { case AscentMode.VERTICAL_ASCENT: driveVerticalAscent(s); break; case AscentMode.GRAVITY_TURN: driveGravityTurn(s); break; case AscentMode.COAST_TO_APOAPSIS: driveCoastToApoapsis(s); break; case AscentMode.CIRCULARIZE: driveCircularizationBurn(s); break; } driveLimitOverheat(s); driveRendezvousStuff(s); driveUpdateStats(s); }
void DriveVerticalAscent(FlightCtrlState s) { if (timedLaunch) { status = "Awaiting liftoff"; return; } if (!ascentPath.IsVerticalAscent(vesselState.altitudeASL, vesselState.speedSurface)) mode = AscentMode.GRAVITY_TURN; if (autoThrottle && orbit.ApA > desiredOrbitAltitude) mode = AscentMode.COAST_TO_APOAPSIS; //during the vertical ascent we just thrust straight up at max throttle if (forceRoll && vesselState.altitudeTrue > 50) { // pre-align roll unless correctiveSteering is active as it would just interfere with that core.attitude.attitudeTo(90 - desiredInclination, 90, verticalRoll, this); } else { core.attitude.attitudeTo(Vector3d.up, AttitudeReference.SURFACE_NORTH, this); } if (autoThrottle) core.thrust.targetThrottle = 1.0F; if (!vessel.LiftedOff()) status = "Awaiting liftoff"; else status = "Vertical ascent"; }
void driveRendezvousStuff(FlightCtrlState s) { if (mode == AscentMode.ON_PAD && timeIgnitionForRendezvous && rendezvousTarget != null) { double phaseAngle = ARUtils.clampDegrees(vesselState.longitude - part.vessel.mainBody.GetLongitude(rendezvousTarget.transform.position)); double[] warpLookaheadTimes = new double[] { 10, 12.5, 15, 25, 50, 500, 10000, 100000 }; double angleDifference = ARUtils.clampDegrees(phaseAngle - predictedLaunchPhaseAngle); double angleRate = 360.0 / rendezvousTarget.orbit.period - 360 / part.vessel.mainBody.rotationPeriod; rendezvousIgnitionCountdown = angleDifference / angleRate; if (rendezvousIgnitionCountdown < 0.0 && rendezvousIgnitionCountdown > -1.0) { mode = AscentMode.VERTICAL_ASCENT; Staging.ActivateNextStage(); } else { if (rendezvousIgnitionCountdown < 0) { rendezvousIgnitionCountdown = rendezvousTarget.orbit.period / 2; rendezvousIgnitionCountdownHolding = true; } else { rendezvousIgnitionCountdownHolding = false; } core.warpTo(this, rendezvousIgnitionCountdown, warpLookaheadTimes); } } }
void DriveGravityTurn(FlightCtrlState s) { //stop the gravity turn when our apoapsis reaches the desired altitude if (autoThrottle && orbit.ApA > desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } //if we've fallen below the turn start altitude, go back to vertical ascent if (ascentPath.IsVerticalAscent(vesselState.altitudeASL, vesselState.speedSurface)) { mode = AscentMode.VERTICAL_ASCENT; return; } if (autoThrottle) { core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, desiredOrbitAltitude + mainBody.Radius); if (core.thrust.targetThrottle < 1.0F) { //when we are bringing down the throttle to make the apoapsis accurate, we're liable to point in weird //directions because thrust goes down and so "difficulty" goes up. so just burn prograde core.attitude.attitudeTo(Vector3d.forward, AttitudeReference.ORBIT, this); status = "Fine tuning apoapsis"; return; } } //transition gradually from the rotating to the non-rotating reference frame. this calculation ensures that //when our maximum possible apoapsis, given our orbital energy, is desiredOrbitalRadius, then we are //fully in the non-rotating reference frame and thus doing the correct calculations to get the right inclination double GM = mainBody.gravParameter; double potentialDifferenceWithApoapsis = GM / vesselState.radius - GM / (mainBody.Radius + desiredOrbitAltitude); double verticalSpeedForDesiredApoapsis = Math.Sqrt(2 * potentialDifferenceWithApoapsis); double referenceFrameBlend = Mathf.Clamp((float)(vesselState.speedOrbital / verticalSpeedForDesiredApoapsis), 0.0F, 1.0F); Vector3d actualVelocityUnit = ((1 - referenceFrameBlend) * vesselState.surfaceVelocity.normalized + referenceFrameBlend * vesselState.orbitalVelocity.normalized).normalized; double desiredHeading = MathExtensions.Deg2Rad * OrbitalManeuverCalculator.HeadingForLaunchInclination(vessel.mainBody, desiredInclination, launchLatitude, OrbitalManeuverCalculator.CircularOrbitSpeed(vessel.mainBody, desiredOrbitAltitude + mainBody.Radius)); Vector3d desiredHeadingVector = Math.Sin(desiredHeading) * vesselState.east + Math.Cos(desiredHeading) * vesselState.north; double desiredFlightPathAngle = ascentPath.FlightPathAngle(vesselState.altitudeASL, vesselState.speedSurface); Vector3d desiredVelocityUnit = Math.Cos(desiredFlightPathAngle * Math.PI / 180) * desiredHeadingVector + Math.Sin(desiredFlightPathAngle * Math.PI / 180) * vesselState.up; Vector3d desiredThrustVector = desiredVelocityUnit; if (correctiveSteering) { Vector3d velocityError = (desiredVelocityUnit - actualVelocityUnit); const double Kp = 5.0; //control gain //"difficulty" scales the controller gain to account for the difficulty of changing a large velocity vector given our current thrust double difficulty = vesselState.surfaceVelocity.magnitude / (50 + 10 * vesselState.ThrustAccel(core.thrust.targetThrottle)); if (difficulty > 5) difficulty = 5; if (vesselState.limitedMaxThrustAccel == 0) difficulty = 1.0; //so we don't freak out over having no thrust between stages Vector3d steerOffset = Kp * difficulty * velocityError; //limit the amount of steering to 10 degrees. Furthermore, never steer to a FPA of > 90 (that is, never lean backward) double maxOffset = 10 * Math.PI / 180; if (desiredFlightPathAngle > 80) maxOffset = (90 - desiredFlightPathAngle) * Math.PI / 180; if (steerOffset.magnitude > maxOffset) steerOffset = maxOffset * steerOffset.normalized; desiredThrustVector += steerOffset; } desiredThrustVector = desiredThrustVector.normalized; if (limitAoA) { float fade = vesselState.dynamicPressure < aoALimitFadeoutPressure ? (float)(aoALimitFadeoutPressure / vesselState.dynamicPressure) : 1; currentMaxAoA = Math.Min(fade * maxAoA, 180d); limitingAoA = vessel.altitude < mainBody.atmosphereDepth && Vector3.Angle(vesselState.surfaceVelocity, desiredThrustVector) > currentMaxAoA; if (limitingAoA) { desiredThrustVector = Vector3.RotateTowards(vesselState.surfaceVelocity, desiredThrustVector, (float)(currentMaxAoA * Mathf.Deg2Rad), 1).normalized; } } if (forceRoll && Vector3.Angle(vesselState.up, vesselState.forward) > 7 && core.attitude.attitudeError < 5) { var pitch = 90 - Vector3.Angle(vesselState.up, desiredThrustVector); var hdg = core.rover.HeadingToPos(vessel.CoM, vessel.CoM + desiredThrustVector); core.attitude.attitudeTo(hdg, pitch, turnRoll, this); } else { core.attitude.attitudeTo(desiredThrustVector, AttitudeReference.INERTIAL, this); } status = "Gravity turn"; }
void DriveGravityTurn(FlightCtrlState s) { //stop the gravity turn when our apoapsis reaches the desired altitude if (autoThrottle && orbit.ApA > desiredOrbitAltitude) { mode = AscentMode.COAST_TO_APOAPSIS; return; } //if we've fallen below the turn start altitude, go back to vertical ascent if (vesselState.altitudeASL < ascentPath.VerticalAscentEnd()) { mode = AscentMode.VERTICAL_ASCENT; return; } if (autoThrottle) { core.thrust.targetThrottle = ThrottleToRaiseApoapsis(orbit.ApR, desiredOrbitAltitude + mainBody.Radius); if (core.thrust.targetThrottle < 1.0F) { //when we are bringing down the throttle to make the apoapsis accurate, we're liable to point in weird //directions because thrust goes down and so "difficulty" goes up. so just burn prograde core.attitude.attitudeTo(Vector3d.forward, AttitudeReference.ORBIT, this); status = "Fine tuning apoapsis"; return; } } //transition gradually from the rotating to the non-rotating reference frame. this calculation ensures that //when our maximum possible apoapsis, given our orbital energy, is desiredOrbitalRadius, then we are //fully in the non-rotating reference frame and thus doing the correct calculations to get the right inclination double GM = mainBody.gravParameter; double potentialDifferenceWithApoapsis = GM / vesselState.radius - GM / (mainBody.Radius + desiredOrbitAltitude); double verticalSpeedForDesiredApoapsis = Math.Sqrt(2 * potentialDifferenceWithApoapsis); double referenceFrameBlend = Mathf.Clamp((float)(vesselState.speedOrbital / verticalSpeedForDesiredApoapsis), 0.0F, 1.0F); Vector3d actualVelocityUnit = ((1 - referenceFrameBlend) * vesselState.velocityVesselSurfaceUnit + referenceFrameBlend * vesselState.velocityVesselOrbitUnit).normalized; double desiredHeading = Math.PI / 180 * OrbitalManeuverCalculator.HeadingForInclination(desiredInclination, vesselState.latitude); Vector3d desiredHeadingVector = Math.Sin(desiredHeading) * vesselState.east + Math.Cos(desiredHeading) * vesselState.north; double desiredFlightPathAngle = ascentPath.FlightPathAngle(vesselState.altitudeASL); Vector3d desiredVelocityUnit = Math.Cos(desiredFlightPathAngle * Math.PI / 180) * desiredHeadingVector + Math.Sin(desiredFlightPathAngle * Math.PI / 180) * vesselState.up; Vector3d desiredThrustVector = desiredVelocityUnit; if (correctiveSteering) { Vector3d velocityError = (desiredVelocityUnit - actualVelocityUnit); const double Kp = 5.0; //control gain //"difficulty" scales the controller gain to account for the difficulty of changing a large velocity vector given our current thrust double difficulty = vesselState.velocityVesselSurface.magnitude / (50 + 10 * vesselState.ThrustAccel(core.thrust.targetThrottle)); if (difficulty > 5) { difficulty = 5; } if (vesselState.limitedMaxThrustAccel == 0) { difficulty = 1.0; //so we don't freak out over having no thrust between stages } Vector3d steerOffset = Kp * difficulty * velocityError; //limit the amount of steering to 10 degrees. Furthemore, never steer to a FPA of > 90 (that is, never lean backward) double maxOffset = 10 * Math.PI / 180; if (desiredFlightPathAngle > 80) { maxOffset = (90 - desiredFlightPathAngle) * Math.PI / 180; } if (steerOffset.magnitude > maxOffset) { steerOffset = maxOffset * steerOffset.normalized; } desiredThrustVector += steerOffset; } desiredThrustVector = desiredThrustVector.normalized; core.attitude.attitudeTo(desiredThrustVector, AttitudeReference.INERTIAL, this); status = "Gravity turn"; }
void driveVerticalAscent(FlightCtrlState s) { //during the vertical ascent we just thrust straight up at max throttle if (seizeThrottle) s.mainThrottle = efficientThrottle(); if (vesselState.altitudeASL > gravityTurnStartAltitude) mode = AscentMode.GRAVITY_TURN; if (seizeThrottle && part.vessel.orbit.ApA > desiredOrbitAltitude) { //lastAccelerationTime = vesselState.time; mode = AscentMode.COAST_TO_APOAPSIS; } core.attitudeTo(Vector3d.up, MechJebCore.AttitudeReference.SURFACE_NORTH, this); }
/////////////////////////////////////////////// // INITIALIZATION ///////////////////////////// /////////////////////////////////////////////// private void initializeInternals() { mode = AscentMode.DISENGAGED; //lastAccelerationTime = 0; //lastAttemptedWarpIndex = 0; }
void DriveRetractSolarPanels(FlightCtrlState s) { if (autoThrottle) core.thrust.targetThrottle = 0.0f; core.attitude.AxisControl(false, false, false); if (timedLaunch && tMinus > 10.0) { status = "Pre Launch"; return; } status = "Retracting solar panels"; core.solarpanel.RetractAll(); if (core.solarpanel.AllRetracted()) mode = AscentMode.VERTICAL_ASCENT; }