private void Init(Vessel v, ReentrySimulation.SimCurves _simCurves, double startTime, int limitChutesStage)
        {
            totalMass = 0;

            var oParts = v.Parts;
            count = oParts.Count;

            simCurves = _simCurves;

            if (parts.Capacity < count)
                parts.Capacity = count;

            for (int i=0; i < count; i++)
            {
                SimulatedPart simulatedPart = null;
                bool special = false;
                for (int j = 0; j < oParts[i].Modules.Count; j++)
                {
                    ModuleParachute mp = oParts[i].Modules[j] as ModuleParachute;
                    if (mp != null && v.mainBody.atmosphere)
                    {
                        special = true;
                        simulatedPart = SimulatedParachute.Borrow(mp, simCurves, startTime, limitChutesStage);
                    }
                }
                if (!special)
                {
                    simulatedPart = SimulatedPart.Borrow(oParts[i], simCurves);
                }

                parts.Add(simulatedPart);
                totalMass += simulatedPart.totalMass;
            }
        }
 public static SimulatedParachute Borrow(ModuleParachute mp, ReentrySimulation.SimCurves simCurve, double startTime, int limitChutesStage)
 {
     SimulatedParachute part = pool.Borrow();
     part.Init(mp.part, simCurve);
     part.Init(mp, startTime, limitChutesStage);
     return part;
 }
Example #3
0
        protected virtual void Set(Part p, ReentrySimulation.SimCurves _simCurves)
        {
            Rigidbody rigidbody = p.rb;

            totalMass = rigidbody == null ? 0 : rigidbody.mass; // TODO : check if we need to use this or the one without the childMass
            shieldedFromAirstream = p.ShieldedFromAirstream;

            noDrag = rigidbody == null && !PhysicsGlobals.ApplyDragToNonPhysicsParts;
            hasLiftModule = p.hasLiftModule;
            bodyLiftMultiplier = p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier;

            simCurves = _simCurves;

            cubes = new DragCubeList();
            CopyDragCubesList(p.DragCubes, cubes);

            // Rotation to convert the vessel space vesselVelocity to the part space vesselVelocity
            vesselToPart = Quaternion.LookRotation(p.vessel.GetTransform().InverseTransformDirection(p.transform.forward), p.vessel.GetTransform().InverseTransformDirection(p.transform.up)).Inverse();

            //DragCubeMultiplier = PhysicsGlobals.DragCubeMultiplier;
            //DragMultiplier = PhysicsGlobals.DragMultiplier;


            //if (p.dragModel != Part.DragModel.CUBE)
            //    MechJebCore.print(p.name + " " + p.dragModel);

            //oPart = p;

        }
        private void Set(Vessel v, ReentrySimulation.SimCurves _simCurves, double startTime, int limitChutesStage)
        {
            totalMass = 0;

            var oParts = v.Parts;
            count = oParts.Count;

            simCurves = _simCurves;

            if (parts.Capacity < count)
                parts.Capacity = count;

            for (int i=0; i < count; i++)
            {
                SimulatedPart simulatedPart = null;
                bool special = false;
                for (int j = 0; j < oParts[i].Modules.Count; j++)
                {
                    if (oParts[i].Modules[j] is ModuleParachute)
                    {
                        special = true;
                        simulatedPart = SimulatedParachute.New((ModuleParachute)oParts[i].Modules[j], simCurves, startTime, limitChutesStage);
                    }
                }
                if (!special)
                {
                    simulatedPart = SimulatedPart.New(oParts[i], simCurves);
                }

                parts.Add(simulatedPart);
                totalMass += simulatedPart.totalMass;
            }
        }
 new static public SimulatedParachute New(ModuleParachute mp, ReentrySimulation.SimCurves simCurve, double startTime, int limitChutesStage)
 {
     SimulatedParachute part = new SimulatedParachute();
     part.Set(mp.part, simCurve);
     part.Set(mp, startTime, limitChutesStage);
     return part;
 }
Example #6
0
        protected void StartSimulation()
        {
            simulationRunning = true;

            stopwatch.Start(); //starts a timer that times how long the simulation takes

            Orbit             patch = GetReenteringPatch() ?? orbit;
            ReentrySimulation sim   = new ReentrySimulation(patch, patch.StartUT, vesselState.massDrag / vesselState.mass, descentSpeedPolicy, endAltitudeASL, vesselState.limitedMaxThrustAccel);

            //Run the simulation in a separate thread
            ThreadPool.QueueUserWorkItem(RunSimulation, sim);
        }
