コード例 #1
0
        private void ToggleGear()
        {
            List <Part> partsList = EditorLogic.SortedShipList;

            foreach (Part p in partsList)
            {
                if (p.Modules.Contains <ModuleWheelDeployment>())
                {
                    ModuleWheelDeployment l = p.Modules.GetModule <ModuleWheelDeployment>();
                    l.ActionToggle(new KSPActionParam(KSPActionGroup.Gear,
                                                      gearToggle ? KSPActionType.Activate : KSPActionType.Deactivate));
                }

                if (p.Modules.Contains("FSwheel"))
                {
                    PartModule m      = p.Modules["FSwheel"];
                    MethodInfo method =
                        m.GetType().GetMethod("animate", BindingFlags.Instance | BindingFlags.NonPublic);
                    if (method == null)
                    {
                        FARLogger.Error("FSwheel does not have method 'animate");
                    }
                    else
                    {
                        method.Invoke(m, gearToggle ? new object[] { "Deploy" } : new object[] { "Retract" });
                    }
                }

                if (p.Modules.Contains("FSBDwheel"))
                {
                    PartModule m      = p.Modules["FSBDwheel"];
                    MethodInfo method =
                        m.GetType().GetMethod("animate", BindingFlags.Instance | BindingFlags.NonPublic);
                    if (method == null)
                    {
                        FARLogger.Error("FSBDwheel does not have method 'animate");
                    }
                    else
                    {
                        method.Invoke(m, gearToggle ? new object[] { "Deploy" } : new object[] { "Retract" });
                    }
                }

                // ReSharper disable once InvertIf
                if (p.Modules.Contains("KSPWheelAdjustableGear"))
                {
                    PartModule m      = p.Modules["KSPWheelAdjustableGear"];
                    MethodInfo method = m.GetType().GetMethod("deploy", BindingFlags.Instance | BindingFlags.Public);
                    try
                    {
                        if (method == null)
                        {
                            FARLogger.Error("KSPWheelAdjustableGear does not have method 'animate");
                        }
                        else
                        {
                            method.Invoke(m, null);
                        }
                    }
                    catch (Exception e)
                    {
                        //we just catch and print this ourselves to allow things to continue working, since there seems to be a bug in KSPWheels as of this writing
                        FARLogger.Exception(e);
                    }
                }
            }

            gearToggle = !gearToggle;
        }
コード例 #2
0
 public LandingGearEC(ModuleWheelDeployment landingGear, double extra_Deploy)
 {
     this.landingGear  = landingGear;
     this.extra_Deploy = extra_Deploy;
 }
