public double RCSDeltaVVacuum()
        {
            // Use the average specific impulse of all RCS parts.
            double totalIsp     = 0;
            int    numThrusters = 0;

            double monopropMass = vessel.TotalResourceMass("MonoPropellant");

            foreach (ModuleRCS pm in VesselExtensions.GetModules <ModuleRCS>(vessel))
            {
                totalIsp += pm.atmosphereCurve.Evaluate(0);
                numThrusters++;
            }

            double m0 = (HighLogic.LoadedSceneIsEditor)
                ? EditorLogic.SortedShipList.Where(
                p => p.physicalSignificance != Part.PhysicalSignificance.NONE).Sum(p => p.TotalMass())
                : vesselState.mass;
            double m1 = m0 - monopropMass;

            if (numThrusters == 0 || m1 <= 0)
            {
                return(0);
            }
            double isp = totalIsp / numThrusters;

            return(isp * 9.81 * Math.Log(m0 / m1));
        }
        public double RCSDeltaVVacuum()
        {
            // Use the average specific impulse of all RCS parts.
            double totalIsp     = 0;
            double monopropMass = 0;
            int    numThrusters = 0;

            List <Part> parts = (HighLogic.LoadedSceneIsEditor)
                ? EditorLogic.SortedShipList
                : (vessel == null) ? new List <Part>() : vessel.Parts;

            foreach (Part p in parts)
            {
                foreach (PartResource r in p.Resources)
                {
                    if (r.amount > 0 && r.info.name == "MonoPropellant")
                    {
                        monopropMass += r.amount * r.info.density;
                    }
                }
            }

            foreach (ModuleRCS pm in VesselExtensions.GetModules <ModuleRCS>(vessel))
            {
                totalIsp += pm.atmosphereCurve.Evaluate(0);
                numThrusters++;
            }

            double m0 = (HighLogic.LoadedSceneIsEditor)
                ? EditorLogic.SortedShipList.Where(
                p => p.physicalSignificance != Part.PhysicalSignificance.NONE).Sum(p => p.TotalMass())
                : vesselState.mass;
            double m1 = m0 - monopropMass;

            if (numThrusters == 0 || m1 <= 0)
            {
                return(0);
            }
            double isp = totalIsp / numThrusters;

            return(isp * 9.81 * Math.Log(m0 / m1));
        }
Beispiel #3
0
        // The list of throttles is ordered under the assumption that you iterate
        // over the vessel as follows:
        //      foreach part in vessel.parts:
        //          foreach rcsModule in part.Modules.OfType<ModuleRCS>:
        //              ...
        // Note that rotation balancing is not supported at the moment.
        public void GetThrottles(Vessel vessel, VesselState state, Vector3 direction,
                                 out double[] throttles, out List <RCSSolver.Thruster> thrustersOut)
        {
            thrustersOut = callerThrusters;

            Vector3 rotation = Vector3.zero;

            var rcsBalancer = VesselExtensions.GetMasterMechJeb(vessel).rcsbal;

            // Update vessel info if needed.
            CheckVessel(vessel, state);

            Vector3      dir = direction.normalized;
            RCSSolverKey key = new RCSSolverKey(ref dir, rotation);

            if (thrusters.Count == 0)
            {
                throttles = double0;
            }
            else if (direction == Vector3.zero)
            {
                throttles = originalThrottles;
            }
            else if (results.TryGetValue(key, out throttles))
            {
                cacheHits++;
            }
            else
            {
                // This task hasn't been calculated. We'll handle that here.
                // Meanwhile, TryGetValue() will have set 'throttles' to null, but
                // we'll make it a 0-element array instead to avoid null checks.
                cacheMisses++;
                throttles = double0;

                if (pending.Contains(key))
                {
                    // We've submitted this key before, so we need to check the
                    // results queue.
                    while (resultsQueue.Count > 0)
                    {
                        SolverResult sr = (SolverResult)resultsQueue.Dequeue();
                        results[sr.key] = sr.throttles;
                        pending.Remove(sr.key);
                        if (sr.key == key)
                        {
                            throttles = sr.throttles;
                        }
                    }
                }
                else
                {
                    // This task was neither calculated nor pending, so we've never
                    // submitted it. Do so!
                    pending.Add(key);
                    tasks.Enqueue(new SolverTask(key, dir, rotation));
                    workEvent.Set();
                }
            }

            // Return a copy of the array to make sure ours isn't modified.
            throttles = (double[])throttles.Clone();
        }
