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";
            }
        }
Esempio n. 2
0
        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";
            }
        }
Esempio n. 3
0
        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;
            }
        }
Esempio n. 4
0
        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);
            }
        }
Esempio n. 9
0
        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;
            }
        }
Esempio n. 10
0
        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";
        }
Esempio n. 12
0
        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);
            }
        }
Esempio n. 13
0
        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";
            }
        }
Esempio n. 15
0
        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";
            }
        }
Esempio n. 17
0
        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";
        }
Esempio n. 18
0
        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";
        }
Esempio n. 20
0
        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);
            }
        }
Esempio n. 23
0
        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;
            }
        }
Esempio n. 24
0
        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;
            }
        }
Esempio n. 26
0
        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"
        }
Esempio n. 27
0
        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"
        }
Esempio n. 28
0
        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 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 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";
        }
Esempio n. 51
0
        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;
        }