コード例 #3
0
ファイル: SteamShip.cs プロジェクト: bssthu/SteamGauges-Fork
        //This method is called periodically by the main thread,
        //then it decides if it is time to start a new thread or not
        public static void update()
        {
            now = Time.time;
            //double lc = lastCharge;
            //float lt = lastTime;
            //lastTime = now;
            double dt = TimeWarp.fixedDeltaTime;

            //Debug.Log("Starting simulation at " + Math.Round(now, 3));
            isReadable = false;
            //Reset some stuff
            ship              = FlightGlobals.ActiveVessel;
            body              = ship.mainBody;
            _gravity          = FlightGlobals.getGeeForceAtPosition(ship.CoM).magnitude;
            _electricCharge   = 0;
            _electricMax      = 0;
            _evamaxfuel       = 0;
            _evafuel          = 0;
            _liquidFuel       = 0;
            _liquidMax        = 0;
            _monoFuel         = 0;
            _monoMax          = 0;
            _airin            = 0;
            _airreq           = 0;
            _mass             = 0;
            _max_thrust       = 0;
            _cur_thrust       = 0;
            _isp              = 0;
            _engineaccel      = 0;
            _enginedecel      = 0;
            _max_temp_percent = 0;
            _max_temp_actual  = 0;
            double _best_temp     = 0;
            double _best_ablation = 0;

            _ablation = 1;
            _tarpos   = new Vector3();


//            int cur_stage = ship.currentStage;
//Debug.Log("##########################");
//Debug.Log("Current Stage: "+cur_stage);
            //Part loop - look at all the parts and modules for stuff.
            foreach (Part P in ship.parts)
            {
                /*Debug.Log("-------------------------------");
                *  Debug.Log(P.name);
                *  Debug.Log("inStageIndex: " + P.inStageIndex);
                *  Debug.Log("inv Stage: " + P.inverseStage);
                *  Debug.Log("Manual Stage Offset: " + P.manualStageOffset);
                *  Debug.Log("Computed Stage: " + (P.inverseStage + P.manualStageOffset));
                *  Debug.Log("Original Stage: " + P.originalStage);
                *  //Debug.Log("Stage After: " + P.stageAfter);
                *  //Debug.Log("Stage Before: " + P.stageBefore);
                *  Debug.Log("Stage Offset: " + P.stageOffset); */

                //Temperature
                //First, look at internal temps
                _best_temp = P.temperature / P.maxTemp;
                if (_best_temp > _max_temp_percent)
                {
                    _max_temp_percent = _best_temp;
                    _max_temp_actual  = P.temperature;
                }
                //Now look at skin temps
                _best_temp = P.skinTemperature / P.skinMaxTemp;
                if (_best_temp > _max_temp_percent)
                {
                    _max_temp_percent = _best_temp;
                    _max_temp_actual  = P.skinTemperature;
                }

                //if (P.temperature/P.maxTemp > .5) Debug.Log(P.partInfo.title + " Temp: " + (int)P.temperature + "⁰/" + (int)P.maxTemp + "⁰");



                //Vessel Mass
                if (P.physicalSignificance == Part.PhysicalSignificance.FULL)
                {
                    _mass += P.mass;
                    _mass += P.GetResourceMass();
                }
                //Resource loop
                foreach (PartResource pr in P.Resources)
                {
                    if (pr.resourceName.Equals("ElectricCharge"))
                    {
                        _electricCharge += pr.amount;
                        _electricMax    += pr.maxAmount;
                    }
                    else if (pr.resourceName.Equals("LiquidFuel"))
                    {
                        _liquidFuel += pr.amount;
                        _liquidMax  += pr.maxAmount;
                    }
                    else if (pr.resourceName.Equals("MonoPropellant"))
                    {
                        _monoFuel += pr.amount;
                        _monoMax  += pr.maxAmount;
                    }
                    else if (pr.resourceName.Equals("Ablator"))
                    {
                        _best_ablation = pr.amount / pr.maxAmount;
                        if (_best_ablation < _ablation)
                        {
                            _ablation = _best_ablation;
                        }
                    }
                }
                //Module loop
                foreach (PartModule pm in P.Modules)
                {
                    if (pm is KerbalEVA)
                    {
                        KerbalEVA eva = pm as KerbalEVA;
                        _evafuel    = eva.Fuel;
                        _evamaxfuel = eva.FuelCapacity;
                    }
                    else if (pm is ModuleWheelDeployment)
                    {
                        _gearstate = 0; //Gear up
                        ModuleWheelDeployment mlg = pm as ModuleWheelDeployment;
                        if (mlg.stateString.Equals("Deployed"))
                        {
                            _gearstate = 3;
                        }
                        if (mlg.stateString.StartsWith("Deploying"))
                        {
                            _gearstate = 2;
                        }
                        if (mlg.stateString.StartsWith("Retracting"))
                        {
                            _gearstate = 1;
                        }
                    }
                    else if (pm is ModuleEngines)
                    {
                        ModuleEngines ME = pm as ModuleEngines;
                        if (ME.engineShutdown || !ME.EngineIgnited) //We don't care about engines that aren't ready to fire
                        {
                            continue;
                        }
                        if (1 / ME.engineAccelerationSpeed > _engineaccel)
                        {
                            _engineaccel = 1 / ME.engineAccelerationSpeed;
                        }
                        if (1 / ME.engineDecelerationSpeed > _enginedecel)
                        {
                            _enginedecel = 1 / ME.engineDecelerationSpeed;
                        }
                        //Thrust/ISP
                        float thrust = ME.maxThrust * ME.thrustPercentage * 0.01f;

                        _isp        += thrust / ME.atmosphereCurve.Evaluate((float)ship.staticPressurekPa);
                        _max_thrust += thrust;
                        _cur_thrust += ME.finalThrust;  //I think this is the actual thrust being produced by each engine
                        //Intake Air
                        foreach (Propellant Pro in ME.propellants)
                        {
                            if (Pro.name.Equals("IntakeAir"))
                            {
                                _airreq += Pro.currentRequirement;
                            }
                        }
                        //Overheat stuff
                        //if (!ME.flameout)
                        //{
                        //    double temp = P.temperature / P.maxTemp;
                        //    if (temp > _max_temp) _max_temp = temp;
                        //}
                    }
                    else if (pm is ModuleEnginesFX)  //The newer engines all use this instead of ModuleEngines
                    {
                        ModuleEnginesFX MFX = pm as ModuleEnginesFX;
                        if (MFX.engineShutdown || !MFX.EngineIgnited)
                        {
                            continue;
                        }
                        //Thrust/ISP
                        float thrust = MFX.maxThrust * MFX.thrustPercentage * 0.01f;
                        _isp        += thrust / MFX.atmosphereCurve.Evaluate((float)ship.staticPressurekPa);
                        _max_thrust += thrust;
                        _cur_thrust += MFX.finalThrust;
                        //Overheat stuff
                        //if (!MFX.flameout)
                        //{
                        //    double temp = P.temperature / P.maxTemp;
                        //    if (temp > _max_temp) _max_temp = temp;
                        //}
                        if (1 / MFX.engineAccelerationSpeed > _engineaccel)
                        {
                            _engineaccel = 1 / MFX.engineAccelerationSpeed;
                        }
                        if (1 / MFX.engineDecelerationSpeed > _enginedecel)
                        {
                            _enginedecel = 1 / MFX.engineDecelerationSpeed;
                        }
                        foreach (Propellant Pro in MFX.propellants)
                        {
                            if (Pro.name.Equals("IntakeAir"))
                            {
                                _airreq += Pro.currentRequirement;
                            }
                        }
                    }
                    else if (pm is ModuleResourceIntake)
                    {
                        ModuleResourceIntake MRI = pm as ModuleResourceIntake;
                        if (MRI.intakeEnabled && MRI.resourceName.Equals("IntakeAir"))
                        {
                            _airin += MRI.airFlow * dt; //Using dt courtesy of FAR
                        }
                    }
                }
            }
            _isp = _max_thrust / _isp;    //Complete the average isp

            if (_engineaccel == 0)
            {
                _engineaccel = 2;                       //Default in case the engines don't define it
            }
            //Debug.Log("Engine Acceleration: " + Math.Round(_engineaccel, 2));

            //Electric charge rate
            if (now - lastTime > 0.1)
            {
                _chargeRate = (_electricCharge - lastCharge) / (now - lastTime);
                lastCharge  = _electricCharge;
                lastTime    = now;
            }
            //Clamp to +30/-30
            if (_chargeRate > 30)
            {
                _chargeRate = 30;
            }
            if (_chargeRate < -30)
            {
                _chargeRate = -30;
            }

            //Maneuver node burn calculations
            //dv = Isp * ln (m0 / m1)
            //e^(dv/ISP) = m0/m1
            //m1 = m0/e^(dv/ISP)
            //mass flow = thrust/isp
            try
            {
                ManeuverNode myNode    = FlightGlobals.ActiveVessel.patchedConicSolver.maneuverNodes.ToArray()[0];
                double       deltaVRem = myNode.GetBurnVector(ship.orbit).magnitude; //Remaining ΔV in the burn, per maneuver node
                //Debug.Log("dV: " + Math.Round(deltaVRem, 1));
                //Debug.Log("Mass: " + Math.Round(_mass, 1) + " ISP: " + Math.Round(_isp, 1));
                _final_mass = _mass / Math.Pow(Math.E, (deltaVRem / (_isp * 9.82)));                   //Mass after burn   Changed from 9.821
                //Debug.Log("Final Mass: " + Math.Round(_final_mass, 1));
                _mass_rate = _max_thrust / (_isp * 9.82);                                              //Mass flow rate
                //Debug.Log("Mass Rate: " + Math.Round(_mass_rate, 3));
                //Debug.Log("Burn Mass: " + Math.Round(_mass - _final_mass, 1));
                _burn_time = (_mass - _final_mass);// / _mass_rate;                                         //Time it takes for this burn
                //Debug.Log("Burn Mass: " + _burn_time);
                _burn_time = _burn_time / _mass_rate;
                //Debug.Log("Burn Time: " + _burn_time);
                _burn_time += 2d;
                //_burn_time += _engineaccel + _enginedecel;                                             //Factor in slow throttles
            }
            catch
            {
                _burn_time  = 0;
                _final_mass = 0;
                _mass_rate  = 0;
            }

            //Heading, courtesy of MechJeb

            /*Vector3d CoM, up, north, east, forward, rootPartPos;
             * Quaternion rotationSurface, rotationVesselSurface;
             * CoM = ship.findWorldCenterOfMass();
             * up = (CoM - body.position).normalized;
             * Rigidbody rigidBody = ship.rootPart.rigidbody;
             * if (rigidBody != null) rootPartPos = rigidBody.position;
             * north = Vector3d.Exclude(up, (body.position + body.transform.up * (float)body.Radius) - CoM).normalized;
             * east = body.getRFrmVel(CoM).normalized;
             * forward = ship.GetTransform().up;
             * rotationSurface = Quaternion.LookRotation(north, up);
             * rotationVesselSurface = Quaternion.Inverse(Quaternion.Euler(90, 0, 0) * Quaternion.Inverse(ship.GetTransform().rotation) * rotationSurface);
             * _heading = rotationVesselSurface.eulerAngles.y; */
            _heading = FlightGlobals.ship_heading;
            //Debug.Log("FlightGlobals Heading: " + FlightGlobals.ship_heading + " MechJeb Heading: " + _heading);   //good
            //Debug.Log("Ship temp: " + FlightGlobals.ship_temp);    //not useful
            //Debug.Log("Get dV: " + FlightGlobals.ActiveVessel.GetDeltaV());    //nothing
            //Debug.Log("Surface Height: " + FlightGlobals.ActiveVessel.GetHeightFromSurface() + " Terrain Height: "+FlightGlobals.ActiveVessel.GetHeightFromTerrain()); //not useful
            //Debug.Log("GetMass(): " + FlightGlobals.ActiveVessel.GetTotalMass()+" myMass: "+_mass);    //good
            //Debug.Log("IAS: " + FlightGlobals.ActiveVessel.indicatedAirSpeed + " M: " + FlightGlobals.ActiveVessel.mach);  //good
            //Waypoint distance code
            //Uses the Spherical Law of Cosines from http://www.movable-type.co.uk/scripts/latlong.html
            NavWaypoint nW = NavWaypoint.fetch; //The active nav waypoint

            FinePrint.WaypointManager WM;
            WM = FinePrint.WaypointManager.Instance();
            if (nW != null && nW.IsActive)
            {
                //Convert these all to radians to do actual math on them.
                double Aa = ship.latitude * Math.PI / 180;
                double Ao = ship.longitude * Math.PI / 180;
                double Ba = nW.Latitude * Math.PI / 180;
                double Bo = nW.Longitude * Math.PI / 180;
                _nav_dist = Math.Acos(Math.Sin(Aa) * Math.Sin(Ba) + Math.Cos(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao)) * FlightGlobals.currentMainBody.Radius;  //used to be 600000
                double y = Math.Sin(Bo - Ao) * Math.Cos(Ba);
                double x = Math.Cos(Aa) * Math.Sin(Ba) - Math.Sin(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao);
                double b = Math.Atan2(y, x) * 180 / Math.PI; //Converted to degrees.
                _nav_heading = (b + 360f) % 360f;            //Convert to 0-360 from -180 to 180
            }
            else if (_nav_waypoint != 0)
            {
                //defaults
                _nav_heading = -1;
                _nav_dist    = -1;
                //now try actually calculating things
                int count = 0;
                foreach (FinePrint.Waypoint W in WM.Waypoints)
                {
                    //we clearly only care about waypoints on this planet
                    if (W.celestialName.Equals(body.name))
                    {
                        count++;
                        if (count == _nav_waypoint)
                        {
                            //Convert these all to radians to do actual math on them.
                            double Aa = ship.latitude * Math.PI / 180;
                            double Ao = ship.longitude * Math.PI / 180;
                            double Ba = W.latitude * Math.PI / 180;
                            double Bo = W.longitude * Math.PI / 180;
                            _nav_dist = Math.Acos(Math.Sin(Aa) * Math.Sin(Ba) + Math.Cos(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao)) * FlightGlobals.currentMainBody.Radius;
                            double y = Math.Sin(Bo - Ao) * Math.Cos(Ba);
                            double x = Math.Cos(Aa) * Math.Sin(Ba) - Math.Sin(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao);
                            double b = Math.Atan2(y, x) * 180 / Math.PI; //Converted to degrees.
                            _nav_heading = (b + 360f) % 360f;            //Convert to 0-360 from -180 to 180
                        }
                    }
                }
            }
            else
            {
                _nav_dist    = -1;    //Both of these at -1 implies no nav data
                _nav_heading = -1f;
            }

            //Pitch, yaw, roll

            /*NavBall ball = FlightUIController.fetch.GetComponentInChildren<NavBall>();
             * Quaternion vesselRot = Quaternion.Inverse(ball.relativeGymbal);
             * pitch = (vesselRot.eulerAngles.x > 180) ? (360 - vesselRot.eulerAngles.x) : -vesselRot.eulerAngles.x;
             * roll = (vesselRot.eulerAngles.z > 180) ? (360 - vesselRot.eulerAngles.z) : -vesselRot.eulerAngles.z;
             * yaw = vesselRot.eulerAngles.y;    //Heading, more or less
             * _rel_yaw = AngleAroundNormal(ship.GetObtVelocity(), ship.ReferenceTransform.up, ship.ReferenceTransform.forward);
             * _rel_pitch = AngleAroundNormal(ship.GetObtVelocity(), ship.ReferenceTransform.up, ship.ReferenceTransform.right);
             *
             * //Polars
             * if (pitch > 0f)
             * {
             *  Quaternion polarRot = Quaternion.Euler(90, 0, 0) * vesselRot;
             *  polar_yaw = (polarRot.eulerAngles.y + 180f) % 360f - 180f;
             *  polar_pitch = (polarRot.eulerAngles.x + 180f) % 360f - 180f;
             *  polar_roll = polarRot.eulerAngles.z;
             * }
             * else
             * {
             *  Quaternion polarRot = Quaternion.Euler(-90, 0, 0) * vesselRot;
             *  polar_yaw = (polarRot.eulerAngles.y + 180f) % 360f - 180f;
             *  polar_pitch = (polarRot.eulerAngles.x + 180f) % 360f - 180f;
             *  polar_roll = polarRot.eulerAngles.z;
             * }*/

            //Airspeed stuff
            getAirspeedInfo(ship, out _mach, out _tas, out _eas);

            //Max Altitude
            if (ship.Landed)
            {
                max_altitude = ship.altitude;               //Reset max altitude upon landing/takeoff
            }
            if (ship.altitude > max_altitude)
            {
                max_altitude = ship.altitude;
            }

            //RA
            double terrain = ship.terrainAltitude;

            if (terrain < 0)
            {
                terrain = 0;
            }
            _ra = (long)(body.GetAltitude(ship.CoM) - terrain);

            //In orbit?
            inOrbit = false;
            //Minimum periapsis of 5km
            if (ship.orbit.PeA > 5000)
            {
                //Make sure our orbit is out of the atmosphere, if there is one.
                if (body.atmosphere)
                {
                    if (ship.orbit.PeA > body.atmosphereDepth)  //is this the same as max altitude?
                    {
                        inOrbit = true;
                    }
                }
                else
                {
                    inOrbit = true;
                }
            }

            //Closest approach and time, target distance and speed
            if (FlightGlobals.fetch.VesselTarget != null)
            {
                Orbit    TO   = FlightGlobals.fetch.VesselTarget.GetOrbit();
                Vector3d aPos = ship.ReferenceTransform.position;                                   //Control source's position
                Vector3d tPos = _tarpos = FlightGlobals.fetch.VesselTarget.GetTransform().position; //Rough distance
                if (FlightGlobals.fetch.VesselTarget is ModuleDockingNode)                          //Use more precise distance
                {
                    ModuleDockingNode targetDockingPort = FlightGlobals.fetch.VesselTarget as ModuleDockingNode;
                    tPos = _tarpos = targetDockingPort.controlTransform.position;
                }
                // MechJeb guidance targets _don't have an orbit_!
                else if (TO != null)
                {
                    // This is in the wrong coordinate system for _tarpos, and only usable for distance
                    aPos = ship.GetOrbit().pos;
                    tPos = TO.pos;
                }
                _target_dist = Vector3d.Distance(aPos, tPos);
            }
            else
            {
                _target_dist = 0;
            }

            var target = FlightGlobals.fetch.VesselTarget;

            // MechJeb guidance targets _don't have an orbit!
            // Also, landed targets often have invalid orbit that causes error spam and lag.
            if (target != null && target.GetOrbit() != null)
            {
                Orbit O  = ship.GetOrbit();
                Orbit TO = target.GetOrbit();

                double t = Planetarium.GetUniversalTime();
                Func <double, Vector3d> targetPosAt = (d => TO.getPositionAtUT(t + d));

                // For landed targets the orbit doesn't make sense, so calculate planet rotation
                if (target.GetVessel() != null && target.GetVessel().LandedOrSplashed)
                {
                    Vessel        ves   = target.GetVessel();
                    CelestialBody vbody = ves.mainBody;
                    Vector3d      pos   = vbody.GetRelSurfacePosition(ves.latitude, ves.longitude, ves.altitude);

                    targetPosAt = delegate(double d) {
                        double angle = vbody.rotates ? d * 360.0 / vbody.rotationPeriod : 0;
                        return(vbody.position + QuaternionD.AngleAxis(angle, Vector3d.down) * pos);
                    };
                }

                //I chunck my orbit into 100 pieces, and find between which two chuncks the minimum occurs...
                double period   = O.period;
                double bestDist = double.MaxValue;
                double bestTime = 0;

                for (double d = 0; d < period; d += period / 100)    //Look at 100 places around the orbit
                {
                    if (d == 0)
                    {
                        continue;           //skip the first time
                    }
                    double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d));
                    if (dist < bestDist)
                    {
                        bestDist = dist;
                        bestTime = d;
                    }
                }
                //Now, do it again, but over a small section of the orbit centered on the above
                double start = bestTime - (period / 100);
                double end   = bestTime + (period / 100);
                for (double d = start; d < end; d += period / 1000)    //Look at 100 places within this chunck of orbit
                {
                    if (d == start)
                    {
                        continue;               //Skip the first time
                    }
                    double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d));
                    if (dist < bestDist)
                    {
                        bestDist = dist;
                        bestTime = d;
                    }
                }
                //And one last time, which is probably overkill
                start = bestTime - (period / 1000);
                end   = bestTime + (period / 1000);
                for (double d = start; d < end; d += period / 10000)    //Look at 100 places within this chunck of orbit
                {
                    if (d == start)
                    {
                        continue;               //For ease of computation
                    }
                    double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d));
                    if (dist < bestDist)
                    {
                        bestDist = dist;
                        bestTime = d;   //previous time is my start time
                    }
                }
                _closest_time     = bestTime;
                _closest_approach = bestDist;
            }
            else
            {
                _closest_approach = 0;
                _closest_time     = 0;
            }

            //Time to Impact calculations
            double vertspeed = FlightGlobals.ActiveVessel.verticalSpeed;

            if (!ship.Landed && vertspeed < 0 && ship.orbit.PeA <= 0 && !ship.mainBody.atmosphere)
            {
                double Vf = Math.Sqrt((vertspeed * vertspeed) + 2 * _ra * _gravity);
                //t = 2d/(Vi+Vf)
                _tti = (Math.Abs(Vf) - Math.Abs(vertspeed)) / _gravity;
                _tti++;  //So you hit the ground at 0, not 1 second after 0
            }
            else
            {
                _tti = 0;
            }
            //Suicide Altitude calculations
            if (ship.Landed || ship.orbit.PeA > 0 || body.atmosphere)
            {
                _sa = -1;  //Not calculating this
            }
            else
            {
                _sa = 0;
                double avgG = body.gravParameter / ((body.Radius + ship.terrainAltitude) * (body.Radius + ship.terrainAltitude));
                avgG += FlightGlobals.getGeeForceAtPosition(ship.CoM).magnitude;
                avgG /= 2;
                //Debug.Log("Average G: " + Math.Round(avgG, 2));
                double vdv = Math.Sqrt((2 * avgG * _ra) + (ship.verticalSpeed * ship.verticalSpeed));
                //Debug.Log("Vertical Delta V: " + Math.Round(vdv, 1));
                //Altitude Fraction = (Vertical dv ^2) / (2 * 1000 * Thrust (kN))
                double altFrac = (vdv * vdv) / (2 * 1000 * _max_thrust);
                //Debug.Log("Altitude Fraction: " + Math.Round(altFrac, 2));
                //m-avg = (m0 + (m0 / e ^ (dv / (Isp * 9.82)))) / 2
                double avgMass = (_mass / Math.Pow(Math.E, (vdv / (_isp * 9.82))));
                avgMass = (_mass + avgMass) / 2;
                //Debug.Log("Average mass: " + Math.Round(avgMass, 1));
                _sa = (long)Math.Round(altFrac * avgMass * 1000);
                //Debug.Log("Suicide Altitude: " + Math.Round(_sa));
            }

            //Cleanup
            isReadable = true;
            //Debug.Log("Thread simulation complete at " + Math.Round(Time.time, 3));
        }