// return the total environent radiation at position specified public static double Compute(Vessel v, Vector3d position, double gamma_transparency, double sunlight, out bool blackout, out bool magnetosphere, out bool inner_belt, out bool outer_belt, out bool interstellar, out double shieldedRadiation) { // prepare out parameters blackout = false; magnetosphere = false; inner_belt = false; outer_belt = false; interstellar = false; shieldedRadiation = 0.0; // no-op when Radiation is disabled if (!Features.Radiation) { return(0.0); } // store stuff Space gsm; Vector3 p; double D; double r; // accumulate radiation double radiation = 0.0; CelestialBody body = v.mainBody; while (body != null) { // Compute radiation values from overlapping 3d fields (belts + magnetospheres) RadiationBody rb = Info(body); RadiationModel mf = rb.model; // activity is [-0.15..1.05] var activity = rb.SolarActivity(false); if (mf.Has_field()) { // transform to local space once var scaled_position = ScaledSpace.LocalToScaledSpace(position); // generate radii-normalized GSM space gsm = Gsm_space(rb, true); // move the point in GSM space p = gsm.Transform_in(scaled_position); // accumulate radiation and determine pause/belt flags if (mf.has_inner) { D = mf.Inner_func(p); inner_belt |= D < 0; // allow for radiation field to grow/shrink with solar activity D -= activity * 0.25 / mf.inner_radius; r = RadiationInBelt(D, mf.inner_radius, rb.radiation_inner_gradient); radiation += r * rb.radiation_inner * (1 + activity * 0.3); } if (mf.has_outer) { D = mf.Outer_func(p); outer_belt |= D < 0; // allow for radiation field to grow/shrink with solar activity D -= activity * 0.25 / mf.outer_radius; r = RadiationInBelt(D, mf.outer_radius, rb.radiation_outer_gradient); radiation += r * rb.radiation_outer * (1 + activity * 0.3); } if (mf.has_pause) { gsm = Gsm_space(rb, false); p = gsm.Transform_in(scaled_position); D = mf.Pause_func(p); radiation += Lib.Clamp(D / -0.1332f, 0.0f, 1.0f) * rb.RadiationPause(); magnetosphere |= D < 0.0f && !Lib.IsSun(rb.body); //< ignore heliopause interstellar |= D > 0.0f && Lib.IsSun(rb.body); //< outside heliopause } } if (rb.radiation_surface > 0 && body != v.mainBody) { Vector3d direction; double distance; if (Sim.IsBodyVisible(v, position, body, v.KerbalismData().EnvVisibleBodies, out direction, out distance)) { var r0 = RadiationR0(rb); var r1 = DistanceRadiation(r0, distance); // clamp to max. surface radiation. when loading on a rescaled system, the vessel can appear to be within the sun for a few ticks radiation += Math.Min(r1, rb.radiation_surface); #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " from surface of " + body + ": " + Lib.HumanReadableRadiation(radiation) + " gamma: " + Lib.HumanReadableRadiation(r1)); } #endif } } // avoid loops in the chain body = (body.referenceBody != null && body.referenceBody.referenceBody == body) ? null : body.referenceBody; } // add extern radiation radiation += Settings.ExternRadiation / 3600.0; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " extern: " + Lib.HumanReadableRadiation(radiation) + " gamma: " + Lib.HumanReadableRadiation(Settings.ExternRadiation)); } #endif // apply gamma transparency if inside atmosphere radiation *= gamma_transparency; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " after gamma: " + Lib.HumanReadableRadiation(radiation) + " transparency: " + gamma_transparency); } #endif // add surface radiation of the body itself if (Lib.IsSun(v.mainBody) && v.altitude < v.mainBody.Radius) { if (v.altitude > v.mainBody.Radius) { radiation += DistanceRadiation(RadiationR0(Info(v.mainBody)), v.altitude); } } #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " from current main body: " + Lib.HumanReadableRadiation(radiation) + " gamma: " + Lib.HumanReadableRadiation(DistanceRadiation(RadiationR0(Info(v.mainBody)), v.altitude))); } #endif shieldedRadiation = radiation; // if there is a storm in progress if (Storm.InProgress(v)) { // inside a magnetopause (except heliosphere), blackout the signal // outside, add storm radiations modulated by sun visibility if (magnetosphere) { blackout = true; } else { var vd = v.KerbalismData(); var activity = Info(vd.EnvMainSun.SunData.body).SolarActivity(false) / 2.0; var strength = PreferencesRadiation.Instance.StormRadiation * sunlight * (activity + 0.5); radiation += strength; shieldedRadiation += vd.EnvHabitatInfo.AverageHabitatRadiation(strength); } } // add emitter radiation after atmosphere transparency var emitterRadiation = Emitter.Total(v); radiation += emitterRadiation; shieldedRadiation += emitterRadiation; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " after emitters: " + Lib.HumanReadableRadiation(radiation) + " shielded " + Lib.HumanReadableRadiation(shieldedRadiation)); } #endif // for EVAs, add the effect of nearby emitters if (v.isEVA) { var nearbyEmitters = Emitter.Nearby(v); radiation += nearbyEmitters; shieldedRadiation += nearbyEmitters; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " nearby emitters " + Lib.HumanReadableRadiation(nearbyEmitters)); } #endif } var passiveShielding = PassiveShield.Total(v); shieldedRadiation -= passiveShielding; #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " passiveShielding " + Lib.HumanReadableRadiation(passiveShielding)); } if (v.loaded) { Lib.Log("Radiation " + v + " before clamp: " + Lib.HumanReadableRadiation(radiation) + " shielded " + Lib.HumanReadableRadiation(shieldedRadiation)); } #endif // clamp radiation to positive range // note: we avoid radiation going to zero by using a small positive value radiation = Math.Max(radiation, Nominal); shieldedRadiation = Math.Max(shieldedRadiation, Nominal); #if DEBUG_RADIATION if (v.loaded) { Lib.Log("Radiation " + v + " after clamp: " + Lib.HumanReadableRadiation(radiation) + " shielded " + Lib.HumanReadableRadiation(shieldedRadiation)); } #endif // return radiation return(radiation); }
/// <summary> /// Update the 'sunsInfo' list and the 'mainSun', 'solarFluxTotal' variables. /// Uses discrete or analytic (for high timewarp speeds) evaluation methods based on the isAnalytic bool. /// Require the 'visibleBodies' variable to be set. /// </summary> // at the two highest timewarp speed, the number of sun visibility samples drop to the point that // the quantization error first became noticeable, and then exceed 100%, to solve this : // - we switch to an analytical estimation of the sunlight/shadow period // - atmo_factor become an average atmospheric absorption factor over the daylight period (not the whole day) public static void UpdateSunsInfo(VesselData vd, Vector3d vesselPosition) { Vessel v = vd.Vessel; double lastSolarFlux = 0.0; vd.sunsInfo = new List <SunInfo>(Sim.suns.Count); vd.solarFluxTotal = 0.0; vd.rawSolarFluxTotal = 0.0; foreach (Sim.SunData sunData in Sim.suns) { SunInfo sunInfo = new SunInfo(sunData); if (vd.isAnalytic) { // get sun direction and distance Lib.DirectionAndDistance(vesselPosition, sunInfo.sunData.body, out sunInfo.direction, out sunInfo.distance); // analytical estimation of the portion of orbit that was in sunlight, current limitations : // - the result is dependant on the vessel altitude at the time of evaluation, // consequently it gives inconsistent behavior with highly eccentric orbits // - this totally ignore the orbit inclinaison, polar orbits will be treated as equatorial orbits sunInfo.sunlightFactor = 1.0 - Sim.ShadowPeriod(v) / Sim.OrbitalPeriod(v); // get atmospheric absorbtion // for atmospheric bodies whose rotation period is less than 120 hours, // determine analytic atmospheric absorption over a single body revolution instead // of using a discrete value that would be unreliable at large timesteps : if (vd.inAtmosphere) { sunInfo.atmoFactor = Sim.AtmosphereFactorAnalytic(v.mainBody, vesselPosition, sunInfo.direction); } else { sunInfo.atmoFactor = 1.0; } } else { // determine if in sunlight, calculate sun direction and distance sunInfo.sunlightFactor = Sim.IsBodyVisible(v, vesselPosition, sunData.body, vd.visibleBodies, out sunInfo.direction, out sunInfo.distance) ? 1.0 : 0.0; // get atmospheric absorbtion sunInfo.atmoFactor = Sim.AtmosphereFactor(v.mainBody, vesselPosition, sunInfo.direction); } // get resulting solar flux in W/m² sunInfo.rawSolarFlux = sunInfo.sunData.SolarFlux(sunInfo.distance); sunInfo.solarFlux = sunInfo.rawSolarFlux * sunInfo.sunlightFactor * sunInfo.atmoFactor; // increment total flux from all stars vd.rawSolarFluxTotal += sunInfo.rawSolarFlux; vd.solarFluxTotal += sunInfo.solarFlux; // add the star to the list vd.sunsInfo.Add(sunInfo); // the most powerful star will be our "default" sun. Uses raw flux before atmo / sunlight factor if (sunInfo.rawSolarFlux > lastSolarFlux) { lastSolarFlux = sunInfo.rawSolarFlux; vd.mainSun = sunInfo; } } vd.sunlightFactor = 0.0; foreach (SunInfo sunInfo in vd.sunsInfo) { sunInfo.fluxProportion = sunInfo.rawSolarFlux / vd.rawSolarFluxTotal; vd.sunlightFactor += sunInfo.SunlightFactor * sunInfo.fluxProportion; } // avoid rounding errors if (vd.sunlightFactor > 0.99) { vd.sunlightFactor = 1.0; } }
/// <summary> /// Update the 'sunsInfo' list and the 'mainSun', 'solarFluxTotal' variables. /// Uses discrete or analytic (for high timewarp speeds) evaluation methods based on the isAnalytic bool. /// Require the 'visibleBodies' variable to be set. /// </summary> // at the two highest timewarp speed, the number of sun visibility samples drop to the point that // the quantization error first became noticeable, and then exceed 100%, to solve this: // - we switch to an analytical estimation of the sunlight/shadow period // - atmo_factor become an average atmospheric absorption factor over the daylight period (not the whole day) public static void UpdateSunsInfo(VesselData vd, Vector3d vesselPosition, double elapsedSeconds) { Vessel v = vd.Vessel; double lastSolarFlux = 0.0; vd.sunsInfo = new List <SunInfo>(Sim.suns.Count); vd.solarFluxTotal = 0.0; vd.rawSolarFluxTotal = 0.0; foreach (Sim.SunData sunData in Sim.suns) { SunInfo sunInfo = new SunInfo(sunData); if (vd.isAnalytic) { // get sun direction and distance Lib.DirectionAndDistance(vesselPosition, sunInfo.sunData.body, out sunInfo.direction, out sunInfo.distance); // analytical estimation of the portion of orbit that was in sunlight. // it has some limitations, see the comments on Sim.ShadowPeriod if (Settings.UseSamplingSunFactor) { // sampling estimation of the portion of orbit that is in sunlight // until we will calculate again sunInfo.sunlightFactor = Sim.SampleSunFactor(v, elapsedSeconds); } else { // analytical estimation of the portion of orbit that was in sunlight. // it has some limitations, see the comments on Sim.ShadowPeriod sunInfo.sunlightFactor = 1.0 - Sim.ShadowPeriod(v) / Sim.OrbitalPeriod(v); } // get atmospheric absorbtion // for atmospheric bodies whose rotation period is less than 120 hours, // determine analytic atmospheric absorption over a single body revolution instead // of using a discrete value that would be unreliable at large timesteps : if (vd.inAtmosphere) { sunInfo.atmoFactor = Sim.AtmosphereFactorAnalytic(v.mainBody, vesselPosition, sunInfo.direction); } else { sunInfo.atmoFactor = 1.0; } } else { // determine if in sunlight, calculate sun direction and distance sunInfo.sunlightFactor = Sim.IsBodyVisible(v, vesselPosition, sunData.body, vd.visibleBodies, out sunInfo.direction, out sunInfo.distance) ? 1.0 : 0.0; // get atmospheric absorbtion sunInfo.atmoFactor = Sim.AtmosphereFactor(v.mainBody, vesselPosition, sunInfo.direction); } // get resulting solar flux in W/m² sunInfo.rawSolarFlux = sunInfo.sunData.SolarFlux(sunInfo.distance); sunInfo.solarFlux = sunInfo.rawSolarFlux * sunInfo.sunlightFactor * sunInfo.atmoFactor; // increment total flux from all stars vd.rawSolarFluxTotal += sunInfo.rawSolarFlux; vd.solarFluxTotal += sunInfo.solarFlux; // add the star to the list vd.sunsInfo.Add(sunInfo); // the most powerful star will be our "default" sun. Uses raw flux before atmo / sunlight factor if (sunInfo.rawSolarFlux > lastSolarFlux) { lastSolarFlux = sunInfo.rawSolarFlux; vd.mainSun = sunInfo; } } vd.sunlightFactor = 0.0; foreach (SunInfo sunInfo in vd.sunsInfo) { sunInfo.fluxProportion = sunInfo.rawSolarFlux / vd.rawSolarFluxTotal; vd.sunlightFactor += sunInfo.SunlightFactor * sunInfo.fluxProportion; } // avoid rounding errors if (vd.sunlightFactor > 0.99) { vd.sunlightFactor = 1.0; } }