Sunday, 16 January 2011

Moon phases with PyEphem

Calculating all sorts of astronomical data including the phases of the moon is a piece of cake if you use the right tools. Combining the fantastic PyEphem package with the powers of Gnuplot and a suitable font will provide you with an actractive illustration of the path of the moon through the sky.

Let us first have a look at what we may achieve. The illustration shows the path of the moon through the sky as calculated for the Netherlands on January 11, 2011. The path of the sun is displayed as well for comparison. The horizontal axis shows the compass direction while the vertical axis shows the elevation above the horizon. The path of the moon is plotted with a resolution of 15 minutes and only for those parts that are above the horizon. On top of the path we have plotted an image indicating its phase for every hour together with the time. Now how did we put this together?

The essential ingredients are the PyEphem package,Gnuplot and a suitable font. The first thing we have to do is calculate the position of the moon as seen from our location and its phase. This is actually quite straightforward:

from datetime import date
from math import radians as rad,degrees as deg

import ephem

g = ephem.Observer()
g.name='Somewhere'
g.lat=rad(52.0)  # lat/long in decimal degrees
g.long=rad(5.2)

m = ephem.Moon()

g.date = date.today()# local time zone, I'm in UTC+1
g.date -= ephem.hour # always everything in UTC

for i in range(24*4): # compute position for every 15 minutes
    m.compute(g)

    nnm = ephem.next_new_moon(g.date)
    pnm = ephem.previous_new_moon(g.date)
    # for use w. moon_phases.ttf A -> just past  newmoon,
    # Z just before newmoon
    # '0' is full, '1' is new
    # note that we cannot use m.phase as this is the percentage of the moon
    # that is illuminated which is not the same as the phase!
    lunation=(g.date-pnm)/(nnm-pnm)
    symbol=lunation*26
    if symbol < 0.2 or symbol > 25.8 :
        symbol = '1'  # new moon
    else:
        symbol = chr(ord('A')+int(symbol+0.5)-1)

    print(ephem.localtime(g.date).time(), deg(m.alt),deg(m.az),
      ephem.localtime(g.date).time().strftime("%H%M"),
      m.phase,symbol)
    g.date += ephem.minute*15

If you run the script a list of numbers is produced that may be processed by Gnuplot. The list looks something like:

00:00:00.000002 7.91511093193 276.570014287 0000 45.1640434265 G
00:15:00.000002 5.7418040753 279.412684378 0015 45.262512207 G
00:29:59.000002 3.61571815838 282.256638545 0029 45.3610076904 G
00:44:59.000002 1.57919683222 285.110182297 0044 45.4595336914 G
...
The first column is the time, the second the elevation above the horizon, the third the azimuth (the compass direction), the forth the time again in a compact HHMM format and the final column holds a letter that will be used to display the phase of the moon with the help of the moon_phases.ttf font.

Assuming you saved the data in a file m.dat the following bit of gnuplot will create a picture called moon.png similar to the one at the top of the page (if you saved it as moon.plot you can run it with gnuplot moon.plot)

set terminal png truecolor nocrop font "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf" 12 size 640,480
set output 'moon.png'
set grid xtics nomxtics ytics nomytics noztics nomztics \
 nox2tics nomx2tics noy2tics nomy2tics nocbtics nomcbtics
set grid layerdefault   linetype 0 linewidth 1.000,  linetype 0 linewidth 1.000
set xtics border in scale 1,0.5 mirror norotate  offset character 0, 0, 0
set xtics 45 norangelimit
set ytics border in scale 1,0.5 mirror norotate  offset character 0, 0, 0
set ytics autofreq  norangelimit
set xlabel "Azimuth (compass direction, in degrees)"
set xlabel  offset character 0, 0, 0 font "" textcolor lt -1 norotate
set xrange [ 45.0000 : 315.000 ] noreverse nowriteback
set ylabel "Elevation (degrees above horizon)"
set ylabel  offset character 0, 0, 0 font "" textcolor lt -1 rotate by -270
set yrange [ 0.000000 : 90.0000 ] noreverse nowriteback
plot '/tmp/m.dat'u 3:2 w l lc 4 lw 2 t "moon path", '/tmp/m.dat' u 3:2:(10) every 4 w circles t "" lc rgb "#ddddff", '/tmp/m.dat' u 3:2:6 every 4 w labels offset character 0,character -1 font "/home/michel/sitescripts/sunrise/moon_phases.ttf,30"   t "", '/tmp/m.dat'u 3:2:4 every 4 w labels font "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf,8" tc rgb "#dd0000" t ""
#    EOF

The crucial bit is the final (and very long) plot. It draws several subplots:

  • a line representing the path of the moon
  • a series of circles with a radius of 10 in pale blue to be used as a backdrop for the moon phase characters
  • a series of labels that represent the phases of the moon (you must explicitely point to the location of the font)
  • and a final set of labels that use the values from the column with the short time format

1 comment:

  1. Hey, thanx for the code, really useful. I have a question, I would just like to calculate moon phase for a single (given) date, and get back the number between 0 and 1 for its phase. Do you have any suggestions on how to do that?

    ReplyDelete