Ejemplo n.º 1
0
 /// <summary>
 /// Returns the star the given body orbits
 /// </summary>
 public static KopernicusStar GetNearest(CelestialBody body)
 {
     KopernicusStar nearestStar = Stars.OrderBy(s => Vector3.Distance(body.position, s.sun.position)).First();
     double greatestDistance = 0;
     for (Int32 i = 0; i < KopernicusStar.Stars.Count; i++)
     {
         KopernicusStar star = KopernicusStar.Stars[i];
         double distance = Vector3d.Distance(body.position, star.sun.position);
         if (star.shifter.givesOffLight && distance > greatestDistance)
         {
             greatestDistance = distance;
             nearestStar = star;
         }
     }
     return nearestStar;
 }
Ejemplo n.º 2
0
            void Start()
            {
                _star = GetComponent <KopernicusStar>();
                Mesh mesh = _star.sun.scaledBody.GetComponent <MeshFilter>().sharedMesh;

                _colliders = new OcclusionNetComponent[mesh.vertexCount / Resolution];
                for (Int32 i = 0; i < mesh.vertexCount; i += Resolution)
                {
                    OcclusionNetComponent component = new GameObject().AddComponent <OcclusionNetComponent>();
                    component.transform.parent = _star.transform.parent;
                    component.Body             = _star.sun;
                    component.Target           = _star.target;
                    component.Vertex           = mesh.vertices[i];
                    _colliders[i / Resolution] = component;
                }
            }
