Phase of the moon? It turns out that's really complicated.
Ladies and gentlemen, we've left Mars. Well, at least I'm done with the Martian lander from the past few months. I hope you had chance to experiment with it and find out that it's not too easy to land a craft on any planet!
While researching the Martian lander project, I bumped into another interesting scripting problem that relates to space. How do you ascertain the phase of the moon for a given date? There are formulas, of course, and you can do the math knowing that the lunar rotation is precisely—um...well, it's not quite that simple, actually.
Sure, you can just say that the moon orbits the Earth every 27.3 days, but that's relative to the stars, the sidereal orbit. The period between moon phases (such as a full moon) is also known as a synodic orbit, and that's 29.5 days.
So the simple task of ascertaining whether it's a full moon already has some math involved. Then there's the issue of the moon's illumination level being relative to where you are on Earth too. That makes sense. A full moon in Punta Arenas, Chile, is different from that in Lapland (though not by much).
The long and short of it is that the math behind calculating the illumination level of the moon isn't quite as simple as it may seem. You could take a known date and time of a full moon (for example, November 14 at 8:52 am EST) and keep adding precisely 29.530 days or 42,523.20 minutes.
But seriously, you also can let someone else do the work too, right? I mean, this column is just about a shell script, after all. So, let's see how Google does it! If you check Google to see the current phase of the moon, it actually references a website (moongiant.com), as shown in Figure 1.
Do a bit of digging at the Moon Giant site, and you can see that there are two basic forms of URL that produce the data desired: a specified date or just “today” as the date. Test it by going to this URL: www.moongiant.com/phase/today.
Specify a date, and the format gets a wee bit more complex: http://www.moongiant.com/phase/MM/DD/YYYY.
You can use this find out the phase of the moon on the day the new US President will be sworn in with: www.moongiant.com/phase/01/20/2017. (If you guessed it's a full moon, well, you're not right!)
This means that you can quite easily write a succinct script that tells you the current illumination level of the moon by simply using curl or GET with the first of these three URLs:
url="http://www.moongiant.com/phase/today" pattern="Illumination:" phase="$( curl -s "$url" | grep "$pattern" | tr ',' '\ ' | grep "$pattern" | sed 's/[^0-9]//g')" echo $phase
A quick run of the script as I write this (on October 3, 2016), and the output is a rather confusing: “6”. Six. What does that mean? It's actually just the illumination level with everything else scrubbed out of the output data.
A 6% illumination is close to a new moon, but not quite. The new moon was actually two days before, on October 1st.
The interesting part of the script is absolutely all in the phase= statement. Let's unwrap it and look more closely:
curl -s "$url" | grep "$pattern" | tr ',' '\ ' | grep "$pattern" | sed 's/[^0-9]//g'
First off, if you aren't familiar with curl, go read the man page. It's a terrific, quite powerful utility that lets you debug web servers, send queries to web pages as if you were various web browsers, interact with FTP servers and, of course, just grab a web page's source for further analysis. It's the latter skill I'm using for this task.
Once the source to the page is flowing in, the next step in the pipe is to extract the line that contains the illumination level. That turns out to be exactly “Illumination:”, but unfortunately, it doesn't appear by itself on the HTML source line. In fact, it's quite a complex output line! That's the job of the next two lines actually.
The invocation to tr turns every comma into a hard return, effectively breaking up one really long line into a lot of shorter lines. Then grep is invoked a second time to extract the now further isolated illumination level indicator.
Finally, superfluous data is axed by having sed remove everything that's not a digit. The end result? Input like Illumination: 6% turns into “6”, and that's stored in the variable phase. Got it?
Now the output can be enhanced:
echo "The moon's current illumination level: $phase%"
Slightly more understandable output!
Phases of the moon aren't generally described by their illumination level, however, and require knowledge of the previous day's state too, since that's how you ascertain “waxing” or “waning”.
Some are easy: 0% is a new moon, 25% is a quarter moon, 50% is a half moon, and 100% is a full moon. Or is it? Actually, there are eight phases to the moon, and 50% illumination is known as a “quarter moon”, confusingly enough.
In fact, the phase depends on where in the new moon → new moon cycle it is, so that 50% illumination prior to a full moon is the “first quarter” phase, while 50% illumination subsequent to a full moon is the “last quarter” phase—crazy complicated.
Again, let's simplify, however. So skip the waxing and waning for now and instead use the following:
0–5% = new moon.
6–45% = crescent.
46–55% = quarter.
56–95% = gibbous.
96–100% = full moon.
Now let's code that. Most easily, that can be done with a chain of if-then-else statements:
if [ $phase -lt 5 ] ; then phasename="new" elif [ $phase -lt 45 ] ; then phasename="crescent" elif [ $phase -lt 55 ] ; then phasename="quarter" elif [ $phase -lt 95 ] ; then phasename="gibbous" else phasename="full" fi
With the aesthetically pleasing results:
$ potm.sh The moon is currently crescent with 11% illuminated.
Let's stop here for this article. In my next article, I'll add the ability to analyze whether it's waxing or waning (for example, compare yesterday's illumination level with today's to see if the moon is getting brighter or darker).