Example #7
0
        protected void StartSimulation(bool addParachuteError)
        {
            double altitudeOfPreviousPrediction         = 0;
            double parachuteMultiplierForThisSimulation = this.parachuteSemiDeployMultiplier;

            if (addParachuteError)
            {
                errorSimulationRunning = true;
                errorStopwatch.Start(); //starts a timer that times how long the simulation takes
            }
            else
            {
                simulationRunning = true;
                stopwatch.Start(); //starts a timer that times how long the simulation takes
            }
            Orbit patch = GetReenteringPatch() ?? orbit;

            // Work out what the landing altitude was of the last prediction, and use that to pass into the next simulation
            if (result != null)
            {
                if (result.outcome == ReentrySimulation.Outcome.LANDED && result.body != null)
                {
                    altitudeOfPreviousPrediction = result.endASL; // Note that we are caling GetResult here to force the it to calculate the endASL, if it has not already done this. It is not allowed to do this previously as we are only allowed to do it from this thread, not the reentry simulation thread.
                }
            }
            // Is this a simulation run with errors added? If so then add some error to the parachute multiple
            if (addParachuteError)
            {
                parachuteMultiplierForThisSimulation *= 1d + (random.Next(1000000) - 500000d) / 10000000d;
            }

            // The curves used for the sim are not thread safe so we need a copy used only by the thread
            ReentrySimulation.SimCurves simCurves = ReentrySimulation.SimCurves.Borrow(patch.referenceBody);


            //if (descentSpeedPolicy != null)
            //    print(vesselState.limitedMaxThrustAccel.ToString("F2") + " " + descentSpeedPolicy.MaxAllowedSpeed(vesselState.CoM - mainBody.position, vesselState.surfaceVelocity).ToString("F2"));

            SimulatedVessel   simVessel = SimulatedVessel.Borrow(vessel, simCurves, patch.StartUT, core.landing.enabled && deployChutes ? limitChutesStage : -1);
            ReentrySimulation sim       = ReentrySimulation.Borrow(patch, patch.StartUT, simVessel, simCurves, descentSpeedPolicy, decelEndAltitudeASL, vesselState.limitedMaxThrustAccel, parachuteMultiplierForThisSimulation, altitudeOfPreviousPrediction, addParachuteError, dt, Time.fixedDeltaTime, maxOrbits, noSkipToFreefall);

            //MechJebCore.print("Sim ran with dt=" + dt.ToString("F3"));

            //Run the simulation in a separate thread
            ThreadPool.QueueUserWorkItem(RunSimulation, sim);
            //RunSimulation(sim);
        }
Example #8
0
        protected void RunSimulation(object o)
        {
            ReentrySimulation sim = (ReentrySimulation)o;

            ReentrySimulation.Result newResult = sim.RunSimulation();

            result = newResult;

            //see how long the simulation took
            stopwatch.Stop();
            long millisecondsToCompletion = stopwatch.ElapsedMilliseconds;

            stopwatch.Reset();

            //set the delay before the next simulation
            millisecondsBetweenSimulations = 2 * millisecondsToCompletion;

            //start the stopwatch that will count off this delay
            stopwatch.Start();

            simulationRunning = false;
        }
Example #9
0
        protected void StartSimulation()
        {
            simulationRunning = true;

            stopwatch.Start(); //starts a timer that times how long the simulation takes

            List <SimulatedParachute> usableChutes = new List <SimulatedParachute>();

            foreach (ModuleParachute p in vesselState.parachutes)
            {
                if (deployChutes && p.part.inverseStage >= limitChutesStage)
                {
                    usableChutes.Add(new SimulatedParachute(p));
                }
            }


            Orbit             patch = GetReenteringPatch() ?? orbit;
            ReentrySimulation sim   = new ReentrySimulation(patch, patch.StartUT, vesselState.massDrag / vesselState.mass, usableChutes, vesselState.mass, descentSpeedPolicy, endAltitudeASL, vesselState.limitedMaxThrustAccel);

            //Run the simulation in a separate thread
            ThreadPool.QueueUserWorkItem(RunSimulation, sim);
        }