Ejemplo n.º 3
0
        private static Double Flux(ModularFlightIntegrator fi, KopernicusStar star)
        {
            // Nullchecks
            try
            {
                if (fi == null)
                {
                    return 0;
                }
                if (fi.Vessel == null || fi.Vessel.state == Vessel.State.DEAD || fi.CurrentMainBody == null)
                {
                    return 0;
                }
                if (star == null)
                {
                    return 0;
                }

                // Get sunVector
                Boolean directSunlight = false;
                Vector3 integratorPosition = fi.transform.position;
                Vector3d scaledSpace = ScaledSpace.LocalToScaledSpace(integratorPosition);
                Vector3 position = star.sun.scaledBody.transform.position;
                Double scale = Math.Max((position - scaledSpace).magnitude, 1);
                Vector3 sunVector = (position - scaledSpace) / scale;
                Ray ray = new Ray(ScaledSpace.LocalToScaledSpace(integratorPosition), sunVector);

                // Get Solar Flux
                Double realDistanceToSun = 0;
                if (!Physics.Raycast(ray, out RaycastHit raycastHit, Single.MaxValue, ModularFlightIntegrator.SunLayerMask))
                {
                    directSunlight = true;
                    realDistanceToSun = scale * ScaledSpace.ScaleFactor - star.sun.Radius;
                }
                else if (raycastHit.transform.GetComponent<ScaledMovement>().celestialBody == star.sun)
                {
                    realDistanceToSun = ScaledSpace.ScaleFactor * raycastHit.distance;
                    directSunlight = true;
                }

                if (directSunlight)
                {
                    return PhysicsGlobals.SolarLuminosity / (12.5663706143592 * realDistanceToSun * realDistanceToSun);
                }

                return 0;
            }
Ejemplo n.º 4
0
            /// <summary>
            /// Small method to handle flux
            /// </summary>
            public static void Flux(ModularFlightIntegrator fi, KopernicusStar star)
            {
                // Nullchecks
                if (fi.Vessel == null || fi.Vessel.state == Vessel.State.DEAD || fi.CurrentMainBody == null)
                {
                    return;
                }

                // Get sunVector
                RaycastHit raycastHit;
                Vector3d   scaledSpace = ScaledSpace.LocalToScaledSpace(fi.IntegratorTransform.position);
                double     scale       = Math.Max((star.sun.scaledBody.transform.position - scaledSpace).magnitude, 1);
                Vector3    sunVector   = (star.sun.scaledBody.transform.position - scaledSpace) / scale;
                Ray        ray         = new Ray(ScaledSpace.LocalToScaledSpace(fi.IntegratorTransform.position), sunVector);

                // Get Body flux
                Vector3d scaleFactor = ((Vector3d)star.sun.scaledBody.transform.position - fi.CurrentMainBody.scaledBody.transform.position) * (double)ScaledSpace.ScaleFactor;

                fi.bodySunFlux = star.sunFlare == fi.CurrentMainBody ? 0 : PhysicsGlobals.SolarLuminosity / Math.PI * 4 * scaleFactor.sqrMagnitude;

                // Get Solar Flux
                double realDistanceToSun   = 0;
                bool   localDirectSunLight = false;

                if (!Physics.Raycast(ray, out raycastHit, Single.MaxValue, ModularFI.ModularFlightIntegrator.SunLayerMask))
                {
                    localDirectSunLight = true;
                    realDistanceToSun   = scale * ScaledSpace.ScaleFactor - star.sun.Radius;
                }
                else if (raycastHit.transform.GetComponent <ScaledMovement>().celestialBody == star.sun)
                {
                    realDistanceToSun   = ScaledSpace.ScaleFactor * raycastHit.distance;
                    localDirectSunLight = true;
                }
                if (localDirectSunLight)
                {
                    fi.solarFlux = PhysicsGlobals.SolarLuminosity / (12.5663706143592 * realDistanceToSun * realDistanceToSun);
                    if (!fi.Vessel.directSunlight)
                    {
                        fi.Vessel.directSunlight = true;
                    }
                }
            }
Ejemplo n.º 5
0
            /// <summary>
            /// Small method to handle flux
            /// </summary>
            public static void Flux(ModularFlightIntegrator fi, KopernicusStar star)
            {
                // Nullchecks
                if (fi.Vessel == null || fi.Vessel.state == Vessel.State.DEAD || fi.CurrentMainBody == null)
                {
                    return;
                }

                // Get sunVector
                RaycastHit raycastHit;

                fi.Vessel.directSunlight = false;
                Vector3d scaledSpace = ScaledSpace.LocalToScaledSpace(fi.IntegratorTransform.position);
                double   scale       = Math.Max((star.sun.scaledBody.transform.position - scaledSpace).magnitude, 1);
                Vector3  sunVector   = (star.sun.scaledBody.transform.position - scaledSpace) / scale;
                Ray      ray         = new Ray(ScaledSpace.LocalToScaledSpace(fi.IntegratorTransform.position), sunVector);

                // Get Body flux
                fi.solarFlux = 0;
                fi.sunDot    = Vector3d.Dot(fi.sunVector, fi.Vessel.upAxis);
                fi.CurrentMainBody.GetAtmoThermalStats(true, FlightIntegrator.sunBody, fi.sunVector, fi.sunDot, fi.Vessel.upAxis, fi.altitude, out fi.atmosphereTemperatureOffset, out fi.bodyEmissiveFlux, out fi.bodyAlbedoFlux);
                Vector3d scaleFactor = ((Vector3d)star.sun.scaledBody.transform.position - fi.CurrentMainBody.scaledBody.transform.position) * (double)ScaledSpace.ScaleFactor;

                // Get Solar Flux
                double realDistanceToSun = 0;

                if (!Physics.Raycast(ray, out raycastHit, Single.MaxValue, ModularFI.ModularFlightIntegrator.SunLayerMask))
                {
                    fi.Vessel.directSunlight = true;
                    realDistanceToSun        = scale * ScaledSpace.ScaleFactor - star.sun.Radius;
                }
                else if (raycastHit.transform.GetComponent <ScaledMovement>().celestialBody == star.sun)
                {
                    realDistanceToSun        = ScaledSpace.ScaleFactor * raycastHit.distance;
                    fi.Vessel.directSunlight = true;
                }
                if (fi.Vessel.directSunlight)
                {
                    fi.solarFlux = PhysicsGlobals.SolarLuminosity / (12.5663706143592 * realDistanceToSun * realDistanceToSun);
                }
            }
Ejemplo n.º 6
0
 /// <summary>
 /// Returns the brightest star near the given body.
 /// </summary>
 public static KopernicusStar GetBrightest(CelestialBody body)
 {
     double greatestLuminosity = 0;
     KopernicusStar BrightestStar = GetNearest(body);
     for (Int32 i = 0; i < KopernicusStar.Stars.Count; i++)
     {
         KopernicusStar star = KopernicusStar.Stars[i];
         double distance = Vector3d.Distance(body.position, star.sun.position);
         double aparentLuminosity = 0;
         if (star.shifter.givesOffLight)
         {
             aparentLuminosity = star.shifter.solarLuminosity * (1 / (distance * distance));
         }
         if (aparentLuminosity > greatestLuminosity)
         {
             greatestLuminosity = aparentLuminosity;
             BrightestStar = star;
         }
     }
     return BrightestStar;
 }
Ejemplo n.º 7
0
            public override void PostCalculateTracking(Boolean trackingLOS, Vector3 trackingDirection)
            {
                // Maximum values
                Double         maxEnergy   = 0;
                Double         maxFlowRate = 0;
                KopernicusStar maxStar     = null;

                // Override layer mask
                planetLayerMask = ModularFlightIntegrator.SunLayerMask;

                // Efficiency modifier
                _efficMult = (temperatureEfficCurve.Evaluate((Single)part.skinTemperature) * timeEfficCurve.Evaluate((Single)((Planetarium.GetUniversalTime() - launchUT) * 1.15740740740741E-05)) * efficiencyMult);
                _flowRate  = 0;
                sunAOA     = 0;

                // Go through all stars
                foreach (KopernicusStar star in KopernicusStar.Stars)
                {
                    // Calculate stuff
                    Vector3       trackDir = (star.sun.transform.position - panelRotationTransform.position).normalized;
                    CelestialBody old      = trackingBody;
                    trackingTransformLocal  = star.sun.transform;
                    trackingTransformScaled = star.sun.scaledBody.transform;
                    trackingLOS             = CalculateTrackingLOS(trackDir, ref blockingObject);
                    trackingTransformLocal  = old.transform;
                    trackingTransformScaled = old.scaledBody.transform;

                    // Calculate sun AOA
                    Single _sunAOA = 0f;
                    if (!trackingLOS)
                    {
                        _sunAOA = 0f;
                        status  = "Blocked by " + blockingObject;
                    }
                    else
                    {
                        status = "Direct Sunlight";
                        if (panelType == PanelType.FLAT)
                        {
                            _sunAOA = Mathf.Clamp(Vector3.Dot(trackingDotTransform.forward, trackDir), 0f, 1f);
                        }
                        else if (panelType != PanelType.CYLINDRICAL)
                        {
                            _sunAOA = 0.25f;
                        }
                        else
                        {
                            Vector3 direction;
                            if (alignType == PanelAlignType.PIVOT)
                            {
                                direction = trackingDotTransform.forward;
                            }
                            else if (alignType != PanelAlignType.X)
                            {
                                direction = alignType != PanelAlignType.Y ? part.partTransform.forward : part.partTransform.up;
                            }
                            else
                            {
                                direction = part.partTransform.right;
                            }
                            _sunAOA = (1f - Mathf.Abs(Vector3.Dot(direction, trackDir))) * 0.318309873f;
                        }
                    }

                    // Calculate distance multiplier
                    Double __distMult = 1;
                    if (!useCurve)
                    {
                        if (!KopernicusStar.SolarFlux.ContainsKey(star.name))
                        {
                            continue;
                        }
                        __distMult = (Single)(KopernicusStar.SolarFlux[star.name] / stockLuminosity);
                    }
                    else
                    {
                        __distMult = powerCurve.Evaluate((star.sun.transform.position - panelRotationTransform.position).magnitude);
                    }

                    // Calculate flow rate
                    Double __flowRate = _sunAOA * _efficMult * __distMult;
                    if (part.submergedPortion > 0)
                    {
                        Double altitudeAtPos = -FlightGlobals.getAltitudeAtPos((Vector3d)secondaryTransform.position, vessel.mainBody);
                        altitudeAtPos = (altitudeAtPos * 3 + part.maxDepth) * 0.25;
                        if (altitudeAtPos < 0.5)
                        {
                            altitudeAtPos = 0.5;
                        }
                        Double num = 1 / (1 + altitudeAtPos * part.vessel.mainBody.oceanDensity);
                        if (part.submergedPortion >= 1)
                        {
                            __flowRate = __flowRate * num;
                        }
                        else
                        {
                            __flowRate = __flowRate * UtilMath.LerpUnclamped(1, num, part.submergedPortion);
                        }
                        status += ", Underwater";
                    }
                    sunAOA += _sunAOA;
                    Double energy = __distMult * _efficMult;
                    if (energy > maxEnergy)
                    {
                        maxFlowRate = __flowRate;
                        maxEnergy   = energy;
                        maxStar     = star;
                    }

                    // Apply the flow rate
                    _flowRate += __flowRate;
                }

                // Sun AOA
                sunAOA   /= relativeSunAOA ? KopernicusStar.Stars.Count : 1;
                _distMult = _flowRate != 0 ? _flowRate / _efficMult / sunAOA : 0;

                // We got the best star to use
                if (maxStar != null && maxStar.sun != trackingBody)
                {
                    if (!manualTracking)
                    {
                        trackingBody = maxStar.sun;
                        GetTrackingBodyTransforms();
                    }
                }

                // Use the flow rate
                flowRate = (Single)(resHandler.UpdateModuleResourceOutputs(_flowRate) * flowMult);
            }
Ejemplo n.º 8
0
        public override void FixedUpdate()
        {
            base.FixedUpdate();
            frameTimer++;
            if (HighLogic.LoadedSceneIsFlight)
            {
                if ((deployState == ModuleDeployablePart.DeployState.EXTENDED))
                {
                    if (frameTimer > (50 * Kopernicus.RuntimeUtility.RuntimeUtility.KopernicusConfig.SolarRefreshRate))
                    {
                        CelestialBody trackingStar = trackingBody;
                        frameTimer = 0;
                        KopernicusStar bestStar  = KopernicusStar.CelestialBodies[trackingStar];
                        Double         totalFlux = 0;
                        Double         totalFlow = 0;
                        flowRate  = 0;
                        _flowRate = 0;
                        Double bestFlux = 0;
                        for (Int32 s = 0; s < KopernicusStar.Stars.Count; s++)
                        {
                            KopernicusStar star = KopernicusStar.Stars[s];
                            // Use this star
                            star.shifter.ApplyPhysics();

                            // Set Tracking Speed to zero
                            Single oldTrackingSpeed = trackingSpeed;
                            trackingSpeed = 0;

                            // Change the tracking body
                            trackingBody = star.sun;
                            GetTrackingBodyTransforms();
                            CalculateTracking();

                            //Calculate flux
                            double starFlux = star.CalculateFluxAt(vessel) * 1360 / PhysicsGlobals.SolarLuminosityAtHome;

                            //Check if star has better flux
                            if (bestFlux < starFlux)
                            {
                                bestFlux = starFlux;
                                bestStar = star;
                            }

                            // Add to TotalFlux and EC tally
                            totalFlux += starFlux;
                            float panelEffectivness = 0;
                            //Now for some fancy atmospheric math
                            float    atmoDensityMult = 1;
                            float    atmoAngleMult   = 1;
                            float    tempMult        = 1;
                            Vector3d localSpace      = ScaledSpace.ScaledToLocalSpace(star.target.position);
                            if (this.vessel.atmDensity > 0)
                            {
                                float   horizonAngle      = (float)Math.Acos(FlightGlobals.currentMainBody.Radius / (FlightGlobals.currentMainBody.Radius + FlightGlobals.ship_altitude));
                                Vector3 horizonVector     = new Vector3(0, Mathf.Sin(Mathf.Deg2Rad * horizonAngle), Mathf.Cos(Mathf.Deg2Rad * horizonAngle));
                                float   sunZenithAngleDeg = Vector3.Angle(FlightGlobals.upAxis, star.sun.position);

                                Double gravAccelParameter = (vessel.mainBody.gravParameter / Math.Pow(vessel.mainBody.Radius + FlightGlobals.ship_altitude, 2));
                                float  massOfAirColumn    = (float)(FlightGlobals.getStaticPressure() / gravAccelParameter);

                                tempMult        = this.temperatureEfficCurve.Evaluate((float)this.vessel.atmosphericTemperature);
                                atmoDensityMult = AtmosphericAttenutationAirMassMultiplier.Evaluate(massOfAirColumn);
                                atmoAngleMult   = AtmosphericAttenutationSolarAngleMultiplier.Evaluate(sunZenithAngleDeg);
                            }

                            panelEffectivness = (chargeRate / 24.4f) / 56.37091313591871f * sunAOA * tempMult * atmoAngleMult * atmoDensityMult;  //56.blabla is a weird constant we use to turn flux into EC
                            totalFlow        += (starFlux * panelEffectivness) / (1360 / PhysicsGlobals.SolarLuminosityAtHome);
                            // Restore Tracking Speed
                            trackingSpeed = oldTrackingSpeed;
                        }
                        // Restore the starting star
                        trackingBody = trackingStar;
                        KopernicusStar.CelestialBodies[trackingStar].shifter.ApplyPhysics();
                        GetTrackingBodyTransforms();
                        CalculateTracking();
                        vessel.solarFlux = totalFlux;
                        //Add to new output
                        flowRate  = (float)totalFlow;
                        _flowRate = totalFlow / chargeRate;
                        resHandler.UpdateModuleResourceOutputs(_flowRate);
                        //caching logic
                        cachedFlowRate  = flowRate;
                        _cachedFlowRate = _flowRate;
                        // Setup next tracking body
                        if ((bestStar != null && bestStar != trackingBody) && (!_manualTracking))
                        {
                            trackingBody = bestStar.sun;
                            GetTrackingBodyTransforms();
                            CalculateTracking();
                        }
                    }
                    else
                    {
                        //inbetween timings logic
                        flowRate  = cachedFlowRate;
                        _flowRate = _cachedFlowRate;
                        resHandler.UpdateModuleResourceOutputs(_flowRate);
                    }

                    //see if tracked star is blocked or not
                    if (sunAOA > 0)
                    {
                        //this ensures the "blocked" GUI option is set right, if we're exposed to you we're not blocked
                        vessel.directSunlight = true;
                    }
                    // Restore The Current Star
                    KopernicusStar.Current.shifter.ApplyPhysics();
                }
            }
            else
            {
                //Packed logic
                flowRate  = cachedFlowRate;
                _flowRate = _cachedFlowRate;
                resHandler.UpdateModuleResourceOutputs(_flowRate);
            }
        }
        public void LatePostCalculateTracking(Boolean trackingLos, Vector3 trackingDirection, int panelId)
        {
            ModuleDeployableSolarPanel SP = SPs[panelId];

            // Maximum values
            Double         maxEnergy = 0;
            KopernicusStar maxStar   = null;

            // Override layer mask
            typeof(ModuleDeployableSolarPanel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(f => f.Name == "planetLayerMask").SetValue(SP, ModularFlightIntegrator.SunLayerMask);

            // Efficiency modifier
            SP._efficMult = SP.temperatureEfficCurve.Evaluate((Single)part.skinTemperature) *
                            SP.timeEfficCurve.Evaluate(
                (Single)((Planetarium.GetUniversalTime() - SP.launchUT) * 1.15740740740741E-05)) *
                            SP.efficiencyMult;
            SP._flowRate = 0;
            SP.sunAOA    = 0;

            // Go through all stars
            Int32 stars = KopernicusStar.Stars.Count;

            for (Int32 i = 0; i < stars; i++)
            {
                KopernicusStar star = KopernicusStar.Stars[i];

                // Calculate stuff
                Transform     sunTransform = star.sun.transform;
                Vector3       trackDir     = (sunTransform.position - SP.panelRotationTransform.position).normalized;
                CelestialBody old          = SP.trackingBody;
                SP.trackingTransformLocal  = sunTransform;
                SP.trackingTransformScaled = star.sun.scaledBody.transform;



                SP.trackingTransformLocal  = old.transform;
                SP.trackingTransformScaled = old.scaledBody.transform;

                // Calculate sun AOA
                Single sunAoa;
                if (!trackingLos)
                {
                    FieldInfo blockingObject     = typeof(ModuleDeployableSolarPanel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(f => f.Name == "blockingObject");
                    string    blockingObjectName = (string)blockingObject.GetValue(SP);
                    SP.status = Localizer.Format("#Kopernicus_UI_PanelBlocked", blockingObjectName);//"Blocked by " + blockingObjectName
                }
                else
                {
                    SP.status = SP_status_DirectSunlight;//"Direct Sunlight"
                }
                if (SP.panelType == ModuleDeployableSolarPanel.PanelType.FLAT)
                {
                    sunAoa = Mathf.Clamp(Vector3.Dot(SP.trackingDotTransform.forward, trackDir), 0f, 1f);
                }
                else if (SP.panelType != ModuleDeployableSolarPanel.PanelType.CYLINDRICAL)
                {
                    sunAoa = 0.25f;
                }
                else
                {
                    Vector3 direction;
                    if (SP.alignType == ModuleDeployableSolarPanel.PanelAlignType.PIVOT)
                    {
                        direction = SP.trackingDotTransform.forward;
                    }
                    else if (SP.alignType != ModuleDeployableSolarPanel.PanelAlignType.X)
                    {
                        direction = SP.alignType != ModuleDeployableSolarPanel.PanelAlignType.Y
                            ? part.partTransform.forward
                            : part.partTransform.up;
                    }
                    else
                    {
                        direction = part.partTransform.right;
                    }

                    sunAoa = (1f - Mathf.Abs(Vector3.Dot(direction, trackDir))) * 0.318309873f;
                }

                // Calculate distance multiplier
                Double distMult;
                if (!SP.useCurve)
                {
                    if (!KopernicusStar.SolarFlux.ContainsKey(star.name))
                    {
                        continue;
                    }

                    distMult = (Single)(KopernicusStar.SolarFlux[star.name] / StockLuminosity);
                }
                else
                {
                    distMult =
                        SP.powerCurve.Evaluate((star.sun.transform.position - SP.panelRotationTransform.position).magnitude);
                }

                // Calculate flow rate
                Double panelFlowRate = sunAoa * SP._efficMult * distMult;
                if (part.submergedPortion > 0)
                {
                    Double altitudeAtPos =
                        -FlightGlobals.getAltitudeAtPos
                        (
                            (Vector3d)(((Transform)typeof(ModuleDeployableSolarPanel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(f => f.Name == "secondaryTransform").GetValue(SP)).position),
                            vessel.mainBody
                        );
                    altitudeAtPos = (altitudeAtPos * 3 + part.maxDepth) * 0.25;
                    if (altitudeAtPos < 0.5)
                    {
                        altitudeAtPos = 0.5;
                    }

                    Double num = 1 / (1 + altitudeAtPos * part.vessel.mainBody.oceanDensity);
                    if (part.submergedPortion >= 1)
                    {
                        panelFlowRate *= num;
                    }
                    else
                    {
                        panelFlowRate *= UtilMath.LerpUnclamped(1, num, part.submergedPortion);
                    }

                    SP.status += ", " + SP_status_Underwater;//Underwater
                }

                SP.sunAOA += sunAoa;
                Double energy = distMult * SP._efficMult;
                if (energy > maxEnergy)
                {
                    maxEnergy = energy;
                    maxStar   = star;
                }

                // Apply the flow rate
                SP._flowRate += panelFlowRate;
            }

            // Sun AOA
            SP.sunAOA   /= _relativeSunAoa ? stars : 1;
            SP._distMult = Math.Abs(SP._flowRate) > 0.01 ? SP._flowRate / SP._efficMult / SP.sunAOA : 0;

            // We got the best star to use
            if (maxStar != null && maxStar.sun != SP.trackingBody)
            {
                if (!_manualTracking)
                {
                    SP.trackingBody = maxStar.sun;
                    SP.GetTrackingBodyTransforms();
                }
            }

            // Use the flow rate
            SP.flowRate = (Single)(SP.resHandler.UpdateModuleResourceOutputs(SP._flowRate) * SP.flowMult);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Runs before <see cref="ModuleDeployableSolarPanel.FixedUpdate"/>.
        /// </summary>
        void FixedUpdate()
        {
            if (HighLogic.LoadedSceneIsFlight)
            {
                for (Int32 n = 0; n < SPs.Length; n++)
                {
                    ModuleDeployableSolarPanel SP = SPs[n];

                    if (SP.deployState == ModuleDeployablePart.DeployState.EXTENDED)
                    {
                        KopernicusStar trackingStar = KopernicusStar.CelestialBodies[SP.trackingBody];

                        Double         bestFlux   = vessel.solarFlux * 1360 / PhysicsGlobals.SolarLuminosityAtHome;
                        KopernicusStar bestStar   = trackingStar;
                        Double         totalFlux  = 0;
                        Single         totalAoA   = SP.sunAOA;
                        Double         _totalFlow = SP._flowRate;
                        Single         totalFlow  = SP.flowRate;

                        for (Int32 s = 0; s < KopernicusStar.Stars.Count; s++)
                        {
                            KopernicusStar star = KopernicusStar.Stars[s];

                            if (star != trackingStar)
                            {
                                // Use this star
                                star.shifter.ApplyPhysics();
                                double flux = star.CalculateFluxAt(vessel);
                                vessel.solarFlux += flux * PhysicsGlobals.SolarLuminosityAtHome / 1360;

                                // Change the tracking body
                                SP.trackingBody = star.sun;
                                SP.GetTrackingBodyTransforms();

                                // Set Tracking Speed to zero
                                Single trackingSpeed = SP.trackingSpeed;
                                SP.trackingSpeed = 0;

                                // Run The MDSP CalculateTracking
                                SP.CalculateTracking();

                                // Add to TotalFlux and TotalAoA
                                totalFlux  += vessel.solarFlux;
                                totalAoA   += SP.sunAOA;
                                _totalFlow += SP._flowRate;
                                totalFlow  += SP.flowRate;

                                if (bestFlux < flux)
                                {
                                    bestFlux = flux;
                                    bestStar = star;
                                }

                                // Restore Tracking Speed
                                SP.trackingSpeed = trackingSpeed;
                            }
                        }

                        // Restore the tracking body
                        SP.trackingBody = trackingStar.sun;
                        SP.GetTrackingBodyTransforms();

                        // Restore the starting star
                        trackingStar.shifter.ApplyPhysics();

                        totalFlux += trackingStar.CalculateFluxAt(vessel) * PhysicsGlobals.SolarLuminosityAtHome / 1360;

                        vessel.solarFlux = totalFlux;
                        SP.sunAOA        = totalAoA;
                        SP.sunAOA       /= _relativeSunAoa ? KopernicusStar.Stars.Count : 1;
                        SP._flowRate     = _totalFlow;
                        SP.flowRate      = totalFlow;

                        // We got the best star to use
                        if (bestStar != null && bestStar.sun != SP.trackingBody)
                        {
                            if (!_manualTracking)
                            {
                                SP.trackingBody = bestStar.sun;
                                SP.GetTrackingBodyTransforms();
                                continue;
                            }
                        }
                    }
                }

                // Restore The Current Star
                KopernicusStar.Current.shifter.ApplyPhysics();
            }
        }