Instantly share code, notes, and snippets.

# mrrrk/moon.py

Created December 2, 2017 16:05
Show Gist options
• Save mrrrk/e100225508ad8b6882844de99d264ca7 to your computer and use it in GitHub Desktop.
Calculate lunar (moon) phase
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 import math, datetime class Moon: # average length of a lunar cycle (Wikipedia) @staticmethod def lunar_month_days(): return 29.530588853 @staticmethod def day_of_cycle(test_date=None): if test_date is None: test_date = datetime.datetime.now() # the closer the base date (a known new-moon) is to the dates we're interested with, the better base_new_moon = datetime.datetime(2017, 8, 21, 18, 30, 0) days = (test_date - base_new_moon).total_seconds() / (24.0 * 60.0 * 60.0) return days % Moon.lunar_month_days() @staticmethod def illumination(day_of_cycle): # translate daily position as an angle between 0 and 360° angle = 2 * math.pi * day_of_cycle / Moon.lunar_month_days() # move and resize cosine curve so it represents waxing and waning as value from 0 to 1 return (1 + (-math.cos(angle))) / 2.0 # This isn't strictly accurate because new moon and full moon do not actually last for 1/8th of a cycle @staticmethod def phase(day_of_cycle): proportion = float(day_of_cycle) / Moon.lunar_month_days() # add 0.5 to put value in 'middle' of phase # - which might put it past end of array, so use bitwise AND to lop it off index = int(proportion * 8.0 + 0.5) & 7 return { 0: "New Moon", 1: "Waxing Crescent", 2: "First Quarter", 3: "Waxing Gibbous", 4: "Full Moon", 5: "Waning Gibbous", 6: "Last Quarter", 7: "Waning Crescent" }[index]

### mrrrk commented Dec 2, 2017

This uses the average synodic cycle time (time between new moons) along with the time of a recent new moon and assumes you're happy with the approximate result being out by a few hours.