Example #10
0
 public static SimulatedPart Borrow(Part p, ReentrySimulation.SimCurves simCurve)
 {
     SimulatedPart part = pool.Borrow();
     part.Init(p, simCurve);
     return part;
 }
        protected void StartSimulation(bool addParachuteError)
        {
            double altitudeOfPreviousPrediction = 0;
            double parachuteMultiplierForThisSimulation = this.parachuteSemiDeployMultiplier;

            if (addParachuteError)
            {
                errorSimulationRunning = true;
                errorStopwatch.Start(); //starts a timer that times how long the simulation takes
            }
            else
            {
                simulationRunning = true;
                stopwatch.Start(); //starts a timer that times how long the simulation takes
            }

            Orbit patch = GetReenteringPatch() ?? orbit;

            // Work out what the landing altitude was of the last prediction, and use that to pass into the next simulation
            if(null !=this.result)
            {
                if(result.outcome == ReentrySimulation.Outcome.LANDED &&  null != result.body)
                {
                    altitudeOfPreviousPrediction = this.GetResult().endASL; // Note that we are caling GetResult here to force the it to calculate the endASL, if it has not already done this. It is not allowed to do this previously as we are only allowed to do it from this thread, not the reentry simulation thread.
                }
            }

            // Is this a simulation run with errors added? If so then add some error to the parachute multiple
            if (addParachuteError)
            {
                System.Random random = new System.Random();
                parachuteMultiplierForThisSimulation *= (1d + ((random.Next(1000000) - 500000d) /10000000d));
            }

            // The curves used for the simes are not thread safe so we need a copy used only by the thread
            ReentrySimulation.SimCurves simCurves = new ReentrySimulation.SimCurves(patch.referenceBody);

            SimulatedVessel simVessel = SimulatedVessel.New(vessel, simCurves, patch.StartUT, deployChutes ? limitChutesStage : -1);

            ReentrySimulation sim = new ReentrySimulation(patch, patch.StartUT, simVessel, simCurves, descentSpeedPolicy, decelEndAltitudeASL, vesselState.limitedMaxThrustAccel, parachuteMultiplierForThisSimulation, altitudeOfPreviousPrediction, addParachuteError, dt, Time.fixedDeltaTime);

            //MechJebCore.print("Sim ran with dt=" + dt.ToString("F3"));

            //Run the simulation in a separate thread
            ThreadPool.QueueUserWorkItem(RunSimulation, sim);
            //RunSimulation(sim);
        }
        protected void StartSimulation(bool addParachuteError)
        {
            double altitudeOfPreviousPrediction         = 0;
            double parachuteMultiplierForThisSimulation = this.parachuteSemiDeployMultiplier;

            if (addParachuteError)
            {
                errorSimulationRunning = true;
                errorStopwatch.Start(); //starts a timer that times how long the simulation takes
            }
            else
            {
                simulationRunning = true;
                stopwatch.Start(); //starts a timer that times how long the simulation takes
            }

            Orbit patch = GetReenteringPatch() ?? orbit;

            // Work out a mass for the total ship, a DragMass for everything except the parachutes that will be used (including the stowed parachutes that will not be used) and list of parchutes that will be used.
            double totalMass = 0;
            double dragMassExcludingUsedParachutes = 0;
            List <SimulatedParachute> usableChutes = new List <SimulatedParachute>();

            foreach (Part p in vessel.parts)
            {
                if (p.IsPhysicallySignificant())
                {
                    bool   partIsParachute = false;
                    double partDrag        = 0;
                    double partMass        = p.TotalMass();

                    totalMass += partMass;

                    // Is this part a parachute?
                    foreach (PartModule pm in p.Modules)
                    {
                        if (!pm.isEnabled)
                        {
                            continue;
                        }

                        if (pm is ModuleParachute)
                        {
                            ModuleParachute chute = (ModuleParachute)pm;
                            partIsParachute = true;
                            // This is a parachute, but is it one that will be used in the landing / rentry simulation?
                            if (deployChutes && p.inverseStage >= limitChutesStage)
                            {
                                // This chute will be used in the simualtion. Add it to the list of useage parachutes.
                                usableChutes.Add(new SimulatedParachute(chute, patch.StartUT));
                            }
                            else
                            {
                                partDrag = p.maximum_drag;
                            }
                        }
                    }

                    if (false == partIsParachute)
                    {
                        // Part is not a parachute. Just use its drag value.
                        partDrag = p.maximum_drag;
                    }

                    dragMassExcludingUsedParachutes += partDrag * partMass;
                }
            }

            // Work out what the landing altitude was of the last prediction, and use that to pass into the next simulation
            if (null != this.result)
            {
                if (result.outcome == ReentrySimulation.Outcome.LANDED && null != result.body)
                {
                    altitudeOfPreviousPrediction = this.GetResult().endASL; // Note that we are caling GetResult here to force the it to calculate the endASL, if it has not already done this. It is not allowed to do this previously as we are only allowed to do it from this thread, not the reentry simulation thread.
                }
            }

            // Is this a simulation run with errors added? If so then add some error to the parachute multiple
            if (addParachuteError)
            {
                System.Random random = new System.Random();
                parachuteMultiplierForThisSimulation *= ((double)1 + ((double)(random.Next(1000000) - 500000) / (double)10000000));
            }

            ReentrySimulation sim = new ReentrySimulation(patch, patch.StartUT, dragMassExcludingUsedParachutes, usableChutes, totalMass, descentSpeedPolicy, decelEndAltitudeASL, vesselState.limitedMaxThrustAccel, parachuteMultiplierForThisSimulation, altitudeOfPreviousPrediction, addParachuteError, dt);

            //Run the simulation in a separate thread
            ThreadPool.QueueUserWorkItem(RunSimulation, sim);
        }
