Skip to content

Commit 5c51817

Browse files
committed
feat: Calculate solar hour angle from MSTS environment file sun rise/set times
1 parent 43379ea commit 5c51817

File tree

4 files changed

+40
-21
lines changed

4 files changed

+40
-21
lines changed

Source/Orts.Formats.Msts/EnvironmentFile.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// COPYRIGHT 2009 - 2023 by the Open Rails project.
1+
// COPYRIGHT 2009 - 2023 by the Open Rails project.
22
//
33
// This file is part of Open Rails.
44
//
@@ -16,6 +16,7 @@
1616
// along with Open Rails. If not, see <http://www.gnu.org/licenses/>.
1717

1818
using System.Collections.Generic;
19+
using System.Linq;
1920
using Orts.Parsers.Msts;
2021

2122
namespace Orts.Formats.Msts
@@ -48,6 +49,8 @@ public EnvironmentFile(string filePath)
4849
}
4950
}
5051

52+
public SkySatellite Sun => SkySatellites.FirstOrDefault(s => s.Type == SkySatellite.SkySatelliteType.Sun);
53+
5154
public class WaterLayer
5255
{
5356
public float Height;

Source/RunActivity/Viewer3D/Common/SunMoonPos.cs

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
using System;
2626
using Microsoft.Xna.Framework;
27+
using Orts.Formats.Msts;
2728

2829
namespace Orts.Viewer3D.Common
2930
{
@@ -35,26 +36,18 @@ static class SunMoonPos
3536
/// </summary>
3637
/// <param name="latitude">Latitude, radians.</param>
3738
/// <param name="longitude">Longitude, radians.</param>
39+
/// <param name="sun">Sun satellite information (including rise/set time).</param>
3840
/// <param name="clockTime">Wall clock time since start of activity, days.</param>
3941
/// <param name="date">Structure made up of day, month, year and ordinal date.</param>
4042
/// <returns>A normalize 3D vector indicating the sun's position from the viewer's location.</returns>
41-
public static Vector3 SolarAngle(double latitude, double longitude, float clockTime, SkyViewer.SkyDate date)
43+
public static Vector3 SolarAngle(double latitude, double longitude, EnvironmentFile.SkySatellite sun, float clockTime, SkyViewer.SkyDate date)
4244
{
4345
Vector3 sunDirection;
44-
45-
// For these calculations, west longitude is in positive degrees
46-
var noaaLongitude = -MathHelper.ToDegrees((float)longitude);
46+
double solarHourAngle;
4747

4848
// Fractional year, radians
4949
var fYear = (MathHelper.TwoPi / 365) * (date.OrdinalDate - 1 + (clockTime - 0.5));
5050

51-
// Equation of time, minutes
52-
var eqTime = 229.18 * (0.000075
53-
+ (0.001868 * Math.Cos(fYear))
54-
- (0.032077 * Math.Sin(fYear))
55-
- (0.014615 * Math.Cos(2 * fYear))
56-
- (0.040849 * Math.Sin(2 * fYear)));
57-
5851
// Solar declination, radians
5952
var solarDeclination = 0.006918
6053
- (0.399912 * Math.Cos(fYear))
@@ -64,14 +57,37 @@ public static Vector3 SolarAngle(double latitude, double longitude, float clockT
6457
- (0.002697 * Math.Cos(3 * fYear))
6558
+ (0.001480 * Math.Sin(3 * fYear));
6659

67-
// Time offset at present longitude, minutes
68-
var timeOffset = eqTime - (4 * noaaLongitude) + (60 * Math.Round(noaaLongitude / 15));
60+
// How much the latitude and solar declination changes the horizon so that we can correctly calculate
61+
// the solar angle based on sun rise/set times
62+
var horizonSolarHourAngle = Math.Acos(-(Math.Sin(latitude) * Math.Sin(solarDeclination)) / (Math.Cos(latitude) * Math.Cos(solarDeclination)));
6963

70-
// True solar time, minutes (since midnight)
71-
var trueSolar = (clockTime * 24 * 60) + timeOffset;
64+
if (sun?.RiseTime != 0 && sun?.SetTime != 0 && sun?.RiseTime < sun?.SetTime && !double.IsNaN(horizonSolarHourAngle))
65+
{
66+
var noonTimeD = (float)(sun.RiseTime + sun.SetTime) / 2 / 86400;
67+
var riseSetScale = 90 / (noonTimeD - ((float)sun.RiseTime / 86400));
68+
solarHourAngle = MathHelper.ToRadians((clockTime - noonTimeD) * riseSetScale * (float)(horizonSolarHourAngle / MathHelper.PiOver2));
69+
}
70+
else
71+
{
72+
// For these calculations, west longitude is in positive degrees
73+
var noaaLongitude = -MathHelper.ToDegrees((float)longitude);
7274

73-
// Solar hour angle, radians
74-
var solarHourAngle = MathHelper.ToRadians((float)(trueSolar / 4) - 180);
75+
// Equation of time, minutes
76+
var eqTime = 229.18 * (0.000075
77+
+ (0.001868 * Math.Cos(fYear))
78+
- (0.032077 * Math.Sin(fYear))
79+
- (0.014615 * Math.Cos(2 * fYear))
80+
- (0.040849 * Math.Sin(2 * fYear)));
81+
82+
// Time offset at present longitude, minutes
83+
var timeOffset = eqTime - (4 * noaaLongitude) + (60 * Math.Round(noaaLongitude / 15));
84+
85+
// True solar time, minutes (since midnight)
86+
var trueSolar = (clockTime * 24 * 60) + timeOffset;
87+
88+
// Solar hour angle, radians
89+
solarHourAngle = MathHelper.ToRadians((float)(trueSolar / 4) - 180);
90+
}
7591

7692
// Solar zenith cosine. This is the Y COORDINATE of the solar Vector.
7793
var solarZenithCosine = (Math.Sin(latitude) * Math.Sin(solarDeclination))

Source/RunActivity/Viewer3D/MSTSSky.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
141141
// Fill in the sun- and moon-position lookup tables
142142
for (int i = 0; i < skySteps.MaxSteps; i++)
143143
{
144-
mstsskysolarPosArray[i] = SunMoonPos.SolarAngle(mstsskylatitude, mstsskylongitude, ((float)i / skySteps.MaxSteps), date);
144+
mstsskysolarPosArray[i] = SunMoonPos.SolarAngle(mstsskylatitude, mstsskylongitude, MSTSSkyViewer.ENVFile.Sun, ((float)i / skySteps.MaxSteps), date);
145145
mstsskylunarPosArray[i] = SunMoonPos.LunarAngle(mstsskylatitude, mstsskylongitude, ((float)i / skySteps.MaxSteps), date);
146146
}
147147
// Phase of the moon is generated at random
@@ -221,7 +221,7 @@ public void LoadPrep()
221221
date.Day = 21;
222222
date.Year = 2017;
223223
float fractClockTime = (float)MSTSSkyViewer.Simulator.ClockTime / 86400;
224-
mstsskysolarDirection = SunMoonPos.SolarAngle(mstsskylatitude, mstsskylongitude, fractClockTime, date);
224+
mstsskysolarDirection = SunMoonPos.SolarAngle(mstsskylatitude, mstsskylongitude, MSTSSkyViewer.ENVFile.Sun, fractClockTime, date);
225225
mstsskyworldLoc = null;
226226
mstsskylatitude = 0;
227227
mstsskylongitude = 0;

Source/RunActivity/Viewer3D/Sky.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ void WeatherChanged()
106106
// Fill in the sun- and moon-position lookup tables
107107
for (var i = 0; i < SkyInterpolation.MaxSteps; i++)
108108
{
109-
SolarPositionCache[i] = SunMoonPos.SolarAngle(Latitude, Longitude, (float)i / SkyInterpolation.MaxSteps, date);
109+
SolarPositionCache[i] = SunMoonPos.SolarAngle(Latitude, Longitude, Viewer.ENVFile.Sun, (float)i / SkyInterpolation.MaxSteps, date);
110110
LunarPositionCache[i] = SunMoonPos.LunarAngle(Latitude, Longitude, (float)i / SkyInterpolation.MaxSteps, date);
111111
}
112112

0 commit comments

Comments
 (0)