A library that computes the ephemerides.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 

186 lignes
6.2 KiB

  1. #!/usr/bin/env python3
  2. import datetime
  3. from typing import Union
  4. from skyfield.searchlib import find_discrete, find_maxima
  5. from skyfield.timelib import Time
  6. from skyfield.constants import tau
  7. from skyfield.errors import EphemerisRangeError
  8. from .data import Position, AsterEphemerides, MoonPhase, Object, ASTERS
  9. from .dateutil import translate_to_timezone
  10. from .core import get_skf_objects, get_timescale, get_iau2000b
  11. from .enum import MoonPhaseType
  12. from .exceptions import OutOfRangeDateError
  13. RISEN_ANGLE = -0.8333
  14. def _get_skyfield_to_moon_phase(
  15. times: [Time], vals: [int], now: Time
  16. ) -> Union[MoonPhase, None]:
  17. tomorrow = get_timescale().utc(
  18. now.utc_datetime().year, now.utc_datetime().month, now.utc_datetime().day + 1
  19. )
  20. phases = list(MoonPhaseType)
  21. current_phase = None
  22. current_phase_time = None
  23. next_phase_time = None
  24. i = 0
  25. if len(times) == 0:
  26. return None
  27. for i, time in enumerate(times):
  28. if now.utc_iso() <= time.utc_iso():
  29. if vals[i] in [0, 2, 4, 6]:
  30. if time.utc_datetime() < tomorrow.utc_datetime():
  31. current_phase_time = time
  32. current_phase = phases[vals[i]]
  33. else:
  34. i -= 1
  35. current_phase_time = None
  36. current_phase = phases[vals[i]]
  37. else:
  38. current_phase = phases[vals[i]]
  39. break
  40. for j in range(i + 1, len(times)):
  41. if vals[j] in [0, 2, 4, 6]:
  42. next_phase_time = times[j]
  43. break
  44. return MoonPhase(
  45. current_phase,
  46. current_phase_time.utc_datetime() if current_phase_time is not None else None,
  47. next_phase_time.utc_datetime() if next_phase_time is not None else None,
  48. )
  49. def get_moon_phase(compute_date: datetime.date = datetime.date.today(), timezone: int = 0) -> MoonPhase:
  50. earth = get_skf_objects()["earth"]
  51. moon = get_skf_objects()["moon"]
  52. sun = get_skf_objects()["sun"]
  53. def moon_phase_at(time: Time):
  54. time._nutation_angles = get_iau2000b(time)
  55. current_earth = earth.at(time)
  56. _, mlon, _ = current_earth.observe(moon).apparent().ecliptic_latlon("date")
  57. _, slon, _ = current_earth.observe(sun).apparent().ecliptic_latlon("date")
  58. return (((mlon.radians - slon.radians) // (tau / 8)) % 8).astype(int)
  59. moon_phase_at.rough_period = 7.0 # one lunar phase per week
  60. today = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day)
  61. time1 = get_timescale().utc(
  62. compute_date.year, compute_date.month, compute_date.day - 10
  63. )
  64. time2 = get_timescale().utc(
  65. compute_date.year, compute_date.month, compute_date.day + 10
  66. )
  67. try:
  68. times, phase = find_discrete(time1, time2, moon_phase_at)
  69. except EphemerisRangeError as error:
  70. start = translate_to_timezone(error.start_time.utc_datetime(), timezone)
  71. end = translate_to_timezone(error.end_time.utc_datetime(), timezone)
  72. start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(
  73. days=12
  74. )
  75. end = datetime.date(end.year, end.month, end.day) - datetime.timedelta(days=12)
  76. raise OutOfRangeDateError(start, end) from error
  77. return _get_skyfield_to_moon_phase(times, phase, today)
  78. def get_ephemerides(
  79. position: Position, date: datetime.date = datetime.date.today(), timezone: int = 0
  80. ) -> [AsterEphemerides]:
  81. ephemerides = []
  82. def get_angle(for_aster: Object):
  83. def fun(time: Time) -> float:
  84. return (
  85. position.get_planet_topos()
  86. .at(time)
  87. .observe(for_aster.get_skyfield_object())
  88. .apparent()
  89. .altaz()[0]
  90. .degrees
  91. )
  92. fun.rough_period = 1.0
  93. return fun
  94. def is_risen(for_aster: Object):
  95. def fun(time: Time) -> bool:
  96. return get_angle(for_aster)(time) > RISEN_ANGLE
  97. fun.rough_period = 0.5
  98. return fun
  99. start_time = get_timescale().utc(date.year, date.month, date.day, -timezone)
  100. end_time = get_timescale().utc(
  101. date.year, date.month, date.day, 23 - timezone, 59, 59
  102. )
  103. try:
  104. for aster in ASTERS:
  105. rise_times, arr = find_discrete(start_time, end_time, is_risen(aster))
  106. try:
  107. culmination_time, _ = find_maxima(
  108. start_time,
  109. end_time,
  110. f=get_angle(aster),
  111. epsilon=1.0 / 3600 / 24,
  112. num=12,
  113. )
  114. culmination_time = (
  115. culmination_time[0] if len(culmination_time) > 0 else None
  116. )
  117. except ValueError:
  118. culmination_time = None
  119. if len(rise_times) == 2:
  120. rise_time = rise_times[0 if arr[0] else 1]
  121. set_time = rise_times[1 if not arr[1] else 0]
  122. else:
  123. rise_time = rise_times[0] if arr[0] else None
  124. set_time = rise_times[0] if not arr[0] else None
  125. # Convert the Time instances to Python datetime objects
  126. if rise_time is not None:
  127. rise_time = translate_to_timezone(
  128. rise_time.utc_datetime().replace(microsecond=0), to_tz=timezone
  129. )
  130. if culmination_time is not None:
  131. culmination_time = translate_to_timezone(
  132. culmination_time.utc_datetime().replace(microsecond=0),
  133. to_tz=timezone,
  134. )
  135. if set_time is not None:
  136. set_time = translate_to_timezone(
  137. set_time.utc_datetime().replace(microsecond=0), to_tz=timezone
  138. )
  139. ephemerides.append(
  140. AsterEphemerides(rise_time, culmination_time, set_time, aster=aster)
  141. )
  142. except EphemerisRangeError as error:
  143. start = translate_to_timezone(error.start_time.utc_datetime(), timezone)
  144. end = translate_to_timezone(error.end_time.utc_datetime(), timezone)
  145. start = datetime.date(start.year, start.month, start.day + 1)
  146. end = datetime.date(end.year, end.month, end.day - 1)
  147. raise OutOfRangeDateError(start, end) from error
  148. return ephemerides