version 2
This commit is contained in:
125
src/GreenHome.Infrastructure/SunCalculatorService.cs
Normal file
125
src/GreenHome.Infrastructure/SunCalculatorService.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using GreenHome.Application;
|
||||
|
||||
namespace GreenHome.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// سرویس محاسبه طلوع و غروب خورشید
|
||||
/// </summary>
|
||||
public sealed class SunCalculatorService : ISunCalculatorService
|
||||
{
|
||||
public bool IsDaytime(DateTime dateTime, decimal latitude, decimal longitude)
|
||||
{
|
||||
var lat = (double)latitude;
|
||||
var lng = (double)longitude;
|
||||
|
||||
// Calculate sunrise and sunset times
|
||||
var (sunrise, sunset) = CalculateSunriseSunset(dateTime, lat, lng);
|
||||
|
||||
// Check if current time is between sunrise and sunset
|
||||
var currentTime = dateTime.TimeOfDay;
|
||||
return currentTime >= sunrise && currentTime <= sunset;
|
||||
}
|
||||
|
||||
private (TimeSpan sunrise, TimeSpan sunset) CalculateSunriseSunset(DateTime date, double latitude, double longitude)
|
||||
{
|
||||
// Julian day calculation
|
||||
var julianDay = CalculateJulianDay(date);
|
||||
var julianCentury = (julianDay - 2451545.0) / 36525.0;
|
||||
|
||||
// Sun's mean longitude
|
||||
var sunMeanLongitude = (280.46646 + julianCentury * (36000.76983 + julianCentury * 0.0003032)) % 360;
|
||||
|
||||
// Sun's mean anomaly
|
||||
var sunMeanAnomaly = 357.52911 + julianCentury * (35999.05029 - 0.0001537 * julianCentury);
|
||||
|
||||
// Earth's orbit eccentricity
|
||||
var eccentricity = 0.016708634 - julianCentury * (0.000042037 + 0.0000001267 * julianCentury);
|
||||
|
||||
// Sun's equation of center
|
||||
var sunCenter = Math.Sin(ToRadians(sunMeanAnomaly)) * (1.914602 - julianCentury * (0.004817 + 0.000014 * julianCentury))
|
||||
+ Math.Sin(ToRadians(2 * sunMeanAnomaly)) * (0.019993 - 0.000101 * julianCentury)
|
||||
+ Math.Sin(ToRadians(3 * sunMeanAnomaly)) * 0.000289;
|
||||
|
||||
// Sun's true longitude
|
||||
var sunTrueLongitude = sunMeanLongitude + sunCenter;
|
||||
|
||||
// Sun's apparent longitude
|
||||
var sunApparentLongitude = sunTrueLongitude - 0.00569 - 0.00478 * Math.Sin(ToRadians(125.04 - 1934.136 * julianCentury));
|
||||
|
||||
// Mean oblique ecliptic
|
||||
var meanOblique = 23.0 + (26.0 + ((21.448 - julianCentury * (46.815 + julianCentury * (0.00059 - julianCentury * 0.001813)))) / 60.0) / 60.0;
|
||||
|
||||
// Oblique correction
|
||||
var obliqueCorrection = meanOblique + 0.00256 * Math.Cos(ToRadians(125.04 - 1934.136 * julianCentury));
|
||||
|
||||
// Sun's declination
|
||||
var declination = ToDegrees(Math.Asin(Math.Sin(ToRadians(obliqueCorrection)) * Math.Sin(ToRadians(sunApparentLongitude))));
|
||||
|
||||
// Equation of time
|
||||
var y = Math.Tan(ToRadians(obliqueCorrection / 2.0)) * Math.Tan(ToRadians(obliqueCorrection / 2.0));
|
||||
var equationOfTime = 4.0 * ToDegrees(y * Math.Sin(2.0 * ToRadians(sunMeanLongitude))
|
||||
- 2.0 * eccentricity * Math.Sin(ToRadians(sunMeanAnomaly))
|
||||
+ 4.0 * eccentricity * y * Math.Sin(ToRadians(sunMeanAnomaly)) * Math.Cos(2.0 * ToRadians(sunMeanLongitude))
|
||||
- 0.5 * y * y * Math.Sin(4.0 * ToRadians(sunMeanLongitude))
|
||||
- 1.25 * eccentricity * eccentricity * Math.Sin(2.0 * ToRadians(sunMeanAnomaly)));
|
||||
|
||||
// Hour angle sunrise (civil twilight: sun 6 degrees below horizon)
|
||||
var zenith = 90.833; // Official: 90 degrees 50 minutes
|
||||
var hourAngle = ToDegrees(Math.Acos(
|
||||
(Math.Cos(ToRadians(zenith)) / (Math.Cos(ToRadians(latitude)) * Math.Cos(ToRadians(declination))))
|
||||
- Math.Tan(ToRadians(latitude)) * Math.Tan(ToRadians(declination))
|
||||
));
|
||||
|
||||
// Calculate sunrise and sunset in minutes
|
||||
var solarNoon = (720.0 - 4.0 * longitude - equationOfTime) / 1440.0;
|
||||
var sunriseTime = solarNoon - hourAngle * 4.0 / 1440.0;
|
||||
var sunsetTime = solarNoon + hourAngle * 4.0 / 1440.0;
|
||||
|
||||
// Convert to local time (assume UTC offset for Iran: +3:30 = 210 minutes)
|
||||
// You should ideally calculate timezone offset based on longitude
|
||||
var utcOffsetMinutes = Math.Round(longitude / 15.0) * 60.0;
|
||||
|
||||
var sunriseMinutes = sunriseTime * 1440.0 + utcOffsetMinutes;
|
||||
var sunsetMinutes = sunsetTime * 1440.0 + utcOffsetMinutes;
|
||||
|
||||
// Handle edge cases
|
||||
if (sunriseMinutes < 0) sunriseMinutes += 1440;
|
||||
if (sunriseMinutes >= 1440) sunriseMinutes -= 1440;
|
||||
if (sunsetMinutes < 0) sunsetMinutes += 1440;
|
||||
if (sunsetMinutes >= 1440) sunsetMinutes -= 1440;
|
||||
|
||||
var sunrise = TimeSpan.FromMinutes(sunriseMinutes);
|
||||
var sunset = TimeSpan.FromMinutes(sunsetMinutes);
|
||||
|
||||
return (sunrise, sunset);
|
||||
}
|
||||
|
||||
private double CalculateJulianDay(DateTime date)
|
||||
{
|
||||
var year = date.Year;
|
||||
var month = date.Month;
|
||||
var day = date.Day;
|
||||
|
||||
if (month <= 2)
|
||||
{
|
||||
year -= 1;
|
||||
month += 12;
|
||||
}
|
||||
|
||||
var a = year / 100;
|
||||
var b = 2 - a + (a / 4);
|
||||
|
||||
return Math.Floor(365.25 * (year + 4716)) + Math.Floor(30.6001 * (month + 1)) + day + b - 1524.5;
|
||||
}
|
||||
|
||||
private double ToRadians(double degrees)
|
||||
{
|
||||
return degrees * Math.PI / 180.0;
|
||||
}
|
||||
|
||||
private double ToDegrees(double radians)
|
||||
{
|
||||
return radians * 180.0 / Math.PI;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user