예제 #1
0
        /// <summary>
        /// Creates feature button and puts it to <see cref="featureButtons"/> list.
        /// </summary>
        /// <param name="module">Feature related module.</param>
        /// <param name="nameId">Feature unique name.</param>
        /// <param name="tooltip">The tooltip for button.</param>
        /// <param name="onClick">The button click handler.</param>
        /// <param name="isActive">Super light weight function to check whether the feature is activated at the moment.</param>
        public void CreateFeatureButton(DisplayModule module, string nameId, string tooltip, ClickHandler onClick, Func <bool> isActive)
        {
            var texturePath       = "MechJeb2/Icons/" + nameId;
            var texturePathActive = texturePath + "_active";

            var button = new Button();

            button.button = ToolbarManager.Instance.add("MechJeb2", nameId);

            if (GameDatabase.Instance.GetTexture(texturePath, false) == null)
            {
                button.texturePath = Qmark;
                Log.err("No icon for {0}", nameId);
            }
            else
            {
                button.texturePath = texturePath;
            }

            if (GameDatabase.Instance.GetTexture(texturePathActive, false) == null)
            {
                button.texturePathActive = texturePath;
            }
            else
            {
                button.texturePathActive = texturePathActive;
            }

            button.button.ToolTip  = tooltip;
            button.button.OnClick += onClick;
            featureButtons.Add(() =>
            {
                button.button.TexturePath = isActive()
                    ? button.texturePathActive
                    : button.texturePath;
            }, button);

            button.button.Visible     = module.showInCurrentScene;
            button.button.TexturePath = button.texturePath;
        }
