PageRenderTime 39ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Application/Dev/Common/SunTimeCalculator.cs

#
C# | 353 lines | 202 code | 56 blank | 95 comment | 11 complexity | 7b4a628777f2c2976786f9b567383989 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Globalization;
  6. namespace EasyWings.Common
  7. {
  8. /// <summary>
  9. /// Calculates sunset / sunrise time.
  10. ///
  11. /// Implementation of algorithm found in Almanac for Computers, 1990
  12. /// published by Nautical Almanac Office.
  13. ///
  14. /// Implemented by Huysentruit Wouter, Fastload-Media.be.
  15. ///
  16. /// From Wouter Huysentruit, at http://www.codeproject.com/KB/cs/SunTime.aspx.
  17. /// Code provided under The Code Project Open License (CPOL).
  18. ///
  19. /// For TimeZones, see http://fr.wikipedia.org/wiki/Fichier:Timezones2008.png .
  20. /// </summary>
  21. public class SunTimeCalculator
  22. {
  23. #region Declarations
  24. public enum ZenithValue : long
  25. {
  26. /// <summary>
  27. /// Official zenith (90.5)
  28. /// </summary>
  29. Official = 90500,
  30. /// <summary>
  31. /// Civil zenith (96)
  32. /// </summary>
  33. Civil = 96000,
  34. /// <summary>
  35. /// Nautical zenith (102)
  36. /// </summary>
  37. Nautical = 102000,
  38. /// <summary>
  39. /// Astronomical zenith (108)
  40. /// </summary>
  41. Astronomical = 108000
  42. }
  43. internal enum Direction
  44. {
  45. Sunrise,
  46. Sunset
  47. }
  48. private ZenithValue zenith = ZenithValue.Official;
  49. private double longitude;
  50. private double latitude;
  51. private double utcOffset;
  52. private DateTime date;
  53. private DaylightTime daylightChanges;
  54. private int sunriseTime;
  55. private int sunsetTime;
  56. #endregion
  57. #region Construction
  58. /// <summary>
  59. /// Create a new SunTime object with default settings.
  60. /// </summary>
  61. public SunTimeCalculator()
  62. {
  63. this.latitude = 0.0;
  64. this.longitude = 0.0;
  65. this.utcOffset = 1.0;
  66. this.date = DateTime.Now;
  67. this.daylightChanges = TimeZone.CurrentTimeZone.GetDaylightChanges(date.Year);
  68. Update();
  69. }
  70. /// <summary>
  71. /// Create a new SunTime object for the current date.
  72. /// </summary>
  73. /// <param name="latitude">Global position latitude in degrees. Latitude is positive for North and negative for South.</param>
  74. /// <param name="longitude">Global position longitude in degrees. Longitude is positive for East and negative for West.</param>
  75. /// <param name="utcOffset">The local UTC offset (f.e. +1 for Brussel, Kopenhagen, Madrid, Paris).</param>
  76. /// <param name="daylightChanges">The daylight saving settings to use.</param>
  77. public SunTimeCalculator(double latitude, double longitude, double utcOffset, DaylightTime daylightChanges)
  78. {
  79. this.latitude = latitude;
  80. this.longitude = longitude;
  81. this.utcOffset = utcOffset;
  82. this.date = DateTime.Now;
  83. this.daylightChanges = daylightChanges;
  84. Update();
  85. }
  86. /// <summary>
  87. /// Create a new SunTime object for the given date.
  88. /// </summary>
  89. /// <param name="latitude">Global position latitude in degrees. Latitude is positive for North and negative for South.</param>
  90. /// <param name="longitude">Global position longitude in degrees. Longitude is positive for East and negative for West.</param>
  91. /// <param name="utcOffset">The local UTC offset (f.e. +1 for Brussel, Kopenhagen, Madrid, Paris).</param>
  92. /// <param name="daylightChanges">The daylight saving settings to use.</param>
  93. /// <param name="date">The date to calculate the set- and risetime for.</param>
  94. public SunTimeCalculator(double latitude, double longitude, double utcOffset, DaylightTime daylightChanges, DateTime date)
  95. {
  96. this.latitude = latitude;
  97. this.longitude = longitude;
  98. this.utcOffset = utcOffset;
  99. this.daylightChanges = daylightChanges;
  100. this.date = date;
  101. Update();
  102. }
  103. #endregion
  104. #region Private methods
  105. private static double Deg2Rad(double angle)
  106. {
  107. return Math.PI * angle / 180.0;
  108. }
  109. private static double Rad2Deg(double angle)
  110. {
  111. return 180.0 * angle / Math.PI;
  112. }
  113. private static double FixValue(double value, double min, double max)
  114. {
  115. while (value < min)
  116. value += (max - min);
  117. while (value >= max)
  118. value -= (max - min);
  119. return value;
  120. }
  121. private int Calculate(Direction direction)
  122. {
  123. /* doy (N) */
  124. int N = date.DayOfYear;
  125. /* appr. time (t) */
  126. double lngHour = longitude / 15.0;
  127. double t;
  128. if (direction == Direction.Sunrise)
  129. t = N + ((6.0 - lngHour) / 24.0);
  130. else
  131. t = N + ((18.0 - lngHour) / 24.0);
  132. /* mean anomaly (M) */
  133. double M = (0.9856 * t) - 3.289;
  134. /* true longitude (L) */
  135. double L = M + (1.916 * Math.Sin(Deg2Rad(M))) + (0.020 * Math.Sin(Deg2Rad(2 * M))) + 282.634;
  136. L = FixValue(L, 0, 360);
  137. /* right asc (RA) */
  138. double RA = Rad2Deg(Math.Atan(0.91764 * Math.Tan(Deg2Rad(L))));
  139. RA = FixValue(RA, 0, 360);
  140. /* adjust quadrant of RA */
  141. double Lquadrant = (Math.Floor(L / 90.0)) * 90.0;
  142. double RAquadrant = (Math.Floor(RA / 90.0)) * 90.0;
  143. RA = RA + (Lquadrant - RAquadrant);
  144. RA = RA / 15.0;
  145. /* sin cos DEC (sinDec / cosDec) */
  146. double sinDec = 0.39782 * Math.Sin(Deg2Rad(L));
  147. double cosDec = Math.Cos(Math.Asin(sinDec));
  148. /* local hour angle (cosH) */
  149. double cosH = (Math.Cos(Deg2Rad((double)zenith / 1000.0f)) - (sinDec * Math.Sin(Deg2Rad(latitude)))) / (cosDec * Math.Cos(Deg2Rad(latitude)));
  150. /* local hour (H) */
  151. double H;
  152. if (direction == Direction.Sunrise)
  153. H = 360.0 - Rad2Deg(Math.Acos(cosH));
  154. else
  155. H = Rad2Deg(Math.Acos(cosH));
  156. H = H / 15.0;
  157. /* time (T) */
  158. double T = H + RA - (0.06571 * t) - 6.622;
  159. /* universal time (T) */
  160. double UT = T - lngHour;
  161. UT += utcOffset; // local UTC offset
  162. if (daylightChanges != null)
  163. if ((date > daylightChanges.Start) && (date < daylightChanges.End))
  164. UT += daylightChanges.Delta.TotalHours;
  165. UT = FixValue(UT, 0, 24);
  166. return (int)Math.Round(UT * 3600); // Convert to seconds
  167. }
  168. private void Update()
  169. {
  170. sunriseTime = Calculate(Direction.Sunrise);
  171. sunsetTime = Calculate(Direction.Sunset);
  172. }
  173. #endregion
  174. #region Public methods
  175. /// <summary>
  176. /// Combine a degrees/minutes/seconds value to an angle in degrees.
  177. /// </summary>
  178. /// <param name="degrees">The degrees part of the value.</param>
  179. /// <param name="minutes">The minutes part of the value.</param>
  180. /// <param name="seconds">The seconds part of the value.</param>
  181. /// <returns>The combined angle in degrees.</returns>
  182. public static double DegreesToAngle(double degrees, double minutes, double seconds)
  183. {
  184. if (degrees < 0)
  185. return degrees - (minutes / 60.0) - (seconds / 3600.0);
  186. else
  187. return degrees + (minutes / 60.0) + (seconds / 3600.0);
  188. }
  189. #endregion
  190. #region Properties
  191. /// <summary>
  192. /// Gets or sets the global position longitude in degrees.
  193. /// Longitude is positive for East and negative for West.
  194. /// </summary>
  195. public double Longitude
  196. {
  197. get { return longitude; }
  198. set
  199. {
  200. longitude = value;
  201. Update();
  202. }
  203. }
  204. /// <summary>
  205. /// Gets or sets the global position latitude in degrees.
  206. /// Latitude is positive for North and negative for South.
  207. /// </summary>
  208. public double Latitude
  209. {
  210. get { return latitude; }
  211. set
  212. {
  213. latitude = value;
  214. Update();
  215. }
  216. }
  217. /// <summary>
  218. /// Gets or sets the date where the RiseTime and SetTime apply to.
  219. /// </summary>
  220. public DateTime Date
  221. {
  222. get { return date; }
  223. set
  224. {
  225. date = value;
  226. Update();
  227. }
  228. }
  229. /// <summary>
  230. /// Gets or sets the local UTC offset in hours.
  231. /// F.e.: +1 for Brussel, Kopenhagen, Madrid, Paris.
  232. /// See Windows Time settings for a list of offsets.
  233. /// </summary>
  234. public double UtcOffset
  235. {
  236. get { return utcOffset; }
  237. set
  238. {
  239. utcOffset = value;
  240. Update();
  241. }
  242. }
  243. /// <summary>
  244. /// The time (in seconds starting from midnight) the sun will rise on the given location at the given date.
  245. /// </summary>
  246. public int RiseTimeSec
  247. {
  248. get { return sunriseTime; }
  249. }
  250. /// <summary>
  251. /// The time (in seconds starting from midnight) the sun will set on the given location at the given date.
  252. /// </summary>
  253. public int SetTimeSec
  254. {
  255. get { return sunsetTime; }
  256. }
  257. /// <summary>
  258. /// The time the sun will rise on the given location at the given date.
  259. /// </summary>
  260. public DateTime RiseTime
  261. {
  262. get { return date.Date.AddSeconds(sunriseTime); }
  263. }
  264. /// <summary>
  265. /// The time the sun will set on the given location at the given date.
  266. /// </summary>
  267. public DateTime SetTime
  268. {
  269. get { return date.Date.AddSeconds(sunsetTime); }
  270. }
  271. /// <summary>
  272. /// Gets or sets the zenith used in the sunrise / sunset time calculation.
  273. /// </summary>
  274. public ZenithValue Zenith
  275. {
  276. get { return zenith; }
  277. set
  278. {
  279. zenith = value;
  280. Update();
  281. }
  282. }
  283. /// <summary>
  284. /// Gets or sets the daylight saving range to use in sunrise / sunset time calculation.
  285. /// </summary>
  286. public DaylightTime DaylightChanges
  287. {
  288. get { return daylightChanges; }
  289. set
  290. {
  291. daylightChanges = value;
  292. Update();
  293. }
  294. }
  295. #endregion
  296. }
  297. }