/// <summary> /// Calculate the position of the sun /// </summary> /// <param name="sunInfo">Calculates and receives sun info, including position, etc. Parameters marked as calculation parameters need to be set first.</param> /// <param name="rotateYDegrees">Rotate around the Y axis</param> public static void CalculateSunPosition(SunInfo sunInfo, float rotateYDegrees) { // dateTime should already be UTC format double d = (sunInfo.DateTime.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds / dayMs) + jDiff; double e = degreesToRadians * sunInfo.AxisTilt; // obliquity of the Earth double m = SolarMeanAnomaly(d); double l = EclipticLongitude(m); double dec = Declination(e, l, 0); double ra = RightAscension(e, l, 0); double lw = -degreesToRadians * sunInfo.Longitude; double phi = degreesToRadians * sunInfo.Latitude; double h = SiderealTime(d, lw) - ra; double azimuth = Azimuth(h, phi, dec); double altitude = Altitude(h, phi, dec); ConvertAzimuthAtltitudeToUnitVector(azimuth, altitude, ref sunInfo.UnitVectorUp); sunInfo.UnitVectorUp = Quaternion.AngleAxis(rotateYDegrees, Vector3.up) * sunInfo.UnitVectorUp; sunInfo.UnitVectorDown = -sunInfo.UnitVectorUp; sunInfo.JulianDays = d; sunInfo.Declination = dec; sunInfo.RightAscension = ra; sunInfo.Azimuth = azimuth; sunInfo.Altitude = altitude; sunInfo.SolarMeanAnomaly = m; sunInfo.EclipticLongitude = l; sunInfo.SiderealTime = h; }
/// <summary> /// Calculate moon position /// </summary> /// <param name="sunInfo">Sun info, already calculated</param> /// <param name="moonInfo">Receives moon info</param> /// <param name="rotateYDegrees">Rotate the moon in the sky around the y axis by this degrees</param> public static void CalculateMoonPosition(SunInfo sunInfo, MoonInfo moonInfo, float rotateYDegrees) { double d = sunInfo.JulianDays; double e = degreesToRadians * sunInfo.AxisTilt; // obliquity of the Earth double L = degreesToRadians * (218.316 + 13.176396 * d); // ecliptic longitude double M = degreesToRadians * (134.963 + 13.064993 * d); // mean anomaly double F = degreesToRadians * (93.272 + 13.229350 * d); // mean distance double l = L + degreesToRadians * 6.289 * Math.Sin(M); // longitude double b = degreesToRadians * 5.128 * Math.Sin(F); // latitude double dist = 385001.0 - (20905.0 * Math.Cos(M)); // distance to the moon in km double ra = RightAscension(e, l, b); double dec = Declination(e, l, b); const double sunDistance = 149598000.0; // avg sun distance to Earth double phi = Math.Acos(Math.Sin(sunInfo.Declination) * Math.Sin(dec) + Math.Cos(sunInfo.Declination) * Math.Cos(dec) * Math.Cos(sunInfo.RightAscension - ra)); double inc = Math.Atan2(sunDistance * Math.Sin(phi), dist - sunDistance * Math.Cos(phi)); double angle = Math.Atan2(Math.Cos(sunInfo.Declination) * Math.Sin(sunInfo.RightAscension - ra), Math.Sin(sunInfo.Declination) * Math.Cos(dec) - Math.Cos(sunInfo.Declination) * Math.Sin(dec) * Math.Cos(sunInfo.RightAscension - ra)); double fraction = (1.0 + Math.Cos(inc)) * 0.5; double phase = 0.5 + (0.5 * inc * Math.Sign(angle) * (1.0 / Math.PI)); double lw = -degreesToRadians * sunInfo.Longitude; phi = degreesToRadians * sunInfo.Latitude; double H = SiderealTime(d, lw) - ra; double h = Altitude(H, phi, dec); // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. double pa = Math.Atan2(Math.Sin(H), Math.Tan(phi) * Math.Cos(dec) - Math.Sin(dec) * Math.Cos(H)); h = h + AstroRefraction(h); // altitude correction for refraction double azimuth = Azimuth(H, phi, dec); double altitude = h; ConvertAzimuthAtltitudeToUnitVector(azimuth, altitude, ref moonInfo.UnitVectorUp); // set moon position and look at the origin moonInfo.UnitVectorUp = Quaternion.AngleAxis(rotateYDegrees, Vector3.up) * moonInfo.UnitVectorUp; moonInfo.UnitVectorDown = -moonInfo.UnitVectorUp; moonInfo.Distance = dist; moonInfo.Phase = phase; moonInfo.PercentFull = 1.0 - Math.Abs((0.5 - phase) * 2.0); moonInfo.Angle = angle; moonInfo.Fraction = fraction; moonInfo.Azimuth = azimuth; moonInfo.Altitude = altitude; moonInfo.RightAscension = ra; moonInfo.Declination = dec; moonInfo.LunarMeanAnomaly = M; moonInfo.EclipticLongitude = L; moonInfo.SiderealTime = H; moonInfo.ParallacticAngle = pa; }
/// <summary> /// Calculate the position of the sun /// </summary> /// <param name="sunInfo">Calculates and receives sun info, including position, etc. Parameters marked as calculation parameters need to be set first.</param> /// <param name="rotateYDegrees">Rotate around the Y axis</param> public static void CalculateSunPosition(SunInfo sunInfo, float rotateYDegrees) { // dateTime should already be UTC format double d = (sunInfo.DateTime.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds / dayMs) + jDiff; //double d = sunInfo.DateTime.ToOADate() + 2415018.5; double e = degreesToRadians * sunInfo.AxisTilt; // obliquity of the Earth double m = SolarMeanAnomaly(d); double l = EclipticLongitude(m); double dec = Declination(e, l, 0); double ra = RightAscension(e, l, 0); double lw = -degreesToRadians * sunInfo.Longitude; double phi = degreesToRadians * sunInfo.Latitude; double h = SiderealTime(d, lw) - ra; double azimuth = Azimuth(h, phi, dec); double altitude = Altitude(h, phi, dec); ConvertAzimuthAtltitudeToUnitVector(azimuth, altitude, ref sunInfo.UnitVectorUp); sunInfo.UnitVectorUp = Quaternion.AngleAxis(rotateYDegrees, Vector3.up) * sunInfo.UnitVectorUp; sunInfo.UnitVectorDown = -sunInfo.UnitVectorUp; sunInfo.JulianDays = d; sunInfo.Declination = dec; sunInfo.RightAscension = ra; sunInfo.Azimuth = azimuth; sunInfo.Altitude = altitude; sunInfo.SolarMeanAnomaly = m; sunInfo.EclipticLongitude = l; sunInfo.SiderealTime = h; double n = JulianCycle(d, lw); double ds = ApproxTransit(0, lw, n); double jnoon = SolarTransit(ds, m, l); double jSunSet = JulianDateForSunAltitude(-0.8 * (Math.PI / 180.0), lw, phi, dec, n, m, l); double jSunRise = jnoon - (jSunSet - jnoon); double jDusk = JulianDateForSunAltitude(-10.0 * (Math.PI / 180.0), lw, phi, dec, n, m, l); double jDawn = jnoon - (jDusk - jnoon); try { sunInfo.Dawn = JulianToDateTime(jDawn).TimeOfDay; sunInfo.Dusk = JulianToDateTime(jDusk).TimeOfDay; sunInfo.SunRise = JulianToDateTime(jSunRise).TimeOfDay; sunInfo.SunSet = JulianToDateTime(jSunSet).TimeOfDay; } catch { // don't crash if date time is out of bounds } }
/// <summary> /// Decompile entire suninfo block into Vector of separate elements. /// </summary> /// <param name="byteptr_t">Pointer to the beginning of suninfo block in Global data.</param> /// <param name="length">Length of the block to be read.</param> /// <param name="db">Database to which add classes.</param> private static unsafe void E_SunInfo(byte *byteptr_t, uint length, Database.Underground2 db) { const uint size = 0x110; for (uint loop = 0; loop < length / size; ++loop) { uint offset = 8 + loop * size; // current offset of the suninfo (padding included) // Get CollectionName string CName = ScriptX.NullTerminatedString(byteptr_t + offset + 8, 0x18); CName = Resolve.GetPathFromCollection(CName); Map.BinKeys[Bin.Hash(CName)] = CName; var Class = new SunInfo((IntPtr)(byteptr_t + offset), CName, db); db.SunInfos.Collections.Add(Class); } }
private void EvaluateEnvironment(double elapsedSeconds) { UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.EvaluateStatus"); // we use analytic mode if more than 2 minutes of game time has passed since last evaluation (~ x6000 timewarp speed) isAnalytic = elapsedSeconds > 120.0; // get vessel position Vector3d position = Lib.VesselPosition(Vessel); // this should never happen again if (Vector3d.Distance(position, Vessel.mainBody.position) < 1.0) { throw new Exception("Shit hit the fan for vessel " + Vessel.vesselName); } // situation underwater = Sim.Underwater(Vessel); breathable = Sim.Breathable(Vessel, EnvUnderwater); landed = Lib.Landed(Vessel); inAtmosphere = Vessel.mainBody.atmosphere && Vessel.altitude < Vessel.mainBody.atmosphereDepth; zeroG = !EnvLanded && !inAtmosphere; visibleBodies = Sim.GetLargeBodies(position); // get solar info (with multiple stars / Kopernicus support) // get the 'visibleBodies' and 'sunsInfo' lists, the 'mainSun', 'solarFluxTotal' variables. // require the situation variables to be evaluated first UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.Sunlight"); SunInfo.UpdateSunsInfo(this, position); UnityEngine.Profiling.Profiler.EndSample(); sunBodyAngle = Sim.SunBodyAngle(Vessel, position, mainSun.SunData.body); // temperature at vessel position UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.Temperature"); temperature = Sim.Temperature(Vessel, position, solarFluxTotal, out albedoFlux, out bodyFlux, out totalFlux); tempDiff = Sim.TempDiff(EnvTemperature, Vessel.mainBody, EnvLanded); UnityEngine.Profiling.Profiler.EndSample(); // radiation UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.Radiation"); gammaTransparency = Sim.GammaTransparency(Vessel.mainBody, Vessel.altitude); bool new_innerBelt, new_outerBelt, new_magnetosphere; radiation = Radiation.Compute(Vessel, position, EnvGammaTransparency, mainSun.SunlightFactor, out blackout, out new_magnetosphere, out new_innerBelt, out new_outerBelt, out interstellar, out shieldedRadiation); if (new_innerBelt != innerBelt || new_outerBelt != outerBelt || new_magnetosphere != magnetosphere) { innerBelt = new_innerBelt; outerBelt = new_outerBelt; magnetosphere = new_magnetosphere; if (Evaluated) { API.OnRadiationFieldChanged.Notify(Vessel, innerBelt, outerBelt, magnetosphere); } } UnityEngine.Profiling.Profiler.EndSample(); thermosphere = Sim.InsideThermosphere(Vessel); exosphere = Sim.InsideExosphere(Vessel); inStorm = Storm.InProgress(Vessel); vesselSituations.Update(); // other stuff gravioli = Sim.Graviolis(Vessel); UnityEngine.Profiling.Profiler.EndSample(); }
/// <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; } }
private void sunInformationButton_Click(object sender, RoutedEventArgs e) { SunInfo sunInfo = new SunInfo(); sunInfo.Show(); }
/// <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; } }