Source

earth/moon/phases.ts

  1. import { Day, JulianDay } from '@/types'
  2. import { MOON_PHASE_UPPER_LIMITS, MOON_SYNODIC_PERIOD, MoonPhase, MoonPhaseQuarter } from '@/constants'
  3. import { getDecimalYear } from '@/times'
  4. import { fmod } from '@/utils'
  5. // The value of K must be an integer
  6. function getK (jd: JulianDay): number {
  7. const decimalYear = getDecimalYear(jd)
  8. const decimalK = 12.3685 * (decimalYear - 2000)
  9. return decimalK >= 0 ? Math.floor(decimalK) : Math.ceil(decimalK)
  10. }
  11. function getPhaseK (jd: JulianDay, phase: MoonPhaseQuarter): number {
  12. let k = getK(jd)
  13. if (phase === MoonPhaseQuarter.FirstQuarter) {
  14. k = k + 0.25
  15. } else if (phase == MoonPhaseQuarter.Full) {
  16. k = k + 0.5
  17. } else if (phase == MoonPhaseQuarter.LastQuarter) {
  18. k = k + 0.75
  19. }
  20. return k
  21. }
  22. /**
  23. * The time of a given Moon phase.
  24. * Results are already corrected for the Sun's aberration and by the Moon's light-time.
  25. * @param {JulianDay} jd The julian day
  26. * @param {MoonPhase} phase The requested phase
  27. * @return {JulianDay}
  28. * @memberof module:Earth
  29. */
  30. export function getTimeOfMeanPhase (jd: JulianDay, phase: MoonPhaseQuarter): JulianDay {
  31. const k = getPhaseK(jd, phase)
  32. const T = k / 1236.85
  33. return 2451_550.097_66
  34. + 29.530_588_861 * k
  35. + 0.000_154_37 * Math.pow(T, 2)
  36. - 0.000_000_150 * Math.pow(T, 3)
  37. + 0.000_000_000_73 * Math.pow(T, 4)
  38. }
  39. /**
  40. * The age of the Moon cycle (0 = New Moon, MOON_SYNODIC_PERIOD/2 = Full Moon).
  41. * This is a low-accuracy age of the moon, using the average moon synodic period.
  42. * @param {JulianDay} jd The julian day
  43. * @return {JulianDay}
  44. * @memberof module:Earth
  45. */
  46. export function getAge (jd: JulianDay): Day {
  47. let jdNewMoon = getTimeOfMeanPhase(jd - MOON_SYNODIC_PERIOD, MoonPhaseQuarter.New)
  48. if (jdNewMoon > jd) {
  49. jdNewMoon = jdNewMoon - MOON_SYNODIC_PERIOD
  50. }
  51. return fmod(jd - jdNewMoon, MOON_SYNODIC_PERIOD)
  52. }
  53. /**
  54. * The age name of the Moon cycle (New, WaxingCresent, FirstQuarter etc)
  55. * @param {JulianDay} jd The julian day
  56. * @return {MoonPhase} The moon phase name
  57. * @memberof module:Earth
  58. */
  59. export function getAgeName (jd: JulianDay): MoonPhase {
  60. const frac = getAge(jd) / MOON_SYNODIC_PERIOD
  61. // Order matter since we wrote down only upper limits.
  62. if (frac <= (MOON_PHASE_UPPER_LIMITS[MoonPhase.New])) {
  63. return MoonPhase.New
  64. } else if (frac <= (MOON_PHASE_UPPER_LIMITS[MoonPhase.WaxingCrescent])) {
  65. return MoonPhase.WaxingCrescent
  66. } else if (frac <= (MOON_PHASE_UPPER_LIMITS[MoonPhase.FirstQuarter])) {
  67. return MoonPhase.FirstQuarter
  68. } else if (frac <= (MOON_PHASE_UPPER_LIMITS[MoonPhase.WaxingGibbous])) {
  69. return MoonPhase.WaxingGibbous
  70. } else if (frac <= (MOON_PHASE_UPPER_LIMITS[MoonPhase.Full])) {
  71. return MoonPhase.Full
  72. } else if (frac <= (MOON_PHASE_UPPER_LIMITS[MoonPhase.WaningGibbous])) {
  73. return MoonPhase.WaningGibbous
  74. } else if (frac <= (MOON_PHASE_UPPER_LIMITS[MoonPhase.LastQuarter])) {
  75. return MoonPhase.LastQuarter
  76. } else if (frac <= (MOON_PHASE_UPPER_LIMITS[MoonPhase.WaningCrescent])) {
  77. return MoonPhase.WaningCrescent
  78. } else {
  79. return MoonPhase.New
  80. }
  81. }