sun_position_calculator.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import math
  2. class Position:
  3. def __init__(self, azimuth, altitude):
  4. self.azimuth = azimuth
  5. self.altitude = altitude
  6. class SunPositionCalculator:
  7. def __init__(self):
  8. self.MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24
  9. self.J0 = 0.0009
  10. self.J1970 = 2440588
  11. self.J2000 = 2451545
  12. self.TO_RAD = math.pi / 180.0
  13. self.OBLIQUITY_OF_EARTH = 23.4397 * self.TO_RAD
  14. self.PERIHELION_OF_EARTH = 102.9372 * self.TO_RAD
  15. def to_julian(self, unixtime_in_ms):
  16. return unixtime_in_ms / self.MILLISECONDS_PER_DAY - 0.5 + self.J1970
  17. def from_julian(self, j):
  18. return round((j + 0.5 - self.J1970) * self.MILLISECONDS_PER_DAY)
  19. def to_days(self, unixtime_in_ms):
  20. return self.to_julian(unixtime_in_ms) - self.J2000
  21. def right_ascension(self, l, b):
  22. return math.atan2(math.sin(l) * math.cos(self.OBLIQUITY_OF_EARTH) - math.tan(b) * math.sin(self.OBLIQUITY_OF_EARTH), math.cos(l))
  23. def declination(self, l, b):
  24. return math.asin(math.sin(b) * math.cos(self.OBLIQUITY_OF_EARTH) + math.cos(b) * math.sin(self.OBLIQUITY_OF_EARTH) * math.sin(l))
  25. def azimuth(self, h, phi, dec):
  26. return math.atan2(math.sin(h), math.cos(h) * math.sin(phi) - math.tan(dec) * math.cos(phi)) + math.pi
  27. def altitude(self, h, phi, dec):
  28. return math.asin(math.sin(phi) * math.sin(dec) + math.cos(phi) * math.cos(dec) * math.cos(h))
  29. def sidereal_time(self, d, lw):
  30. return math.radians(280.16 + 360.9856235 * d) - lw
  31. def solar_mean_anomaly(self, d):
  32. return math.radians(357.5291 + 0.98560028 * d)
  33. def equation_of_center(self, m):
  34. return math.radians(1.9148 * math.sin(1.0 * m) + 0.02 * math.sin(2.0 * m) + 0.0003 * math.sin(3.0 * m))
  35. def ecliptic_longitude(self, m):
  36. return m + self.equation_of_center(m) + self.PERIHELION_OF_EARTH + math.pi
  37. def pos(self, unixtime_in_ms, lat, lon):
  38. lw = -math.radians(lon)
  39. phi = math.radians(lat)
  40. d = self.to_days(unixtime_in_ms)
  41. m = self.solar_mean_anomaly(d)
  42. l = self.ecliptic_longitude(m)
  43. dec = self.declination(l, 0.0)
  44. ra = self.right_ascension(l, 0.0)
  45. h = self.sidereal_time(d, lw) - ra
  46. return Position(self.azimuth(h, phi, dec), self.altitude(h, phi, dec))
  47. def julian_cycle(self, d, lw):
  48. return round(d - self.J0 - lw / (2.0 * math.pi))
  49. def approx_transit(self, ht, lw, n):
  50. return self.J0 + (ht + lw) / (2.0 * math.pi) + n
  51. def solar_transit_j(self, ds, m, l):
  52. return self.J2000 + ds + 0.0053 * math.sin(m) - 0.0069 * math.sin(2.0 * l)
  53. def hour_angle(self, h, phi, d):
  54. return math.acos((math.sin(h) - math.sin(phi) * math.sin(d)) / (math.cos(phi) * math.cos(d)))
  55. def observer_angle(self, height):
  56. return -2.076 * math.sqrt(height) / 60.0
  57. def get_set_j(self, h, lw, phi, dec, n, m, l):
  58. w = self.hour_angle(h, phi, dec)
  59. a = self.approx_transit(w, lw, n)
  60. return self.solar_transit_j(a, m, l)
  61. def time_at_phase(self, date, sun_phase, lat, lon, height):
  62. lw = -math.radians(lon)
  63. phi = math.radians(lat)
  64. dh = self.observer_angle(height)
  65. d = self.to_days(date)
  66. n = self.julian_cycle(d, lw)
  67. ds = self.approx_transit(0.0, lw, n)
  68. m = self.solar_mean_anomaly(ds)
  69. l = self.ecliptic_longitude(m)
  70. dec = self.declination(l, 0.0)
  71. j_noon = self.solar_transit_j(ds, m, l)
  72. h0 = math.radians(sun_phase.angle_deg + dh)
  73. j_set = self.get_set_j(h0, lw, phi, dec, n, m, l)
  74. if sun_phase.is_rise:
  75. j_rise = j_noon - (j_set - j_noon)
  76. return self.from_julian(j_rise)
  77. else:
  78. return self.from_julian(j_set)
  79. # Test example
  80. if __name__ == "__main__":
  81. unixtime = 1362441600000
  82. lat = 48.0
  83. lon = 9.0
  84. calculator = SunPositionCalculator()
  85. pos = calculator.pos(unixtime, lat, lon)
  86. az = math.degrees(pos.azimuth)
  87. alt = math.degrees(pos.altitude)
  88. print(f"The position of the sun is {az}/{alt}")