Example #13
0
        private void RunSimulation(object o)
        {
            ReentrySimulation sim = (ReentrySimulation)o;

            try
            {
                ReentrySimulation.Result newResult = sim.RunSimulation();

                lock (readyResults)
                {
                    readyResults.Enqueue(newResult);
                }

                if (newResult.multiplierHasError)
                {
                    //see how long the simulation took
                    errorStopwatch.Stop();
                    long millisecondsToCompletion = errorStopwatch.ElapsedMilliseconds;
                    lastErrorSimTime  = millisecondsToCompletion * 0.001;
                    lastErrorSimSteps = newResult.steps;

                    errorStopwatch.Reset();

                    //set the delay before the next simulation
                    millisecondsBetweenErrorSimulations = Math.Min(Math.Max(4 * millisecondsToCompletion, 400), 5);
                    // Note that we are going to run the simulations with error in less often that the real simulations

                    //start the stopwatch that will count off this delay
                    errorStopwatch.Start();
                    errorSimulationRunning = false;
                }
                else
                {
                    //see how long the simulation took
                    stopwatch.Stop();
                    long millisecondsToCompletion = stopwatch.ElapsedMilliseconds;
                    stopwatch.Reset();

                    //set the delay before the next simulation
                    millisecondsBetweenSimulations = Math.Min(Math.Max(2 * millisecondsToCompletion, 200), 5);
                    lastSimTime  = millisecondsToCompletion * 0.001;
                    lastSimSteps = newResult.steps;
                    // Do not wait for too long before running another simulation, but also give the processor a rest.

                    // How long should we set the max_dt to be in the future? Calculate for interationsPerSecond runs per second. If we do not enter the atmosphere, however do not do so as we will complete so quickly, it is not a good guide to how long the reentry simulation takes.
                    if (newResult.outcome == ReentrySimulation.Outcome.AEROBRAKED ||
                        newResult.outcome == ReentrySimulation.Outcome.LANDED)
                    {
                        if (this.variabledt)
                        {
                            dt = newResult.maxdt * (millisecondsToCompletion / 1000d) / (1d / (3d * interationsPerSecond));
                            // There is no point in having a dt that is smaller than the physics frame rate as we would be trying to be more precise than the game.
                            dt = Math.Max(dt, sim.min_dt);
                            // Set a sensible upper limit to dt as well. - in this case 10 seconds
                            dt = Math.Min(dt, 10);
                        }
                    }

                    //Debug.Log("Result:" + this.result.outcome + " Time to run: " + millisecondsToCompletion + " millisecondsBetweenSimulations: " + millisecondsBetweenSimulations + " new dt: " + dt + " Time.fixedDeltaTime " + Time.fixedDeltaTime + "\n" + this.result.ToString()); // Note the endASL will be zero as it has not yet been calculated, and we are not allowed to calculate it from this thread :(

                    //start the stopwatch that will count off this delay
                    stopwatch.Start();
                    simulationRunning = false;
                }
            }
            catch (Exception ex)
            {
                //Debug.Log(string.Format("Exception in MechJebModuleLandingPredictions.RunSimulation\n{0}", ex.StackTrace));
                //Debug.LogException(ex);
                Dispatcher.InvokeAsync(() => Debug.LogException(ex));
            }
            finally
            {
                sim.Release();
            }
        }
        protected void RunSimulation(object o)
        {
            try
            {
                ReentrySimulation        sim       = (ReentrySimulation)o;
                ReentrySimulation.Result newResult = sim.RunSimulation();

                if (newResult.multiplierHasError)
                {
                    // If running the simualtion resulted in an error then just ignore it.
                    if (ReentrySimulation.Outcome.ERROR != newResult.outcome)
                    {
                        errorResult = newResult;
                    }

                    //see how long the simulation took
                    errorStopwatch.Stop();
                    long millisecondsToCompletion = errorStopwatch.ElapsedMilliseconds;

                    errorStopwatch.Reset();

                    //set the delay before the next simulation
                    millisecondsBetweenErrorSimulations = Math.Min(Math.Max(4 * millisecondsToCompletion, 400), 5); // Note that we are going to run the simualtions with error in less often that the real simulations

                    //start the stopwatch that will count off this delay
                    errorStopwatch.Start();
                    errorSimulationRunning = false;
                }
                else
                {
                    // If running the simualtion resulted in an error then just ignore it.
                    if (ReentrySimulation.Outcome.ERROR != newResult.outcome)
                    {
                        result = newResult;
                    }
                    //see how long the simulation took
                    stopwatch.Stop();
                    long millisecondsToCompletion = stopwatch.ElapsedMilliseconds;
                    stopwatch.Reset();

                    //set the delay before the next simulation
                    millisecondsBetweenSimulations = Math.Min(Math.Max(2 * millisecondsToCompletion, 200), 5); // Do not wait for too long before running another simulation, but also give the processor a rest.

                    // How long should we set the max_dt to be in the future? Calculate for interationsPerSecond runs per second. If we do not enter the atmosphere, however do not do so as we will complete so quickly, it is not a good guide to how long the reentry simulation takes.
                    if (newResult.outcome == ReentrySimulation.Outcome.AEROBRAKED || newResult.outcome == ReentrySimulation.Outcome.LANDED)
                    {
                        if (this.variabledt)
                        {
                            dt = (newResult.maxdt * ((double)millisecondsToCompletion / (double)1000)) / ((double)1 / ((double)3 * (double)interationsPerSecond));
                            // There is no point in having a dt that is smaller than the physics frame rate as we would be trying to be more precise than the game.
                            dt = Math.Max(dt, (double)Time.fixedDeltaTime);
                            // Set a sensible upper limit to dt as well. - in this case 10 seconds
                            dt = Math.Min(dt, 10);
                        }
                    }

                    // TODO remove debugging
                    //Debug.Log("Result:" + this.result.outcome + " Time to run: " + millisecondsToCompletion + " millisecondsBetweenSimulations: " + millisecondsBetweenSimulations + " new dt: " + dt + " Time.fixedDeltaTime " + Time.fixedDeltaTime + "\n" + this.result.ToString()); // Note the endASL will be zero as it has not yet been calculated, and we are not allowed to calculate it from this thread :(

                    //start the stopwatch that will count off this delay
                    stopwatch.Start();
                    simulationRunning = false;
                }
            }
            catch (Exception ex)
            {
                Debug.Log("Exception in MechJebModuleLandingPredictions.RunSimulation\n" + ex.StackTrace);
                Debug.LogException(ex);
            }
        }