예제 #2
0
 public virtual void OnLoad(ConfigNode local, ConfigNode type, ConfigNode global)
 {
     try
     {
         if (global != null)
         {
             ConfigNode.LoadObjectFromConfig(this, global, (int)Pass.Global);
         }
         if (type != null)
         {
             ConfigNode.LoadObjectFromConfig(this, type, (int)Pass.Type);
         }
         if (local != null)
         {
             ConfigNode.LoadObjectFromConfig(this, local, (int)Pass.Local);
         }
     }
     catch (Exception e)
     {
         Log.err(e, "caught exception in OnLoad for {0} : {1}", this.GetType().Name, e);
     }
 }
        // sma is only used for the initial guess but it is the responsibility of the caller
        public void flightangle3constraintMAXE(double rTm, double gamma, double inc, int numStages, double sma, bool omitCoast, bool currentInc)
        {
            if (status == PVGStatus.ENABLED)
            {
                return;
            }

            bool doupdate = false;

            if (rTm != old_rTm || gamma != old_gamma || numStages != old_numStages)
            {
                doupdate = true;
            }

            // avoid slight drift in the current inclination from resetting guidance constantly
            if (inc != old_inc && !currentInc)
            {
                doupdate = true;
            }

            if (p == null || doupdate)
            {
                if (p != null)
                {
                    p.KillThread();
                }

                Log.info("MechJebModuleGuidanceController: setting up flightangle3constraintMAXE");
                PontryaginLaunch solver = NewPontryaginForLaunch(inc, sma);
                solver.omitCoast = omitCoast;
                solver.flightangle3constraintMAXE(rTm, gamma * UtilMath.Deg2Rad, inc * UtilMath.Deg2Rad, numStages);
                p = solver;
            }

            old_rTm       = rTm;
            old_gamma     = gamma;
            old_numStages = numStages;
            old_inc       = inc;
        }
        public void keplerian4constraintLANfree(double sma, double ecc, double inc, double ArgP, bool omitCoast, bool currentInc)
        {
            if (status == PVGStatus.ENABLED)
            {
                return;
            }

            bool doupdate = false;

            if (sma != old_sma || ecc != old_ecc || ArgP != old_ArgP)
            {
                doupdate = true;
            }

            // avoid slight drift in the current inclination from resetting guidance constantly
            if (inc != old_inc && !currentInc)
            {
                doupdate = true;
            }

            if (p == null || doupdate)
            {
                if (p != null)
                {
                    p.KillThread();
                }

                Log.info("MechJebModuleGuidanceController: setting up keplerian4constraintLANfree");
                PontryaginLaunch solver = NewPontryaginForLaunch(inc, sma);
                solver.omitCoast = omitCoast;
                solver.keplerian4constraintLANfree(sma, ecc, inc * UtilMath.Deg2Rad, ArgP * UtilMath.Deg2Rad);
                p = solver;
            }

            old_sma  = sma;
            old_ecc  = ecc;
            old_inc  = inc;
            old_ArgP = ArgP;
        }
        // sma is only used for the initial guess but it is the responsibility of the caller
        public void flightangle4constraint(double rT, double vT, double inc, double gamma, double sma, bool omitCoast, bool currentInc)
        {
            if (status == PVGStatus.ENABLED)
            {
                return;
            }

            bool doupdate = false;

            if (rT != old_rT || vT != old_vT || gamma != old_gamma)
            {
                doupdate = true;
            }

            // avoid slight drift in the current inclination from resetting guidance constantly
            if (inc != old_inc && !currentInc)
            {
                doupdate = true;
            }

            if (p == null || doupdate)
            {
                if (p != null)
                {
                    p.KillThread();
                }

                Log.info("MechJebModuleGuidanceController: setting up flightangle4constraint, rT: {0}; vT:{1}; inc:{2}; gamma: {3}", rT, vT, inc, gamma);
                PontryaginLaunch solver = NewPontryaginForLaunch(inc, sma);
                solver.omitCoast = omitCoast;
                solver.flightangle4constraint(rT, vT, gamma * UtilMath.Deg2Rad, inc * UtilMath.Deg2Rad);
                p = solver;
            }

            old_rT    = rT;
            old_vT    = vT;
            old_inc   = inc;
            old_gamma = gamma;
        }
        private void TryStartSimulation(bool doErrorSim)
        {
            try
            {
                if (!vessel.LandedOrSplashed)
                {
                    // We should be running simulations periodically. If one is not running right now,
                    // check if enough time has passed since the last one to start a new one:
                    if (!simulationRunning && (stopwatch.ElapsedMilliseconds > millisecondsBetweenSimulations || !stopwatch.IsRunning))
                    {
                        // variabledt generate too much instability of the landing site with atmo.
                        // variabledt = !(mainBody.atmosphere && core.landing.enabled);
                        // the altitude may induce some instability but allow for greater precision of the display in manual flight

                        //variabledt = !mainBody.atmosphere || vessel.terrainAltitude < 1000 ;
                        //if (!variabledt)
                        //    dt = 0.5;

                        stopwatch.Stop();
                        stopwatch.Reset();

                        StartSimulation(false);
                    }

                    // We also periodically run simulations containing deliberate errors if we have been asked to do so by the landing autopilot.
                    if (doErrorSim && this.runErrorSimulations && !errorSimulationRunning && (errorStopwatch.ElapsedMilliseconds >= millisecondsBetweenErrorSimulations || !errorStopwatch.IsRunning))
                    {
                        errorStopwatch.Stop();
                        errorStopwatch.Reset();

                        StartSimulation(true);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.err(ex, this);
            }
        }
예제 #7
0
        public double DecelerationEndAltitude()
        {
            if (UseAtmosphereToBrake())
            {
                // if the atmosphere is thick, deceleration (meaning freefall through the atmosphere)
                // should end a safe height above the landing site in order to allow braking from terminal velocity
#warning Drag Length is quite large now without parachutes, check this better
                double landingSiteDragLength = mainBody.DragLength(LandingAltitude, vesselAverageDrag + ParachuteAddedDragCoef(), vesselState.mass);
#if DEBUG
                Log.dbg("DecelerationEndAltitude Atmo {0:00}", (2 * landingSiteDragLength + LandingAltitude));
#endif
                return(1.1 * landingSiteDragLength + LandingAltitude);
            }
            else
            {
                //if the atmosphere is thin, the deceleration burn should end
                //500 meters above the landing site to allow for a controlled final descent
                double d = 500 + LandingAltitude;
                Log.dbg("DecelerationEndAltitude Vacum ", d);
                return(d);
            }
        }
예제 #8
0
        /// <summary>
        /// Queues an action to be invoked on the main game thread and blocks the
        /// current thread until the action has been executed.
        /// </summary>
        /// <param name="action">The action to be queued.</param>
        public static void Invoke(Action action)
        {
            if (!_instanceExists)
            {
                Log.err("No Dispatcher exists in the scene. Actions will not be invoked!");
                return;
            }

            bool hasRun = false;

            InvokeAsync(() =>
            {
                action();
                hasRun = true;
            });

            // Lock until the action has run
            while (!hasRun)
            {
                Thread.Sleep(5);
            }
        }
예제 #9
0
        void DeployParachutes()
        {
            if (vesselState.mainBody.atmosphere && deployChutes)
            {
                for (int i = 0; i < vesselState.parachutes.Count; i++)
                {
                    ModuleParachute p = vesselState.parachutes[i];
                    // what is the ASL at which we should deploy this parachute? It is the actual deployment height above the surface + the ASL of the predicted landing point.
                    double LandingSiteASL = LandingAltitude;
                    double ParachuteDeployAboveGroundAtLandingSite = p.deployAltitude * this.parachutePlan.Multiplier;

                    double ASLDeployAltitude = ParachuteDeployAboveGroundAtLandingSite + LandingSiteASL;

                    if (p.part.inverseStage >= limitChutesStage && p.deploymentState == ModuleParachute.deploymentStates.STOWED &&
                        ASLDeployAltitude > vesselState.altitudeASL && p.deploymentSafeState == ModuleParachute.deploymentSafeStates.SAFE)
                    {
                        p.Deploy();
                        Log.dbg("Deploying parachute {0} at {1}. ({2} + {3})", p.name, ASLDeployAltitude, LandingSiteASL, ParachuteDeployAboveGroundAtLandingSite);
                    }
                }
            }
        }
예제 #10
0
        private void SetTranslatronSpeed(float speed, bool relative = false)
        {
            MechJebCore masterMechJeb = vessel.GetMasterMechJeb();

            if (masterMechJeb != null)
            {
                MechJebModuleTranslatron moduleTranslatron = masterMechJeb.GetComputerModule <MechJebModuleTranslatron>();

                if (moduleTranslatron != null && !moduleTranslatron.hidden)
                {
                    thrust.trans_spd_act = (relative ? thrust.trans_spd_act : 0) + speed;
                }
                else
                {
                    Log.err("couldn't find MechJebModuleTranslatron for translatron control via action group.");
                }
            }
            else
            {
                Log.err("couldn't find the master MechJeb module for the current vessel.");
            }
        }
 override public void activateAction()
 {
     base.activateAction();
     if (this.selectedPartIndex < this.kosModules.Count)
     {
         if (openTerminal)
         {
             this.kosModules[this.selectedPartIndex].GetType().InvokeMember("OpenWindow", System.Reflection.BindingFlags.InvokeMethod, null, this.kosModules[this.selectedPartIndex], null);
         }
         sharedObjects = this.kosModules[this.selectedPartIndex].GetType().GetField("shared", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(this.kosModules[this.selectedPartIndex]);
         if (sharedObjects != null)
         {
             interpreter = sharedObjects.GetType().GetProperty("Interpreter").GetValue(sharedObjects, null);
             if (interpreter != null)
             {
                 interpreter.GetType().InvokeMember("ProcessCommand", System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance, null, interpreter, new object[] { command });
                 if (!this.waitFinish)
                 {
                     this.endAction();
                 }
             }
             else
             {
                 Log.err("---- NO Interpreter OBJECT ----");
                 this.endAction();
             }
         }
         else
         {
             Log.err("---- NO SHARED OBJECT ----");
             this.endAction();
         }
     }
     else
     {
         this.endAction();
     }
 }
예제 #12
0
 public virtual void OnSave(ConfigNode local, ConfigNode type, ConfigNode global)
 {
     try
     {
         if (global != null)
         {
             ConfigNode.CreateConfigFromObject(this, (int)Pass.Global, null).CopyTo(global);
         }
         if (type != null)
         {
             ConfigNode.CreateConfigFromObject(this, (int)Pass.Type, null).CopyTo(type);
         }
         if (local != null)
         {
             ConfigNode.CreateConfigFromObject(this, (int)Pass.Local, null).CopyTo(local);
         }
         dirty = false;
     }
     catch (Exception e)
     {
         Log.err(e, "caught exception in OnSave for {0} : {1}", this.GetType().Name, e);
     }
 }
예제 #13
0
        public void OnTranslatronToggleHSAction(KSPActionParam param)
        {
            MechJebCore masterMechJeb = vessel.GetMasterMechJeb();

            if (masterMechJeb != null)
            {
                MechJebModuleTranslatron moduleTranslatron = masterMechJeb.GetComputerModule <MechJebModuleTranslatron>();

                if (moduleTranslatron != null && !moduleTranslatron.hidden)
                {
                    thrust.trans_kill_h = !thrust.trans_kill_h;
                }
                else
                {
                    //Log.err(Localizer.Format("#MechJeb_LogError_msg2"));//"MechJeb couldn't find MechJebModuleTranslatron for translatron control via action group."
                    Log.err("couldn't find MechJebModuleTranslatron for translatron control via action group.");
                }
            }
            else
            {
                Log.err("couldn't find the master MechJeb module for the current vessel.");
            }
        }
예제 #14
0
        public void LoadScriptModuleConfig()
        {
            string slotName = this.getSaveSlotName(false);

            if (File.Exists <MechJebCore>("mechjeb_settings_script_" + slotName + "_conf.cfg"))
            {
                ConfigNode node = null;
                try
                {
                    node = ConfigNode.Load(IOUtils.GetFilePathFor(this.GetType(), "mechjeb_settings_script_" + slotName + "_conf.cfg"));
                }
                catch (Exception e)
                {
                    Log.err(e, "MechJebModuleScript.LoadConfig caught an exception trying to load mechjeb_settings_script_{0}_conf.cfg: {1}", slotName, e);
                }
                if (node == null)
                {
                    return;
                }

                ConfigNode.LoadObjectFromConfig(this, node);
            }
            this.updateScriptsNames();
        }
예제 #15
0
        public virtual Vector3d Lift(Vector3d vesselVelocity, double liftFactor)
        {
            if (shieldedFromAirstream || hasLiftModule)
            {
                return(Vector3d.zero);
            }

            // direction of the lift in a vessel centric reference
            Vector3d liftV = partToVessel * ((Vector3d)cubes.LiftForce * bodyLiftMultiplier * liftFactor);

            Vector3d liftVector = liftV.ProjectOnPlane(vesselVelocity);

#if DEBUG && false
            if (vesselVelocity.sqrMagnitude > 1 && oPart.DragCubes.LiftForce.sqrMagnitude > 0.001)
            {
                string msg = oPart.name;

                Vector3 bodyL    = oPart.transform.rotation * (oPart.bodyLiftScalar * oPart.DragCubes.LiftForce);
                Vector3 bodyLift = Vector3.ProjectOnPlane(bodyL, -oPart.dragVectorDir);

                msg += "\n liftDir " + MuUtils.PrettyPrint(liftVector) + " vs " + MuUtils.PrettyPrint(bodyLift) + " " + Vector3.Angle(liftVector, bodyLift).ToString("F3") + "°";

                Vector3 localBodyL = oPart.vessel.transform.InverseTransformDirection(bodyL);
                msg += "\n liftV " + MuUtils.PrettyPrint(liftV) + " vs " + MuUtils.PrettyPrint(localBodyL) + " " + Vector3.Angle(liftV, localBodyL).ToString("F3") + "°";

                msg += "\n liftForce " + MuUtils.PrettyPrint(cubes.LiftForce) + " vs " + MuUtils.PrettyPrint(oPart.DragCubes.LiftForce) + " " + Vector3.Angle(cubes.LiftForce, oPart.DragCubes.LiftForce).ToString("F3") + "°";
                msg += "\n Normals " + MuUtils.PrettyPrint(-vesselVelocity) + " vs " + MuUtils.PrettyPrint(-oPart.dragVectorDir) + " " + Vector3.Angle(-vesselVelocity, -oPart.dragVectorDir).ToString("F3") + "°";

                msg += "\n vals " + bodyLiftMultiplier.ToString("F5") + " " + dynamicPressurekPa.ToString("F5") + " " + liftCurves.liftMachCurve.Evaluate(mach).ToString("F5");

                Log.dbg(msg);
            }
#endif

            return(liftVector);
        }
예제 #16
0
 public void LoadConfig(ConfigNode node)
 {
     this.clearAll();
     //Load custom info scripts, which are stored in our ConfigNode:
     ConfigNode[] scriptNodes = node.GetNodes();
     foreach (ConfigNode scriptNode in scriptNodes)
     {
         MechJebModuleScriptAction obj = null;
         if (scriptNode.name.CompareTo(MechJebModuleScriptActionSmartASS.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionSmartASS(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionAscent.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionAscent(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionTimer.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionTimer(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionCrewTransfer.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionCrewTransfer(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionDockingAutopilot.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionDockingAutopilot(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionPause.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionPause(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionStaging.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionStaging(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionTargetDock.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionTargetDock(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionTarget.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionTarget(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionControlFrom.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionControlFrom(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionUndock.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionUndock(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionDockingShield.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionDockingShield(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionQuicksave.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionQuicksave(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionRCS.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionRCS(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionActiveVessel.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionActiveVessel(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionActivateEngine.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionActivateEngine(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionSAS.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionSAS(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionThrottle.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionThrottle(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionExecuteNode.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionExecuteNode(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionManoeuver.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionManoeuver(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionLanding.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionLanding(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionWarp.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionWarp(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionTolerance.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionTolerance(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionWaitFor.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionWaitFor(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionFor.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionFor(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionIf.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionIf(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionWhile.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionWhile(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionParallel.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionParallel(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionActionGroup.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionActionGroup(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionLoadScript.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionLoadScript(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionRendezvous.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionRendezvous(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionRendezvousAP.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionRendezvousAP(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionIRSequencer.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionIRSequencer(scriptModule, core, this);
         }
         else if (scriptNode.name.CompareTo(MechJebModuleScriptActionKos.NAME) == 0)
         {
             obj = new MechJebModuleScriptActionKos(scriptModule, core, this);
         }
         else
         {
             Log.err("MechJebModuleScript.LoadConfig : Unknown node {0}", scriptNode.name);
         }
         if (obj != null)
         {
             ConfigNode.LoadObjectFromConfig(obj, scriptNode);
             obj.postLoad(scriptNode);
             this.addAction(obj);
         }
     }
 }
예제 #17
0
 public override void OnStart(PartModule.StartState state)
 {
     Log.dbg("ModExtensionTest adding MJ2 callback");
     //vesselState.vesselStatePartExtensions.Add(partUpdate);
     //vesselState.vesselStatePartModuleExtensions.Add(partModuleUpdate);
 }
예제 #18
0
        public override void Drive(FlightCtrlState s)
        {
            float threshold = 0.1F;
            bool  _userCommandingRotation = !(Mathfx.Approx(s.pitch, s.pitchTrim, threshold) &&
                                              Mathfx.Approx(s.yaw, s.yawTrim, threshold) &&
                                              Mathfx.Approx(s.roll, s.rollTrim, threshold));
            bool _userCommandingTranslation = !(Math.Abs(s.X) < threshold &&
                                                Math.Abs(s.Y) < threshold &&
                                                Math.Abs(s.Z) < threshold);

            if (_userCommandingRotation && !_userCommandingTranslation)
            {
                userCommandingRotationSmoothed = 2;
            }
            else if (userCommandingRotationSmoothed > 0)
            {
                userCommandingRotationSmoothed--;
            }

            if (core.GetComputerModule <MechJebModuleThrustWindow>().hidden&& core.GetComputerModule <MechJebModuleAscentGuidance>().hidden)
            {
                return;
            }

            if ((tmode != TMode.OFF) && (vesselState.thrustAvailable > 0))
            {
                double spd = 0;

                switch (tmode)
                {
                case TMode.KEEP_ORBITAL:
                    spd = vesselState.speedOrbital;
                    break;

                case TMode.KEEP_SURFACE:
                    spd = vesselState.speedSurface;
                    break;

                case TMode.KEEP_VERTICAL:
                    spd = vesselState.speedVertical;
                    Vector3d rot = Vector3d.up;
                    if (trans_kill_h)
                    {
                        Vector3 hsdir = Vector3.ProjectOnPlane(vesselState.surfaceVelocity, vesselState.up);
                        Vector3 dir   = -hsdir + vesselState.up * Math.Max(Math.Abs(spd), 20 * mainBody.GeeASL);
                        if ((Math.Min(vesselState.altitudeASL, vesselState.altitudeTrue) > 5000) && (hsdir.magnitude > Math.Max(Math.Abs(spd), 100 * mainBody.GeeASL) * 2))
                        {
                            tmode         = TMode.DIRECT;
                            trans_spd_act = 100;
                            rot           = -hsdir;
                        }
                        else
                        {
                            rot = dir.normalized;
                        }
                        core.attitude.attitudeTo(rot, AttitudeReference.INERTIAL, null);
                    }
                    break;
                }

                double t_err = (trans_spd_act - spd) / vesselState.maxThrustAccel;
                if ((tmode == TMode.KEEP_ORBITAL && Vector3d.Dot(vesselState.forward, vesselState.orbitalVelocity) < 0) ||
                    (tmode == TMode.KEEP_SURFACE && Vector3d.Dot(vesselState.forward, vesselState.surfaceVelocity) < 0))
                {
                    //allow thrust to declerate
                    t_err *= -1;
                }

                double t_act = pid.Compute(t_err);

                if ((tmode != TMode.KEEP_VERTICAL) ||
                    !trans_kill_h ||
                    (core.attitude.attitudeError < 2) ||
                    ((Math.Min(vesselState.altitudeASL, vesselState.altitudeTrue) < 1000) && (core.attitude.attitudeError < 90)))
                {
                    if (tmode == TMode.DIRECT)
                    {
                        trans_prev_thrust = targetThrottle = trans_spd_act / 100.0F;
                    }
                    else
                    {
                        trans_prev_thrust = targetThrottle = Mathf.Clamp01(trans_prev_thrust + (float)t_act);
                    }
                }
                else
                {
                    bool useGimbal = (vesselState.torqueGimbal.positive.x > vesselState.torqueAvailable.x * 10) ||
                                     (vesselState.torqueGimbal.positive.z > vesselState.torqueAvailable.z * 10);

                    bool useDiffThrottle = (vesselState.torqueDiffThrottle.x > vesselState.torqueAvailable.x * 10) ||
                                           (vesselState.torqueDiffThrottle.z > vesselState.torqueAvailable.z * 10);

                    if ((core.attitude.attitudeError >= 2) && (useGimbal || (useDiffThrottle && core.thrust.differentialThrottle)))
                    {
                        trans_prev_thrust = targetThrottle = 0.1F;
                        Log.detail(" targetThrottle = 0.1F");
                    }
                    else
                    {
                        trans_prev_thrust = targetThrottle = 0;
                    }
                }
            }

            // Only set throttle if a module need it. Otherwise let the user or other mods set it
            // There is always at least 1 user : the module itself (why ?)
            if (users.Count > 1)
            {
                s.mainThrottle = targetThrottle;
            }

            throttleLimit      = 1;
            throttleFixedLimit = 1;

            limiter = LimitMode.None;

            if (limitThrottle)
            {
                if (maxThrottle < throttleLimit)
                {
                    setFixedLimit((float)maxThrottle, LimitMode.Throttle);
                }
            }

            if (limitToTerminalVelocity)
            {
                float limit = TerminalVelocityThrottle();
                if (limit < throttleLimit)
                {
                    setFixedLimit(limit, LimitMode.TerminalVelocity);
                }
            }

            if (limitDynamicPressure)
            {
                float limit = MaximumDynamicPressureThrottle();
                if (limit < throttleLimit)
                {
                    setFixedLimit(limit, LimitMode.DynamicPressure);
                }
            }

            if (limitToPreventOverheats)
            {
                float limit = (float)TemperatureSafetyThrottle();
                if (limit < throttleLimit)
                {
                    setFixedLimit(limit, LimitMode.Temperature);
                }
            }

            if (limitAcceleration)
            {
                float limit = AccelerationLimitedThrottle();
                if (limit < throttleLimit)
                {
                    setFixedLimit(limit, LimitMode.Acceleration);
                }
            }

            if (electricThrottle && ElectricEngineRunning())
            {
                float limit = ElectricThrottle();
                if (limit < throttleLimit)
                {
                    setFixedLimit(limit, LimitMode.Electric);
                }
            }

            if (limitToPreventFlameout)
            {
                // This clause benefits being last: if we don't need much air
                // due to prior limits, we can close some intakes.
                float limit = FlameoutSafetyThrottle();
                if (limit < throttleLimit)
                {
                    setFixedLimit(limit, LimitMode.Flameout);
                }
            }

            // Any limiters which can limit to non-zero values must come before this, any
            // limiters (like ullage) which enforce zero throttle should come after.  The
            // minThrottle setting has authority over any other limiter that sets non-zero throttle.
            if (limiterMinThrottle && limiter != LimitMode.None)
            {
                if (minThrottle > throttleFixedLimit)
                {
                    setFixedLimit((float)minThrottle, LimitMode.MinThrottle);
                }
                if (minThrottle > throttleLimit)
                {
                    setTempLimit((float)minThrottle, LimitMode.MinThrottle);
                }
            }

            /* auto-RCS ullaging up to very stable */
            if (autoRCSUllaging && s.mainThrottle > 0.0F && throttleLimit > 0.0F)
            {
                if (vesselState.lowestUllage < VesselState.UllageState.VeryStable)
                {
                    Log.info("RCS auto-ullaging: found state below very stable: {0}", vesselState.lowestUllage);
                    if (vessel.hasEnabledRCSModules())
                    {
                        if (!vessel.ActionGroups[KSPActionGroup.RCS])
                        {
                            Log.info("RCS auto-ullaging: enabling RCS action group for automatic ullaging");
                            vessel.ActionGroups.SetGroup(KSPActionGroup.RCS, true);
                        }
                        Log.info("RCS auto-ullaging: firing RCS to stabilize ulllage");
                        setTempLimit(0.0F, LimitMode.UnstableIgnition);
                        s.Z = -1.0F;
                    }
                    else
                    {
                        Log.info("RCS auto-ullaging: vessel has no enabled/staged RCS modules");
                    }
                }
            }

            /* prevent unstable ignitions */
            if (limitToPreventUnstableIgnition && s.mainThrottle > 0.0F && throttleLimit > 0.0F)
            {
                if (vesselState.lowestUllage < VesselState.UllageState.Stable)
                {
                    ScreenMessages.PostScreenMessage(preventingUnstableIgnitionsMessage);
                    Log.info("Unstable Ignitions: preventing ignition in state: {0}", vesselState.lowestUllage);
                    setTempLimit(0.0F, LimitMode.UnstableIgnition);
                }
            }

            // we have to force the throttle here so that rssMode can work, otherwise we don't get our last throttle command
            // back on the next tick after disabling.  we save this before applying the throttle limits so that we preserve
            // the requested throttle, and not the limited throttle.
            if (core.rssMode)
            {
                SetFlightGlobals(s.mainThrottle);
            }

            if (double.IsNaN(throttleLimit))
            {
                throttleLimit = 1.0F;
            }
            throttleLimit = Mathf.Clamp01(throttleLimit);

            /* we do not _apply_ the "fixed" limit, the actual throttleLimit should always be the more limited and lower one */
            /* the purpose of the "fixed" limit is for external consumers like the node executor to consume */
            if (double.IsNaN(throttleFixedLimit))
            {
                throttleFixedLimit = 1.0F;
            }
            throttleFixedLimit = Mathf.Clamp01(throttleFixedLimit);

            vesselState.throttleLimit      = throttleLimit;
            vesselState.throttleFixedLimit = throttleFixedLimit;

            if (s.mainThrottle < throttleLimit)
            {
                limiter = LimitMode.None;
            }

            s.mainThrottle = Mathf.Min(s.mainThrottle, throttleLimit);

            if (smoothThrottle)
            {
                s.mainThrottle = SmoothThrottle(s.mainThrottle);
            }

            if (double.IsNaN(s.mainThrottle))
            {
                s.mainThrottle = 0;
            }

            s.mainThrottle = Mathf.Clamp01(s.mainThrottle);


            if (s.Z == 0 && core.rcs.rcsThrottle && vesselState.rcsThrust)
            {
                s.Z = -s.mainThrottle;
            }

            lastThrottle = s.mainThrottle;

            if (!core.attitude.enabled)
            {
                Vector3d act = new Vector3d(s.pitch, s.yaw, s.roll);
                differentialThrottleDemandedTorque = -Vector3d.Scale(act.xzy, vesselState.torqueDiffThrottle * s.mainThrottle * 0.5f);
            }
        }
예제 #19
0
        void InitDocking()
        {
            lastTarget = core.target.Target;

            try
            {
                vesselBoundingBox = vessel.GetBoundingBox();
                targetBoundingBox = lastTarget.GetVessel().GetBoundingBox();

                if (!overrideTargetSize)
                {
                    targetSize = targetBoundingBox.size.magnitude;
                }
                else
                {
                    targetSize = (float)overridenTargetSize.val;
                }

                if (!overrideSafeDistance)
                {
                    safeDistance = vesselBoundingBox.size.magnitude + targetSize + 0.5f;
                }
                else
                {
                    safeDistance = (float)overridenSafeDistance.val;
                }

                if (core.target.Target is ModuleDockingNode)
                {
                    acquireRange = ((ModuleDockingNode)core.target.Target).acquireRange * 0.5;
                }
                else
                {
                    acquireRange = 0.25;
                }
            }
            catch (Exception e)
            {
                Log.err(e, this);
            }

            if (zSep < 0)  //we're behind the target
            {
                // If we're more than half our own bounding box size behind the target port then use wrong side behavior
                // Still needs improvement. The reason for these changes is that to prevent wrong side behavior when our
                // port slipped behind the target by a fractional amount. The result is that rather than avoiding the
                // target ship we end up trying to pass right through it.
                // What's really needed here is code that compares bounding box positions to determine if we just try to back up or change sides completely.
                if (Math.Abs(zSep) > vesselBoundingBox.size.magnitude * 0.5f)
                {
                    dockingStep = DockingStep.WRONG_SIDE_BACKING_UP;
                }
                else
                {
                    dockingStep = DockingStep.BACKING_UP; // Just back straight up.
                }
            }
            else if (lateralSep.magnitude > dockingcorridorRadius) // in front but far from docking axis
            {
                if (zSep < targetSize)
                {
                    dockingStep = DockingStep.BACKING_UP;
                }
                else
                {
                    dockingStep = DockingStep.MOVING_TO_START;
                }
            }
            else
            {
                dockingStep = DockingStep.DOCKING;
            }
        }
예제 #20
0
        public override void OnLoad(ConfigNode sfsNode)
        {
            if (GuiUtils.skin == null)
            {
                //GuiUtils.skin = new GUISkin();
                new GameObject("zombieGUILoader", typeof(ZombieGUILoader));
            }
            try
            {
                bool generateDefaultWindows = false;

                base.OnLoad(sfsNode); //is this necessary?


                // With the Unity 4.6 upgrade of KSP 1.0 we inherited a serialization problem
                // with object with high depth like config nodes
                // so the partmodule config node passed was not ok.
                // So we use a static dir to save the part config node.
                if (!savedConfig.ContainsKey(part.name))
                {
                    if (HighLogic.LoadedScene == GameScenes.LOADING)
                    {
                        savedConfig.Add(part.name, sfsNode);
                    }
                }
                else
                {
                    partSettings = savedConfig[part.name];
                }

                LoadComputerModules();

                ConfigNode global = new ConfigNode("MechJebGlobalSettings");
                if (File.Exists <MechJebCore>("mechjeb_settings_global.cfg"))
                {
                    try
                    {
                        global = ConfigNode.Load(IOUtils.GetFilePathFor(this.GetType(), "mechjeb_settings_global.cfg"));
                    }
                    catch (Exception e)
                    {
                        Log.err(e, "MechJebCore.OnLoad caught an exception trying to load mechjeb_settings_global.cfg: {0}", e);
                        generateDefaultWindows = true;
                    }
                }
                else
                {
                    generateDefaultWindows = true;
                }

                ConfigNode type       = new ConfigNode("MechJebTypeSettings");
                string     vesselName = vessel != null?string.Join("_", vessel.vesselName.Split(System.IO.Path.GetInvalidFileNameChars())) : ""; // Strip illegal char from the filename

                if ((vessel != null) && File.Exists <MechJebCore>("mechjeb_settings_type_" + vesselName + ".cfg"))
                {
                    try
                    {
                        type = ConfigNode.Load(IOUtils.GetFilePathFor(this.GetType(), "mechjeb_settings_type_" + vesselName + ".cfg"));
                    }
                    catch (Exception e)
                    {
                        Log.err(e, "MechJebCore.OnLoad caught an exception trying to load mechjeb_settings_type_{0}.cfg: {1}", vesselName, e);
                    }
                }

                ConfigNode local = new ConfigNode("MechJebLocalSettings");
                if (sfsNode != null && sfsNode.HasNode("MechJebLocalSettings"))
                {
                    local = sfsNode.GetNode("MechJebLocalSettings");
                }
                else if (partSettings != null && partSettings.HasNode("MechJebLocalSettings"))
                {
                    local = partSettings.GetNode("MechJebLocalSettings");
                }
                else if (sfsNode == null) // capture current Local settings
                {
                    foreach (ComputerModule module in GetComputerModules <ComputerModule>())
                    {
                        try
                        {
                            module.OnSave(local.AddNode(module.GetType().Name), null, null);
                        }
                        catch (Exception e)
                        {
                            Log.err(e, "module {0} threw an exception in OnLoad: {1}", module.GetType().Name, e);
                        }
                    }
                }

                Log.dbg("OnLoad: loading from\n\tLocal: {0}\n\tType: {1}\n\tGlobal: {0}", local, type, global);

                // Remove any currently loaded custom windows
                MechJebModuleCustomInfoWindow win;
                while ((win = GetComputerModule <MechJebModuleCustomInfoWindow>()) != null)
                {
                    RemoveComputerModule(win);
                }

                foreach (ComputerModule module in GetComputerModules <ComputerModule>())
                {
                    try
                    {
                        string     name         = module.GetType().Name;
                        ConfigNode moduleLocal  = local.HasNode(name) ? local.GetNode(name) : null;
                        ConfigNode moduleType   = type.HasNode(name) ? type.GetNode(name) : null;
                        ConfigNode moduleGlobal = global.HasNode(name) ? global.GetNode(name) : null;
                        module.OnLoad(moduleLocal, moduleType, moduleGlobal);
                    }
                    catch (Exception e)
                    {
                        Log.err("module threw an exception in OnLoad: {0} {1}", module.GetType().Name, e);
                    }
                }

                LoadDelayedModules();

                if (generateDefaultWindows)
                {
                    GetComputerModule <MechJebModuleCustomWindowEditor>().AddDefaultWindows();
                }
            }
            catch (ReflectionTypeLoadException ex)
            {
                Log.err("caught a ReflectionTypeLoadException. Those DLL meeds maintenance and can cause serious collateral effects on the system if not fixed:");
                var brokenAssembly = ex.Types.Where(x => x != null).Select(x => x.Assembly).Distinct();
                foreach (Assembly assembly in brokenAssembly)
                {
                    Log.err("{0} {1} {2}",
                            assembly.GetName().Name, assembly.GetName().Version, assembly.Location.Remove(0, Path.GetFullPath(KSPUtil.ApplicationRootPath).Length)
                            );
                }
            }
            catch (Exception e)
            {
                Log.err(e, "caught exception in core OnLoad: {0}", e);
            }
        }
예제 #21
0
        private DifferentialThrottleStatus ComputeDifferentialThrottle(Vector3d torque)
        {
#if DEBUG
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
#endif
            float mainThrottle = vessel.ctrlState.mainThrottle;

            if (mainThrottle == 0)
            {
                torque       = Vector3d.zero;
                mainThrottle = 1;
            }

            int nb_engines = vesselState.enginesWrappers.Count;

            double   torqueScale = 0;
            double   forceScale  = 0;
            Vector3d force       = new Vector3d();

            for (int i = 0; i < nb_engines; i++)
            {
                torque      -= vesselState.enginesWrappers[i].constantTorque;
                torqueScale += vesselState.enginesWrappers[i].maxVariableTorque.magnitude;

                force      += Vector3d.Dot(mainThrottle * vesselState.enginesWrappers[i].maxVariableForce, Vector3d.up) * Vector3d.up;
                forceScale += vesselState.enginesWrappers[i].maxVariableForce.magnitude * 10;
            }

            var engines = vesselState.enginesWrappers.Where(eng => !eng.engine.throttleLocked).ToList();
            var n       = engines.Count;

            if (nb_engines == 0)
            {
                return(DifferentialThrottleStatus.AllEnginesOff);
            }

            if (nb_engines == 1 || n == 0)
            {
                return(DifferentialThrottleStatus.MoreEnginesRequired);
            }


            double[,] a = new double[n, n];
            double[] b      = new double[n];
            double[] boundL = new double[n];
            double[] boundU = new double[n];

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    a[i, j] = Vector3d.Dot(engines[i].maxVariableTorque, engines[j].maxVariableTorque) / (torqueScale * torqueScale)
                              + Vector3d.Dot(engines[i].maxVariableForce, engines[j].maxVariableForce) / (forceScale * forceScale);
                }
                b[i] = -Vector3d.Dot(engines[i].maxVariableTorque, torque) / (torqueScale * torqueScale)
                       - Vector3d.Dot(engines[i].maxVariableForce, force) / (forceScale * forceScale);

                boundL[i] = 0;
                boundU[i] = mainThrottle;
            }
            alglib.minqpstate state;
            alglib.minqpcreate(n, out state);
            alglib.minqpsetquadraticterm(state, a);
            alglib.minqpsetlinearterm(state, b);
            alglib.minqpsetbc(state, boundL, boundU);
            alglib.minqpsetalgobleic(state, 0.0, 0.0, 0.0, 0);
#if DEBUG
            var t1 = stopwatch.ElapsedMilliseconds;
#endif
            alglib.minqpoptimize(state);
#if DEBUG
            var t2 = stopwatch.ElapsedMilliseconds;
#endif
            double[]           x;
            alglib.minqpreport report;
            alglib.minqpresults(state, out x, out report);
#if DEBUG
            var t3 = stopwatch.ElapsedMilliseconds;
            Log.dbg("[DiffThrottle] t1: {0}, t2: {1}, t3: {2}", t1, t2 - t1, t3 - t2);
#endif

            if (x.Any(val => double.IsNaN(val)))
            {
                return(DifferentialThrottleStatus.SolverFailed);
            }

            for (int i = 0; i < n; i++)
            {
                engines[i].thrustRatio = (float)(x[i] / mainThrottle);
            }

            return(DifferentialThrottleStatus.Success);
        }
예제 #22
0
        public void Update()
        {
            if (this != vessel.GetMasterMechJeb() || (!FlightGlobals.ready && HighLogic.LoadedSceneIsFlight) || !ready)
            {
                return;
            }
            Profiler.BeginSample("MechJebCore.Update");

            if (Input.GetKeyDown(KeyCode.V) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)))
            {
                MechJebModuleCustomWindowEditor windowEditor = GetComputerModule <MechJebModuleCustomWindowEditor>();
                if (windowEditor != null)
                {
                    windowEditor.CreateWindowFromSharingString(MuUtils.SystemClipboard);
                }
            }

            //periodically save settings in case we quit unexpectedly
            Profiler.BeginSample("MechJebCore.Update.OnSave");
            if (HighLogic.LoadedSceneIsEditor || (vessel != null && vessel.isActiveVessel))
            {
                if (Time.time > lastSettingsSaveTime + 5)
                {
                    if (NeedToSave())
                    {
                        Log.dbg("Periodic settings save");
                        OnSave(null);
                    }
                    lastSettingsSaveTime = Time.time;
                }
            }
            Profiler.EndSample();

            if (ResearchAndDevelopment.Instance != null && unorderedComputerModules.Any(a => !a.unlockChecked))
            {
                foreach (ComputerModule module in GetComputerModules <ComputerModule>())
                {
                    try
                    {
                        module.UnlockCheck();
                    }
                    catch (Exception e)
                    {
                        Log.err(e, "module {0} threw an exception in UnlockCheck: {1}", module.GetType().Name, e);
                    }
                }
            }

            Profiler.BeginSample("OnMenuUpdate");
            GetComputerModule <MechJebModuleMenu>().OnMenuUpdate(); // Allow the menu movement, even while in Editor
            Profiler.EndSample();

            if (vessel == null)
            {
                Profiler.EndSample();
                return; //don't run ComputerModules' OnUpdate in editor
            }

            foreach (ComputerModule module in GetComputerModules <ComputerModule>())
            {
                Profiler.BeginSample(module.profilerName);
                try
                {
                    if (module.enabled)
                    {
                        module.OnUpdate();
                    }
                }
                catch (Exception e)
                {
                    Log.err(e, "module {0} threw an exception in OnUpdate: {1}", module.GetType().Name, e);
                }
                Profiler.EndSample();
            }
            Profiler.EndSample();
        }
예제 #23
0
        void LoadComputerModules()
        {
            if (moduleRegistry == null)
            {
                moduleRegistry = new List <Type>();
                foreach (var ass in AppDomain.CurrentDomain.GetAssemblies())
                {
                    try
                    {
                        foreach (var module in (from t in ass.GetTypes() where t.IsSubclassOf(typeof(ComputerModule)) && !t.IsAbstract select t).ToList())
                        {
                            moduleRegistry.Add(module);
                        }
                    }
                    catch (Exception e)
                    {
                        Log.err(e, "moduleRegistry creation threw an exception in LoadComputerModules loading {0}: {1}", ass.FullName, e);
                    }
                }
            }

            Assembly        assembly        = Assembly.GetExecutingAssembly();
            FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location);

            // Mono compiler is stupid and use AssemblyVersion for the AssemblyFileVersion
            // So we use an other field to store the dev build number ...
            Attribute[] attributes  = Attribute.GetCustomAttributes(assembly, typeof(AssemblyInformationalVersionAttribute));
            string      dev_version = "";

            if (attributes.Length > 0)
            {
                dev_version = ((AssemblyInformationalVersionAttribute)attributes[0]).InformationalVersion;
            }

            if (dev_version == "")
            {
                version = string.Format("{0}.{1}.{2}", fileVersionInfo.FileMajorPart, fileVersionInfo.FileMinorPart, fileVersionInfo.FileBuildPart);
            }
            else
            {
                version = dev_version;
            }

            if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight)
            {
                Log.info("Loading Mechjeb {0}", version);
            }

            try
            {
                foreach (Type t in moduleRegistry)
                {
                    if ((t != typeof(ComputerModule)) && (t != typeof(DisplayModule) && (t != typeof(MechJebModuleCustomInfoWindow))) &&
                        (t != typeof(AutopilotModule)) &&
                        !blacklist.Contains(t.Name) && (GetComputerModule(t.Name) == null))
                    {
                        ConstructorInfo constructorInfo = t.GetConstructor(new[] { typeof(MechJebCore) });
                        if (constructorInfo != null)
                        {
                            AddComputerModule((ComputerModule)(constructorInfo.Invoke(new object[] { this })));
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Log.err(e, "moduleRegistry loading threw an exception in LoadComputerModules: {0}", e);
            }

            attitude       = GetComputerModule <MechJebModuleAttitudeController>();
            staging        = GetComputerModule <MechJebModuleStagingController>();
            thrust         = GetComputerModule <MechJebModuleThrustController>();
            target         = GetComputerModule <MechJebModuleTargetController>();
            warp           = GetComputerModule <MechJebModuleWarpController>();
            rcs            = GetComputerModule <MechJebModuleRCSController>();
            rcsbal         = GetComputerModule <MechJebModuleRCSBalancer>();
            rover          = GetComputerModule <MechJebModuleRoverController>();
            node           = GetComputerModule <MechJebModuleNodeExecutor>();
            solarpanel     = GetComputerModule <MechJebModuleSolarPanelController>();
            antennaControl = GetComputerModule <MechJebModuleDeployableAntennaController>();
            landing        = GetComputerModule <MechJebModuleLandingAutopilot>();
            settings       = GetComputerModule <MechJebModuleSettings>();
            airplane       = GetComputerModule <MechJebModuleAirplaneAutopilot>();
            guidance       = GetComputerModule <MechJebModuleGuidanceController>();
            stageStats     = GetComputerModule <MechJebModuleStageStats>();
            stageTracking  = GetComputerModule <MechJebModuleLogicalStageTracking>();
        }
예제 #24
0
        public override void OnUpdate()
        {
            //Commenting this because now we support autostaging on non active vessels
            //if (!vessel.isActiveVessel)
            //    return;

            //if autostage enabled, and if we've already staged at least once, and if there are stages left,
            //and if we are allowed to continue staging, and if we didn't just fire the previous stage
            if (waitingForFirstStaging || vessel.currentStage <= 0 || vessel.currentStage <= autostageLimit || vessel.currentStage <= autostageLimitInternal ||
                vesselState.time - lastStageTime < autostagePostDelay)
            {
                return;
            }

            //don't decouple active or idle engines or tanks
            UpdateActiveModuleEngines();
            UpdateBurnedResources();
            if (InverseStageDecouplesActiveOrIdleEngineOrTank(vessel.currentStage - 1, vessel, burnedResources, activeModuleEngines))
            {
                return;
            }

            // prevent staging when the current stage has active engines and the next stage has any engines (but not decouplers or clamps)
            if (hotStaging && InverseStageHasActiveEngines(vessel.currentStage, vessel) && InverseStageHasEngines(vessel.currentStage - 1, vessel) && !InverseStageFiresDecoupler(vessel.currentStage - 1, vessel) && !InverseStageReleasesClamps(vessel.currentStage - 1, vessel) && LastNonZeroDVStageBurnTime() > hotStagingLeadTime)
            {
                return;
            }

            //Don't fire a stage that will activate a parachute, unless that parachute gets decoupled:
            if (HasStayingChutes(vessel.currentStage - 1, vessel))
            {
                return;
            }

            //Always drop deactivated engines or tanks
            if (!InverseStageDecouplesDeactivatedEngineOrTank(vessel.currentStage - 1, vessel))
            {
                //only decouple fairings if the dynamic pressure, altitude, and aerothermal flux conditions are respected
                if ((core.vesselState.dynamicPressure > fairingMaxDynamicPressure || core.vesselState.altitudeASL < fairingMinAltitude || core.vesselState.freeMolecularAerothermalFlux > fairingMaxAerothermalFlux) &&
                    HasFairing(vessel.currentStage - 1, vessel))
                {
                    return;
                }

                //only release launch clamps if we're at nearly full thrust
                if (vesselState.thrustCurrent / vesselState.thrustAvailable < clampAutoStageThrustPct &&
                    InverseStageReleasesClamps(vessel.currentStage - 1, vessel))
                {
                    return;
                }
            }

            //When we find that we're allowed to stage, start a countdown (with a
            //length given by autostagePreDelay) and only stage once that countdown finishes,
            if (countingDown)
            {
                if (vesselState.time - stageCountdownStart > autostagePreDelay)
                {
                    if (InverseStageFiresDecoupler(vessel.currentStage - 1, vessel))
                    {
                        //if we decouple things, delay the next stage a bit to avoid exploding the debris
                        lastStageTime = vesselState.time;
                    }

                    if (!this.vessel.isActiveVessel)
                    {
                        this.currentActiveVessel = FlightGlobals.ActiveVessel;
                        Log.info($"Mechjeb Autostage: Switching from {0} to vessel {1} to stage", FlightGlobals.ActiveVessel.name, this.vessel.name);

                        this.remoteStagingStatus = RemoteStagingState.WaitingFocus;
                        FlightGlobals.ForceSetActiveVessel(this.vessel);
                    }
                    else
                    {
                        Log.info("Mechjeb Autostage: Executing next stage on {0}", FlightGlobals.ActiveVessel.name);

                        if (remoteStagingStatus == RemoteStagingState.Disabled)
                        {
                            StageManager.ActivateNextStage();
                        }
                        else if (remoteStagingStatus == RemoteStagingState.FocusFinished)
                        {
                            StageManager.ActivateNextStage();
                            FlightGlobals.ForceSetActiveVessel(currentActiveVessel);
                            Log.info("Mechjeb Autostage: Has switching back to {0} ", FlightGlobals.ActiveVessel.name);
                            this.remoteStagingStatus = RemoteStagingState.Disabled;
                        }
                    }
                    countingDown = false;

                    if (autostagingOnce)
                    {
                        users.Clear();
                    }
                }
            }
            else
            {
                countingDown        = true;
                stageCountdownStart = vesselState.time;
            }
        }
예제 #25
0
        public void FixedUpdate()
        {
            if (!FlightGlobals.ready)
            {
                return;
            }

            LoadDelayedModules();

            CheckControlledVessel(); //make sure our onFlyByWire callback is registered with the right vessel

            if (this != vessel.GetMasterMechJeb() || (HighLogic.LoadedSceneIsFlight && !vessel.isActiveVessel))
            {
                wasMasterAndFocus = false;
            }

            if (this != vessel.GetMasterMechJeb())
            {
                return;
            }
            Profiler.BeginSample("MechJebCore.FixedUpdate");

            if (!wasMasterAndFocus && (HighLogic.LoadedSceneIsEditor || vessel.isActiveVessel))
            {
                if (HighLogic.LoadedSceneIsFlight && (lastFocus != null) && lastFocus.loaded && (lastFocus.GetMasterMechJeb() != null))
                {
                    Log.info("Focus changed! Forcing {0} to save", lastFocus.vesselName);
                    lastFocus.GetMasterMechJeb().OnSave(null);
                }

                // Clear the modules cache
                ClearModulesCache();

                OnLoad(null); // Force Global reload

                wasMasterAndFocus = true;
                lastFocus         = vessel;
            }

            if (vessel == null)
            {
                Profiler.EndSample();
                return; //don't run ComputerModules' OnFixedUpdate in editor
            }

            Profiler.BeginSample("vesselState");
            ready = vesselState.Update(vessel);
            Profiler.EndSample();

            foreach (ComputerModule module in GetComputerModules <ComputerModule>())
            {
                Profiler.BeginSample(module.profilerName);
                try
                {
                    if (module.enabled)
                    {
                        module.OnFixedUpdate();
                    }
                }
                catch (Exception e)
                {
                    Log.err(e, "module {0} threw an exception in OnFixedUpdate: {1}", module.GetType().Name, e);
                }
                Profiler.EndSample();
            }
            Profiler.EndSample();
        }
예제 #26
0
 // wiring for launchStarted
 public void OnLaunch(EventReport report)
 {
     launchStarted = vesselState.time;
     Log.dbg("[MechJebModuleAscentAutopilot] LaunchStarted = {0}", launchStarted);
 }
예제 #27
0
        public override void OnSave(ConfigNode sfsNode)
        {
            //we have nothing worth saving if we're outside the editor or flight scenes:
            if (!(HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight))
            {
                return;
            }

            // Only Masters can save
            if (this != vessel.GetMasterMechJeb())
            {
                return;
            }

            //KSP calls OnSave *before* OnLoad when the first command pod is created in the editor.
            //Defend against saving empty settings.
            if (unorderedComputerModules.Count == 0)
            {
                return;
            }

            // .23 added a call to OnSave for undocking/decoupling vessel before they are properly init ...
            if (HighLogic.LoadedSceneIsFlight && vessel != null && vessel.vesselName == null)
            {
                return;
            }

            Profiler.BeginSample("MechJebCore.OnSave");
            try
            {
                //Add any to-be-loaded modules so they get saved properly
                Profiler.BeginSample("MechJebCore.OnSave.LoadDelayedModules");
                LoadDelayedModules();

                // base.OnSave(sfsNode); //is this necessary?
                Profiler.EndSample();
                Profiler.BeginSample("MechJebCore.OnSave.ConfigNode");
                ConfigNode local  = new ConfigNode("MechJebLocalSettings");
                ConfigNode type   = new ConfigNode("MechJebTypeSettings");
                ConfigNode global = new ConfigNode("MechJebGlobalSettings");
                Profiler.EndSample();
                Profiler.BeginSample("MechJebCore.OnSave.loop");
                foreach (ComputerModule module in GetComputerModules <ComputerModule>())
                {
                    Profiler.BeginSample(module.profilerName);
                    try
                    {
                        string name = module.GetType().Name;
                        module.OnSave(local.AddNode(name), type.AddNode(name), global.AddNode(name));
                    }
                    catch (Exception e)
                    {
                        Log.err(e, "module {0} threw an exception in OnSave: {0} {1}", module.GetType().Name, e);
                    }
                    Profiler.EndSample();
                }

                Log.dbg("OnSave:\n\tLocal: {0}\n\tType: {1}\n\tGlobal: {2}", local, type, global);
                Profiler.EndSample();
                Profiler.BeginSample("MechJebCore.OnSave.sfsNode");
                if (sfsNode != null)
                {
                    sfsNode.nodes.Add(local);
                }
                Profiler.EndSample();
                Profiler.BeginSample("MechJebCore.OnSave.vesselName");
                // The EDITOR => FLIGHT transition is annoying to handle. OnDestroy is called when HighLogic.LoadedSceneIsEditor is already false
                // So we don't save in that case, which is not that bad since nearly nothing use vessel settings in the editor.
                if (vessel != null || (HighLogic.LoadedSceneIsEditor && EditorLogic.fetch != null))
                {
                    string vesselName = (HighLogic.LoadedSceneIsEditor && EditorLogic.fetch ? EditorLogic.fetch.shipNameField.text : vessel.vesselName);
                    vesselName = string.Join("_", vesselName.Split(Path.GetInvalidFileNameChars())); // Strip illegal char from the filename
                    type.Save(IOUtils.GetFilePathFor(this.GetType(), "mechjeb_settings_type_" + vesselName + ".cfg"));
                }
                Profiler.EndSample();
                Profiler.BeginSample("MechJebCore.OnSave.global");
                if (lastFocus == vessel)
                {
                    global.Save(IOUtils.GetFilePathFor(this.GetType(), "mechjeb_settings_global.cfg"));
                }
                Profiler.EndSample();
            }
            catch (Exception e)
            {
                Log.err(e, "MechJeb caught exception in core OnSave: {0}", e);
            }
            Profiler.EndSample();
        }
예제 #28
0
        // Incorporates a new simulation result into the simulation data set and calculate a new semi deployment multiplier. If the data set has a poor correlation, then it might just leave the mutiplier. If the correlation becomes positive then it will clear the dataset and start again.
        public void AddResult(ReentrySimulation.Result newResult)
        {
            // if this result is the same as the old result, then it is not new!
            if (newResult.multiplierHasError)
            {
                if (lastErrorResult != null)
                {
                    if (newResult.id == lastErrorResult.id)
                    {
                        return;
                    }
                }
                lastErrorResult = newResult;
            }
            else
            {
                if (lastResult != null)
                {
                    if (newResult.id == lastResult.id)
                    {
                        return;
                    }
                }
                lastResult = newResult;
            }

            // What was the overshoot for this new result?
            double overshoot = newResult.GetOvershoot(this.autoPilot.core.target.targetLatitude, this.autoPilot.core.target.targetLongitude);

            Log.dbg("overshoot: {0:0.00} multiplier: {1:0.0000} hasError:{2}", overshoot, newResult.parachuteMultiplier, newResult.multiplierHasError);

            // Add the new result to the linear regression
            regression.Add(overshoot, newResult.parachuteMultiplier);

            // What is the correlation coefficent of the data. If it is weak a correlation then we will dismiss the dataset and use it to change the current multiplier
            correlation = regression.CorrelationCoefficent;
            if (correlation > -0.2) // TODO this is the best value to test for non-correlation?
            {
                // If the correlation is less that 0 then we will give up controlling the parachutes and throw away the dataset. Also check that we have got several bits of data, just in case we get two datapoints that are badly correlated.
                if (correlation > 0 && this.regression.dataSetSize > 5)
                {
                    ClearData();
                    Log.dbg("Giving up control of the parachutes as the data does not correlate: {0}", correlation);
                }
                else
                {
                    Log.dbg("Ignoring the simulation dataset because the correlation is not significant enough: {0}", correlation);
                }
            }
            else
            {
                // How much data is there? If just one datapoint then we need to slightly vary the multplier to avoid doing exactly the same multiplier again and getting a divide by zero!. If there is just two then we will not update the multiplier as we can't conclude much from two points of data!
                int dataSetSize = regression.dataSetSize;
                if (dataSetSize == 1)
                {
                    this.currentMultiplier *= 0.99999;
                }
                else if (dataSetSize == 2)
                {
                    // Doing nothing
                }
                else
                {
                    // Use the linear regression to give us a new prediciton for when to open the parachutes
                    try
                    {
                        this.currentMultiplier = regression.yIntercept;
                    }
                    catch (Exception)
                    {
                        // If there is not enough data then we expect an exception. However we need to vary the multiplier everso slightly so that we get different data in order to start generating data. This should never happen as we have already checked the size of the dataset.
                        this.currentMultiplier *= 0.99999;
                    }
                }

                // Impose sensible limits on the multiplier
                if (this.currentMultiplier < 1 || double.IsNaN(currentMultiplier))
                {
                    this.currentMultiplier = 1;
                }
                if (this.currentMultiplier > this.maxMultiplier)
                {
                    this.currentMultiplier = this.maxMultiplier;
                }
            }

            return;
        }
예제 #29
0
        public ManeuverParameters OptimizeEjection(double UT_transfer, Orbit initial_orbit, Orbit target, CelestialBody target_body, double UT_arrival, double earliest_UT)
        {
            int N = 0;

            while (true)
            {
                alglib.minlmstate  state;
                alglib.minlmreport rep;

                double[] x     = new double[5];
                double[] scale = new double[5];

                Vector3d exitDV, captureDV;
                CalcLambertDVs(UT_transfer, UT_arrival - UT_transfer, out exitDV, out captureDV);

                var maneuver = ComputeEjectionManeuver(exitDV, origin, UT_transfer);

                x[0] = maneuver.dV.x;
                x[1] = maneuver.dV.y;
                x[2] = maneuver.dV.z;
                x[3] = maneuver.UT + N * initial_orbit.period;

                scale[0] = scale[1] = scale[2] = maneuver.dV.magnitude;
                scale[3] = initial_orbit.period;

                OptimizerData data = new OptimizerData();
                data.initial_orbit = initial_orbit;
                data.target_orbit  = target;
                data.target_body   = target_body;
                data.UT_arrival    = UT_arrival;
                data.failed        = true;

                alglib.minlmcreatev(4, 3, x, 0.001, out state);
                alglib.minlmsetcond(state, 0, 0);
                alglib.minlmsetscale(state, scale);
                alglib.minlmoptimize(state, DistanceToTarget, null, data);
                alglib.minlmresults(state, out x, out rep);

                Log.info("Transfer calculator: termination type = {0}", rep.terminationtype);
                Log.info("Transfer calculator: iteration count = {0}", rep.iterationscount);

                // try again if we failed to intersect the target orbit
                if (data.failed)
                {
                    Log.info("Failed to intersect target orbit");
                    N++;
                }
                // try again in one orbit if the maneuver node is in the past
                else if (x[3] < earliest_UT || data.failed)
                {
                    Log.info("Transfer calculator: maneuver is {0} s too early, trying again in {1} s", (earliest_UT - x[3]), initial_orbit.period);
                    N++;
                }
                else
                {
                    Log.info("from optimizer DV = {0} t = {1} original arrival = {2}", new Vector3d(x[0], x[1], x[2]), x[3], UT_arrival);
                    return(new ManeuverParameters(new Vector3d(x[0], x[1], x[2]), x[3]));
                }
                if (N > 10)
                {
                    throw new OperationException("Ejection Optimization failed; try manual selection");
                }
            }
        }
예제 #30
0
        public override void Drive(FlightCtrlState s) // TODO put the brake in when running out of power to prevent nighttime solar failures on hills, or atleast try to
        {                                             // TODO make distance calculation for 'reached' determination consider the rover and waypoint on sealevel to prevent height differences from messing it up -- should be done now?
            if (orbit.referenceBody != lastBody)
            {
                WaypointIndex = -1; Waypoints.Clear();
            }
            MechJebWaypoint wp = (WaypointIndex > -1 && WaypointIndex < Waypoints.Count ? Waypoints[WaypointIndex] : null);

            var brake = vessel.ActionGroups[KSPActionGroup.Brakes];             // keep brakes locked if they are

            curSpeed = Vector3d.Dot(vesselState.surfaceVelocity, vesselState.forward);

            CalculateTraction();
            speedIntAcc = speedPID.intAccum;

            if (wp != null && wp.Body == orbit.referenceBody)
            {
                if (ControlHeading)
                {
                    heading.val = Math.Round(HeadingToPos(vessel.CoM, wp.Position), 1);
                }
                if (ControlSpeed)
                {
                    var nextWP   = (WaypointIndex < Waypoints.Count - 1 ? Waypoints[WaypointIndex + 1] : (LoopWaypoints ? Waypoints[0] : null));
                    var distance = Vector3.Distance(vessel.CoM, wp.Position);
                    if (wp.Target != null)
                    {
                        distance += (float)(wp.Target.srfSpeed * curSpeed) / 2;
                    }
                    // var maxSpeed = (wp.MaxSpeed > 0 ? Math.Min((float)speed, wp.MaxSpeed) : speed); // use waypoints maxSpeed if set and smaller than set the speed or just stick with the set speed
                    var maxSpeed = (wp.MaxSpeed > 0 ? wp.MaxSpeed : speed);                     // speed used to go towards the waypoint, using the waypoints maxSpeed if set or just stick with the set speed
                    var minSpeed = (wp.MinSpeed > 0 ? wp.MinSpeed :
                                    (nextWP != null ? TurningSpeed((nextWP.MaxSpeed > 0 ? nextWP.MaxSpeed : speed), heading - HeadingToPos(wp.Position, nextWP.Position)) :
                                     (distance - wp.Radius > 50 ? turnSpeed.val : 1)));
                    minSpeed = (wp.Quicksave ? 1 : minSpeed);
                    // ^ speed used to go through the waypoint, using half the set speed or maxSpeed as minSpeed for routing waypoints (all except the last)
                    var newSpeed = Math.Min(maxSpeed, Math.Max((distance - wp.Radius) / curSpeed, minSpeed));              // brake when getting closer
                    newSpeed = (newSpeed > turnSpeed ? TurningSpeed(newSpeed, headingErr) : newSpeed);                     // reduce speed when turning a lot
//					if (LimitAcceleration) { newSpeed = curSpeed + Mathf.Clamp((float)(newSpeed - curSpeed), -1.5f, 0.5f); }
//					newSpeed = tgtSpeed + Mathf.Clamp((float)(newSpeed - tgtSpeed), -Time.deltaTime * 8f, Time.deltaTime * 2f);
                    var radius = Math.Max(wp.Radius, 10);
                    if (distance < radius)
                    {
                        if (WaypointIndex + 1 >= Waypoints.Count)                         // last waypoint
                        {
                            newSpeed = new [] { newSpeed, (distance < radius * 0.8 ? 0 : 1) }.Min();
                            // ^ limit speed so it'll only go from 1m/s to full stop when braking to prevent accidents on moons
                            if (LoopWaypoints)
                            {
                                WaypointIndex = 0;
                            }
                            else
                            {
                                newSpeed = 0;
                                brake    = true;
//								tgtSpeed.force(newSpeed);
                                if (curSpeed < brakeSpeedLimit)
                                {
                                    if (wp.Quicksave)
                                    {
                                        //if (s.mainThrottle > 0) { s.mainThrottle = 0; }
                                        if (FlightGlobals.ClearToSave() == ClearToSaveStatus.CLEAR)
                                        {
                                            WaypointIndex  = -1;
                                            ControlHeading = ControlSpeed = false;
                                            QuickSaveLoad.QuickSave();
                                        }
                                    }
                                    else
                                    {
                                        WaypointIndex  = -1;
                                        ControlHeading = ControlSpeed = false;
                                    }
                                }
                                else
                                {
                                    Log.dbg("Is this even getting called?");
//									WaypointIndex++;
                                }
                            }
                        }
                        else
                        {
                            if (wp.Quicksave)
                            {
                                //if (s.mainThrottle > 0) { s.mainThrottle = 0; }
                                newSpeed = 0;
//								tgtSpeed.force(newSpeed);
                                if (curSpeed < brakeSpeedLimit)
                                {
                                    if (FlightGlobals.ClearToSave() == ClearToSaveStatus.CLEAR)
                                    {
                                        WaypointIndex++;
                                        QuickSaveLoad.QuickSave();
                                    }
                                }
                            }
                            else
                            {
                                WaypointIndex++;
                            }
                        }
                    }
                    brake = brake || ((s.wheelThrottle == 0 || !vessel.isActiveVessel) && curSpeed < brakeSpeedLimit && newSpeed < brakeSpeedLimit);
                    // ^ brake if needed to prevent rolling, hopefully
                    tgtSpeed = (newSpeed >= 0 ? newSpeed : 0);
                }
            }

            if (ControlHeading)
            {
                headingPID.intAccum = Mathf.Clamp((float)headingPID.intAccum, -1, 1);

                double instantaneousHeading = vesselState.rotationVesselSurface.eulerAngles.y;
                headingErr = MuUtils.ClampDegrees180(instantaneousHeading - heading);
                if (s.wheelSteer == s.wheelSteerTrim || FlightGlobals.ActiveVessel != vessel)
                {
                    float limit = (Math.Abs(curSpeed) > turnSpeed ? Mathf.Clamp((float)((turnSpeed + 6) / Square(curSpeed)), 0.1f, 1f) : 1f);
                    // turnSpeed needs to be higher than curSpeed or it will never steer as much as it could even at 0.2m/s above it
                    // double act = headingPID.Compute(headingErr * headingErr / 10 * Math.Sign(headingErr));
                    double act = headingPID.Compute(headingErr);
                    if (traction >= tractionLimit)
                    {
                        s.wheelSteer = Mathf.Clamp((float)act, -limit, limit);
                        // prevents it from flying above a waypoint and landing with steering at max while still going fast
                    }
                }
            }

            // Brake if there is no controler (Pilot eject from seat)
            if (BrakeOnEject && vessel.GetReferenceTransformPart() == null)
            {
                s.wheelThrottle = 0;
//				vessel.ActionGroups.SetGroup(KSPActionGroup.Brakes, true);
                brake = true;
            }
            else if (ControlSpeed)
            {
                speedPID.intAccum = Mathf.Clamp((float)speedPID.intAccum, -5, 5);

                speedErr = (WaypointIndex == -1 ? speed.val : tgtSpeed) - Vector3d.Dot(vesselState.surfaceVelocity, vesselState.forward);
                if (s.wheelThrottle == s.wheelThrottleTrim || FlightGlobals.ActiveVessel != vessel)
                {
                    float act = (float)speedPID.Compute(speedErr);
                    s.wheelThrottle = Mathf.Clamp(act, -1f, 1f);
                    // s.wheelThrottle = (!LimitAcceleration ? Mathf.Clamp(act, -1, 1) : // I think I'm using these ( ? : ) a bit too much
                    // (traction == 0 ? 0 : (act < 0 ? Mathf.Clamp(act, -1f, 1f) : (lastThrottle + Mathf.Clamp(act - lastThrottle, -0.01f, 0.01f)) * (traction < tractionLimit ? -1 : 1))));
//						(lastThrottle + Mathf.Clamp(act, -0.01f, 0.01f)));
#if DEBUG
                    Log.dbg("Wheel Throttle {0}", s.wheelThrottle + Mathf.Clamp(act, -0.01f, 0.01f));
#endif
                    if (curSpeed < 0 & s.wheelThrottle < 0)
                    {
                        s.wheelThrottle = 0;
                    }                                                                                    // don't go backwards
                    if (Mathf.Sign(act) + Mathf.Sign(s.wheelThrottle) == 0)
                    {
                        s.wheelThrottle = Mathf.Clamp(act, -1f, 1f);
                    }
                    if (speedErr < -1 && StabilityControl && Mathf.Sign(s.wheelThrottle) + Math.Sign(curSpeed) == 0)                       // StabilityControl && traction > 50 &&
////						vessel.ActionGroups.SetGroup(KSPActionGroup.Brakes, true);
                    {
                        brake = true;
//						foreach (Part p in wheels) {
//							if (p.GetModule<ModuleWheels.ModuleWheelDamage>().stressPercent >= 0.01) { // #TODO needs adaptive braking
//								brake = false;
//								break;
//							}
//						}
                    }
////					else if (!StabilityControl || traction <= 50 || speedErr > -0.2 || Mathf.Sign(s.wheelThrottle) + Mathf.Sign((float)curSpeed) != 0) {
////						vessel.ActionGroups.SetGroup(KSPActionGroup.Brakes, (GameSettings.BRAKES.GetKey() && vessel.isActiveVessel));
////					}
                    lastThrottle = Mathf.Clamp(s.wheelThrottle, -1, 1);
                }
            }

            if (StabilityControl)
            {
                if (!core.attitude.users.Contains(this))
                {
                    core.attitude.users.Add(this);
//					line.enabled = true;
                }
//				float scale = Vector3.Distance(FlightCamera.fetch.mainCamera.transform.position, vessel.CoM) / 900f;
//				line.SetPosition(0, vessel.CoM);
//				line.SetPosition(1, vessel.CoM + hit.normal * 5);
//				line.SetWidth(0, scale + 0.1f);
                var fSpeed = (float)curSpeed;
//				if (Mathf.Abs(fSpeed) >= turnSpeed * 0.75) {
                Vector3 fwd = (Vector3)(traction > 0 ?                 // V when the speed is low go for the vessels forward, else with a bit of velocity
//				                        ((Mathf.Abs(fSpeed) <= turnSpeed ? vesselState.forward : vesselState.surfaceVelocity / 4) - vessel.transform.right * s.wheelSteer) * Mathf.Sign(fSpeed) :
//				                        // ^ and then add the steering
                                        vesselState.forward * 4 - vessel.transform.right * s.wheelSteer * Mathf.Sign(fSpeed) : // and then add the steering
                                        vesselState.surfaceVelocity);                                                          // in the air so follow velocity
                Vector3.OrthoNormalize(ref norm, ref fwd);
                var quat = Quaternion.LookRotation(fwd, norm);

//				if (traction > 0 || speed <= turnSpeed) {
//					var u = new Vector3(0, 1, 0);
//
//					var q = FlightGlobals.ship_rotation;
//					var q_s = quat;
//
//					var q_u = new Quaternion(u.x, u.y, u.z, 0);
//					var a = Quaternion.Dot(q, q_s * q_u);
//					var q_qs = Quaternion.Dot(q, q_s);
//					var b = (a == 0) ? Math.Sign(q_qs) : (q_qs / a);
//					var g = b / Mathf.Sqrt((b * b) + 1);
//					var gu = Mathf.Sqrt(1 - (g * g)) * u;
//					var q_d = new Quaternion() { w = g, x = gu.x, y = gu.y, z = gu.z };
//					var n = q_s * q_d;
//
//					quat = n;
//				}
                if (vesselState.torqueAvailable.sqrMagnitude > 0)
                {
                    core.attitude.attitudeTo(quat, AttitudeReference.INERTIAL, this);
                }
//				}
            }

            if (BrakeOnEnergyDepletion)
            {
                var batteries  = vessel.Parts.FindAll(p => p.Resources.Contains(PartResourceLibrary.ElectricityHashcode) && p.Resources.Get(PartResourceLibrary.ElectricityHashcode).flowState);
                var energyLeft = batteries.Sum(p => p.Resources.Get(PartResourceLibrary.ElectricityHashcode).amount) / batteries.Sum(p => p.Resources.Get(PartResourceLibrary.ElectricityHashcode).maxAmount);
                var openSolars = vessel.mainBody.atmosphere &&                 // true if in atmosphere and there are breakable solarpanels that aren't broken nor retracted
                                 vessel.FindPartModulesImplementing <ModuleDeployableSolarPanel>().FindAll(p => p.isBreakable && p.deployState != ModuleDeployablePart.DeployState.BROKEN &&
                                                                                                           p.deployState != ModuleDeployablePart.DeployState.RETRACTED).Count > 0;

                if (openSolars && energyLeft > 0.99)
                {
                    vessel.FindPartModulesImplementing <ModuleDeployableSolarPanel>().FindAll(p => p.isBreakable &&
                                                                                              p.deployState == ModuleDeployablePart.DeployState.EXTENDED).ForEach(p => p.Retract());
                }

                if (energyLeft < 0.05 && Math.Sign(s.wheelThrottle) + Math.Sign(curSpeed) != 0)
                {
                    s.wheelThrottle = 0;
                }                                                                                                                        // save remaining energy by not using it for acceleration
                if (openSolars || energyLeft < 0.03)
                {
                    tgtSpeed = 0;
                }

                if (curSpeed < brakeSpeedLimit && (energyLeft < 0.05 || openSolars))
                {
                    brake = true;
                }

                if (curSpeed < 0.1 && energyLeft < 0.05 && !waitingForDaylight &&
                    vessel.FindPartModulesImplementing <ModuleDeployableSolarPanel>().FindAll(p => p.deployState == ModuleDeployablePart.DeployState.EXTENDED).Count > 0)
                {
                    waitingForDaylight = true;
                }
            }

//			brake = brake && (s.wheelThrottle == 0); // release brake if the user or AP want to drive
            if (s.wheelThrottle != 0 && (Math.Sign(s.wheelThrottle) + Math.Sign(curSpeed) != 0 || curSpeed < 1))
            {
                brake = false;                 // the AP or user want to drive into the direction of momentum so release the brake
            }

            if (vessel.isActiveVessel)
            {
                if (GameSettings.BRAKES.GetKeyUp())
                {
                    brake = false;                     // release the brakes if the user lets go of them
                }
                if (GameSettings.BRAKES.GetKey())
                {
                    brake = true;                     // brake if the user brakes and we aren't about to flip
                }
            }

            tractionLimit = (double)Mathf.Clamp((float)tractionLimit, 0, 100);
            vessel.ActionGroups.SetGroup(KSPActionGroup.Brakes, brake && (StabilityControl && (ControlHeading || ControlSpeed) ? traction >= tractionLimit : true));
            // only let go of the brake when losing traction if the AP is driving, otherwise assume the player knows when to let go of it
            // also to not constantly turn off the parking brake from going over a small bump
            if (brake && curSpeed < 0.1)
            {
                s.wheelThrottle = 0;
            }
        }