DESCRIPTION OF THE MOON CLOCK
The Moon clock shows the current moon rise time, its set time and the current time, all on a single 24-hour dial. A second dial inset into the main dial shows the moon phase and its current azimuth (compass direction). An LED in the phase dial is illuminated when the moon is ‘up’ (above the horizon). It is all mounted in a small wooden quartz clock bought from a charity shop.
This page outlines the software that drives the clock. I will publish the software listing within a few days – I have made a small tweak to one of the calculations and I want to be sure that it is right.
THE RASPBERRY PI
A RASPBERRY Pi computer drives the Moon Clock. If you need guidance on setting it up, you may find these pages of my blog useful:
The Raspberry Pi sits there asleep most of the time, waiting for the alarm to wake it, and after quickly completing its task, it goes back to sleep again. This means that although it is enclosed in a small box with little ventilation, it runs at a modest core temperature of about 50 degrees, maybe a couple of degrees more in the extremely hot weather we have been having. Any Raspberry Pi will be suitable as long as it has a wi-fi connection. The software is written in Python 3 and runs under Raspbian. The software does need to know the real time and I assume it uses my network’s internet connection for this.
The Moon clock software consists of a very short main routine that sets up the GPIO interface and the Alarm Signals that run the clock, and then sends it to sleep.
An alarm call once a minute wakes the system to check whether any of the hands need to be moved, to move them if needed and then to sleep for a further minute. This takes less than a second, and in fact the alarm interval could be made once every two minutes without any problem.
The system runs ‘headless’. It can be installed to run automatically on boot-up and requires no keyboard or display. However, during development I have run it from my PC using ‘Remote Desktop Connection’ (search my blog for how to install this), which allows me to check system variables and tweak the software. The system has a debug mode that allows the operational parameters to be output to a display if needed. There is a manual stepping button that allows the time and moon phase motors to be stepped manually.
I used a wooden quartz clock that I bought in a charity shop as the basis. The construction is described in my blog so is not repeated here. There is an interface board between the Pi and the hardware. This amplifies the pulses from the GPIO to drive the quartz clock motors, and to light the moon LED. It also interfaces the manual press-button to the GPIO. I designed and built the circuit onto a small stripboard, about 3.5 cm wide and 5 cm long. The circuit for this is also shown elsewhere in my blog and eventually I will produce a circuit diagram.
In this short resume, I have not attempted to explain the thinking behind my maths. I am just trying to set out the functionality. I’m sure there are more elegant approaches but the clock has developed somewhat organically as I thought about each feature. This has left a few vestigial parts that should perhaps be tidied up, but when is enough enough?
The software consists of a short main routine and a number of subroutines or functions. The subroutines are intended to separate logically discrete tasks and to give some modularity to the code. In the following, they are described roughly in the order in which they are executed.
The PyEphem Library
Before going into my software, I must mention the PyEphem library. According to its website, “PyEphem provides basic astronomical computations for the Python programming language. ” This provides the time and date calculations used by the moon clock – they are not pulled in from the internet, but calculated on the Pi.
You will need to install this library with the command:
sudo pip3 install ephem
You can read more about the PyEphem library here in my blog.
If not already installed on your Pi, other libraries you will need:
You can find much more about these if you search my blog.
The main routine is very short and performs the following functions:
- IMPORT LIBRARIES
- INITIALISE GLOBAL VARIABLES
- SET UP GPIO INTERFACE
- SET UP GPIO INTERRUPT EVENTS
- SET UP THE ALARM SIGNAL HANDLER
- LIGHT THE MOON (to show the software has started)
- SET THE ALARM SIGNAL TO ‘RING’ IN 60 SECONDS
- GO INTO WAITING LOOP
- WAIT FOR ALARM OR INTERRUPT EVENT
The main routine is paused in a Waiting Loop until the alarm ‘rings’, the manual advance button it pressed, or a Keyboard interrupt occurs. This is implemented in a Try statement. I have detailed it in my blog here.
ALARM HANDLER : handler(signum, frame)
The Alarm Handler is at the heart of the software. Although it drives the clock, it is not very long because most of the work is done by subroutines that are called by the handler. It is called whenever the Alarm ‘rings’, which is usually once a minute.
It performs the following functions:
- If the clock is not already running (the software has just started) it initialises the clock display by calling subroutine resetclock()
- Get the current time and check whether the clock needs to advance. This will only happen once every 24 minutes, as that is the amount that the time advances for one tick on the 24-hour dial.
- Check whether the phase dial needs to advance (it shows either the phase, when the moon is down, or azimuth when the moon is up)
- Check whether the moon light needs to go on or off
- Advance the clock and phase dials by the necessary number of ticks
- If the time is 1.5 hours after the set time, advance to the new rise and set times
- Set the Alarm Signal to sound in 60 seconds
- Return to waiting loop until there is another alarm or interrupt
Note that the handler routine is required to have two parameters (signum and frame) but these have no function in this routine.
Normally, it is assumed that both the time and phase dials are in the correct position when the clock is started. This is because the time dial could need 43,200 pulses and the phase dial could need 21600 pulses to get to the correct position in some cases. This would take a lot of time. It is much more likely that the clock will be restarted after only a brief pause, so the hands will be in the right place, or they can be manually set to the right place.
Note also that the Phase dial must be in the correct starting position; the Azimuth hand must either be in the correct position (when PHASE is True), or if PHASE is False, be set to 00:00 on startup.
However, if the parameter HANDS is set to False, the clock will pulse the necessary distance from a setting of 00:00
resetclock() performs the following functions:
- Call gettimes() to acquire the present, moonrise and moonset times at the observer (the code sets the observer to London, but could be any position on the globe)
- Call calcpulse() to calculate the number of pulses that the clock must advance in order to set the gettimes() values on the clock
- Call getAzimuth() to find the current azimuth of the moon (this will always have a value, even when the moon is set)
- If the clock has just started, initialise the phase parameters (the dial is assumed to be at the correct phase position);
- If the clock is already running, advance time dial to new rise and set times
This uses the ephem library to obtain the present time and the next rising and next setting time of the moon. The ephem library is general-purpose and can obtain the ‘ephemerides’ of most common celestial bodies. It is necessary to tell ephem the celestial body of interest, the location of the observer and the time of the required observation. In the present case, the observer is London, for which ephem has the latitude and longitude, but you also have to set the date-time of the observation. All times are given in UTC (universal time) and need to be converted to local time for display on the clock. This is done within the calcpulse routine.
This calculates the number of pulses to be applied to the clock motor to show the correct present time, the moon rise time and the moon set time. It is perhaps the most mathematically complex function of the clock software. Its input and output parameters are supplied by global variables.
It operates as follows.
- Get the present time in hours from midnight
- Work out the number of pulses of the s hand from 00:00 to get to this present time
- Get the rise time in hours from midnight
- Work out the number of pulses of the m hand from 00:00 to get to this rise time
- Adjust this number of pulses to allow for the pulses already being used to set the s hand.
- Add 30 pulses to this number so that the next step rounds to the nearest hour
- Find the number of pulses needed for the rise time so that the s hand is left at 00:00
- Calculate the number of pulses needed to set the h hand to the correct set time
- Adjust this for the number of pulses being added for the present and rise times
- Round this to leave the m hand at the 00:00 position
- Add the pulses for current, rise and set times to get the number of pulses needed to set the new times
- Calculate the change from the current position
- Return this number of pulses to the calling routine.
Adjustments are made to allow for correct calculation when the time passes the turnover position, eg when the set time passes midnight.
This function returns a time as the number of hours since midnight. The ephem library returns a time as the number of days since noon on 31 December 1899. Thus day 1.000 is noon on 1st January 1900. (Astronomers use noon as the start point because that is when the sun passes the meridian). This is called the datetime, and is given in the input parameter fulldate.
For use in the clock, it is converted to hours from midnight by timeinhrs(), and corrected for any Daylight Saving Time (DST) that may be active. Determination of DST requires the local time zone to be added to ephem’s datetime. This function makes use of several functions from the pyephem and pytz libraries.
This returns the phase of the moon at the defined observer at the current instant of time. The phase is a fractional number, with 0 being new moon, 0.5 being full moon and 1 being new moon. The fact that new moon can be 0 or 1 (although these precise values are unlikely to be encountered) must be taken into account.
The azimuth of the moon is always available, even when it is below the horizon. We only use the Azimuth when the moon is ‘up’, i.e. above the horizon. The value is obtained from the ephem library. You define the object (the moon), the observer (including the datetime) compute its parameters, and extract the azimuth property. This is given in radians (although confusingly, if you print the azimuth, it is shown in degrees, minutes and seconds). The routine converts radians to degrees as this seems more natural, remembering that there are 2π radians in a circle, a radian being the angle subtended by the radius when marked round the circumference of the circle.
This function allows the ‘seconds’ hands of either the time movement or the phase movement to be advanced by pushing a button on the bottom of the clock case. The main hands can be moved by the manual adjustment wheel on the movement, but the ‘seconds hand’ can only be moved by the motor. This permits manual adjustment if the clock and phase movements lose time, for example because the clock was stopped for a short period or a pulse was missed.
The button advances the azimuth hand if the moon is lit, or the time hand if the moon is not lit.
This advances the clock motor by nStep steps. nStep can be zero. If TEST is true, then the steps are not executed. This allows for the software to be tested without affecting the clock display.
doClock alternately energises GPIO pins 2 and 3. The on and off times are set by parameters pulse1on, pulse1off, pulse2on and pulse2off. This allows the mark space ratio to be adjusted to optimise the operation of the motors.
This is identical to doClock(), except that it pulses GPIO pins 17 and 27. The pulse length parameters are pulse3on, pulse3off, pulse4on and pulse4 off, to allow for different responses of the two motors.
This turns on GPIO pin 23 if moonup is True, otherwise it turns it off. This pin is connected to the LED driver on the circuit board.
This function is intended to signal to the user. It calls lightmoon() to turn the moon on and off nflash times. The ontime and offtime are given in fractional seconds. Flashmoon is a blocking function, although the handler will still be woken by pressing the button or the alarm sounding.
This is a very short routine that returns the fractional part of its input parameter a. (Python has no built-in frac function, although it is available in the add-on maths library)