Example #15
0
 public static SimulatedVessel New(Vessel v, ReentrySimulation.SimCurves simCurves, double startTime, int limitChutesStage)
 {
     SimulatedVessel vessel = new SimulatedVessel();
     vessel.Set(v, simCurves, startTime, limitChutesStage);
     return vessel;
 }
        protected void StartSimulation()
        {
            simulationRunning = true;

            stopwatch.Start(); //starts a timer that times how long the simulation takes

            List<SimulatedParachute> usableChutes = new List<SimulatedParachute>();
            foreach (ModuleParachute p in vesselState.parachutes)
                if (deployChutes && p.part.inverseStage >= limitChutesStage)
                    usableChutes.Add(new SimulatedParachute(p));

            Orbit patch = GetReenteringPatch() ?? orbit;
            ReentrySimulation sim = new ReentrySimulation(patch, patch.StartUT, vesselState.massDrag / vesselState.mass, usableChutes, vesselState.mass, descentSpeedPolicy, endAltitudeASL, vesselState.limitedMaxThrustAccel);

            //Run the simulation in a separate thread
            ThreadPool.QueueUserWorkItem(RunSimulation, sim);
        }
 public static SimulatedVessel Borrow(Vessel v, ReentrySimulation.SimCurves simCurves, double startTime, int limitChutesStage)
 {
     SimulatedVessel vessel = pool.Borrow();
     vessel.Init(v, simCurves, startTime, limitChutesStage);
     return vessel;
 }
 private static void Reset(ReentrySimulation obj)
 {
 }