Beispiel #4
0
        private void CheckVessel(Vessel vessel, VesselState state)
        {
            bool changed = false;

            var rcsBalancer = VesselExtensions.GetMasterMechJeb(vessel).rcsbal;

            if (vessel.parts.Count != lastPartCount)
            {
                lastPartCount = vessel.parts.Count;
                changed       = true;
            }

            // Make sure all thrusters are still enabled, because if they're not,
            // our calculations will be wrong.
            for (int i = 0; i < thrusters.Count; i++)
            {
                if (!thrusters[i].partModule.isEnabled)
                {
                    changed = true;
                    break;
                }
            }

            // Likewise, make sure any previously-disabled RCS modules are still
            // disabled.
            for (int i = 0; i < lastDisabled.Count; i++)
            {
                var pm = lastDisabled[i];
                if (pm.isEnabled)
                {
                    changed = true;
                    break;
                }
            }

            // See if the CoM has moved too much.
            Rigidbody rootPartBody = vessel.rootPart.rigidbody;

            if (rootPartBody != null)
            {
                // But how much is "too much"? Well, it probably has something to do
                // with the ship's moment of inertia (MoI). Let's say the distance
                // 'd' that the CoM is allowed to shift without a reset is:
                //
                //      d = moi * x + c
                //
                // where 'moi' is the magnitude of the ship's moment of inertia and
                // 'x' and 'c' are tuning parameters to be determined.
                //
                // Using a few actual KSP ships, I burned RCS fuel (or moved fuel
                // from one tank to another) to see how far the CoM could shift
                // before the the rotation error on translation became annoying.
                // I came up with roughly:
                //
                //      d         moi
                //      0.005    2.34
                //      0.04    11.90
                //      0.07    19.96
                //
                // I then halved each 'd' value, because we'd like to address this
                // problem -before- it becomes annoying. Least-squares linear
                // regression on the (moi, d/2) pairs gives the following (with
                // adjusted R^2 = 0.999966):
                //
                //      moi = 542.268 d + 1.00654
                //      d = (moi - 1) / 542
                //
                // So the numbers below have some basis in reality. =)

                // Assume MoI magnitude is always >=2.34, since that's all I tested.
                comErrorThreshold = (Math.Max(state.MoI.magnitude, 2.34) - 1) / 542;

                Vector3 comState   = state.CoM;
                Vector3 rootPos    = state.rootPartPos;
                Vector3 com        = WorldToVessel(vessel, comState - rootPos);
                double  thisComErr = (lastCoM - com).magnitude;
                maxComError     = Math.Max(maxComError, thisComErr);
                _comError.value = thisComErr;
                if (_comError > comErrorThreshold)
                {
                    lastCoM = com;
                    changed = true;
                }
            }

            if (!changed)
            {
                return;
            }

            // Something about the vessel has changed. We need to reset everything.

            lastDisabled.Clear();

            // ModuleRCS has no originalThrusterPower attribute, so we have
            // to explicitly reset it.
            ResetThrusterForces();

            // Rebuild the list of thrusters.
            var ts = new List <RCSSolver.Thruster>();

            for (int index = 0; index < vessel.parts.Count; index++)
            {
                Part p = vessel.parts[index];
                foreach (ModuleRCS pm in p.Modules.OfType <ModuleRCS>())
                {
                    if (!pm.isEnabled)
                    {
                        // Keep track of this module so we'll know if it's enabled.
                        lastDisabled.Add(pm);
                    }
                    else if (p.Rigidbody != null && !pm.isJustForShow)
                    {
                        Vector3 pos = VesselRelativePos(state.CoM, vessel, p);

                        // Create a single RCSSolver.Thruster for this part. This
                        // requires some assumptions about how the game's RCS code will
                        // drive the individual thrusters (which we can't control).

                        Vector3[]  thrustDirs   = new Vector3[pm.thrusterTransforms.Count];
                        Quaternion rotationQuat = Quaternion.Inverse(vessel.GetTransform().rotation);
                        for (int i = 0; i < pm.thrusterTransforms.Count; i++)
                        {
                            thrustDirs[i] = (rotationQuat * -pm.thrusterTransforms[i].up).normalized;
                        }

                        ts.Add(new RCSSolver.Thruster(pos, thrustDirs, p, pm));
                    }
                }
            }
            callerThrusters.Clear();
            originalThrottles = new double[ts.Count];
            zeroThrottles     = new double[ts.Count];
            for (int i = 0; i < ts.Count; i++)
            {
                originalThrottles[i] = ts[i].originalForce;
                zeroThrottles[i]     = 0;
                callerThrusters.Add(ts[i]);
            }

            thrusters = ts;
            ClearResults();
        }