Example #19
0
 static public SimulatedPart New(Part p, ReentrySimulation.SimCurves simCurve)
 {
     SimulatedPart part = new SimulatedPart();
     part.Set(p, simCurve);
     return part;
 }
        protected void StartSimulation()
        {
            simulationRunning = true;

            stopwatch.Start(); //starts a timer that times how long the simulation takes

            Orbit patch = GetReenteringPatch() ?? orbit;
            ReentrySimulation sim = new ReentrySimulation(patch, patch.StartUT, vesselState.massDrag / vesselState.mass, descentSpeedPolicy, endAltitudeASL, vesselState.limitedMaxThrustAccel);

            //Run the simulation in a separate thread
            ThreadPool.QueueUserWorkItem(RunSimulation, sim);
        }
        // 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);

            //Debug.Log("overshoot: " + overshoot.ToString("F2") + " multiplier: " + newResult.parachuteMultiplier.ToString("F4") + " hasError:" + 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();
                    // Debug.Log("Giving up control of the parachutes as the data does not correlate: " + correlation);
                }
                else
                {
                    // Debug.Log("Ignoring the simulation dataset because the correlation is not significant enough: " + 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;
        }
        protected void StartSimulation(bool addParachuteError)
        {
            double altitudeOfPreviousPrediction = 0;
            double parachuteMultiplierForThisSimulation = this.parachuteSemiDeployMultiplier;

            if (addParachuteError)
            {
                errorSimulationRunning = true;
                errorStopwatch.Start(); //starts a timer that times how long the simulation takes
            }
            else
            {
                simulationRunning = true;
                stopwatch.Start(); //starts a timer that times how long the simulation takes
            }

            // Work out a mass for the total ship, a DragMass for everything except the parachutes that will be used (including the stowed parachutes that will not be used) and list of parchutes that will be used.
            double totalMass =0;
            double dragMassExcludingUsedParachutes = 0;
            List<SimulatedParachute> usableChutes = new List<SimulatedParachute>();

            foreach (Part p in vessel.parts)
            {
                if (p.IsPhysicallySignificant())
                {
                    bool partIsParachute = false;
                    double partDrag =0;
                    double partMass = p.TotalMass();

                    totalMass += partMass;

                    // Is this part a parachute?
                    foreach (PartModule pm in p.Modules)
                    {
                        if (!pm.isEnabled) continue;

                        if (pm is ModuleParachute)
                        {
                            ModuleParachute chute = (ModuleParachute)pm;
                            partIsParachute = true;
                            // This is a parachute, but is it one that will be used in the landing / rentry simulation?
                            if (deployChutes && p.inverseStage >= limitChutesStage)
                            {
                                // This chute will be used in the simualtion. Add it to the list of useage parachutes.
                                usableChutes.Add(new SimulatedParachute(chute));
                            }
                            else
                            {
                                partDrag = p.maximum_drag;
                            }
                        }
                    }

                    if (false == partIsParachute)
                    {
                        // Part is not a parachute. Just use its drag value.
                        partDrag = p.maximum_drag;
                    }

                    dragMassExcludingUsedParachutes += partDrag * partMass;
                }
            }

            Orbit patch = GetReenteringPatch() ?? orbit;

            // Work out what the landing altitude was of the last prediction, and use that to pass into the next simulation
            if(null !=this.result)
            {
                if(result.outcome == ReentrySimulation.Outcome.LANDED &&  null != result.body)
                {
                    altitudeOfPreviousPrediction = this.GetResult().endASL; // Note that we are caling GetResult here to force the it to calculate the endASL, if it has not already done this. It is not allowed to do this previously as we are only allowed to do it from this thread, not the reentry simulation thread.
                }
            }

            // Is this a simulation run with errors added? If so then add some error to the parachute multiple
            if (addParachuteError)
            {
                System.Random random = new System.Random();
                parachuteMultiplierForThisSimulation *= ((double)1 + ((double)(random.Next(1000000) - 500000) / (double)10000000));
            }

            ReentrySimulation sim = new ReentrySimulation(patch, patch.StartUT, dragMassExcludingUsedParachutes, usableChutes, totalMass, descentSpeedPolicy, decelEndAltitudeASL, vesselState.limitedMaxThrustAccel, parachuteMultiplierForThisSimulation, altitudeOfPreviousPrediction, addParachuteError, dt);

            //Run the simulation in a separate thread
            ThreadPool.QueueUserWorkItem(RunSimulation, sim);
        }