The previous blog discussed the design vocalising weather reports from online weather stations. However on the boat we dont usually have access to the internet and have to rely on our own measurements and observations. Yachtsman like to keep an eye on the barometer in case of approaching bad weather which in areas where the passage of lows is common is signaled by rapidly falling air pressure. This barometric pressure is one of the entries in the hourly log entries we make. However its easy to miss a change in pressure, especially at anchor when no log entries are being made. So one component of my talking yacht will be a talking barometer.
After trying various configurations, I've opted for separate Unix processes. One process does the monitoring, another watches the barometer and vocalising the barometric pressure and yet another provides the menu interface to the presenter. I've also added the talking clock which is controlled by the same menu. I'm helped in this application because voice messages are implicitly merged, so voice output from the controller, the clock and the talking barometer can run independantly provided the occassional collision can be tolerated. Input however goes only to the active process so a single menu-driven controller has to control the behaviour of the other processes. The controller also has to fetch current data, such as barometric pressure and vocalise it. So I need some shared state between these processes
After thrashing around, I've settled (for the moment) on an architecture in which processes communicate via a persistant dictionary of 'pickled' objects. Persistant objects have a unique name and timestamp and have a put() method to save to a file using the name, together with a get(name) function to recover an object given its name. The use of named files is a temporary approach while I find something more efficient. Persistant objects inherit from the Persistant class.
A persistant object is needed to allow the menu controller to turn talkers on and off. A simple Switch class does this:
The simple clock starts up 'on' and then in the timing loop, reads the stored Switch object to see if it should talk.
Actions in the menu get the status of the clock and toggle it:
eval() can only evaluate expressions so I have to use chaining to perform a sequence of actions. This requres methods to return self by default and I was surprised that in Python the default is None.
Data acquision is performed using a small pipeline of generators:
baroReader reads the barometer in a timed loop. I haven't yet hooked up a barometer sensor to the RPi but until it arrives, I can get the software prototyped using pressure readings via HTTP from an nearby weather station. baroReader ignores missing data and could clean up bad data.
baroSmoother provides smoothed values by averaging a number of readings from baroReader.
The Barometer class contains the current reading and its computed data. It has a monitor method which reads smoothed values from baroSmoother to update its local data.
The barometer data needs to be available to other processes such as the barometer talker and the controller. So the barometer also needs to be a persistant object and to save the state using the inherited put() method when it is updated. This is not automatic so state has to be saved explicitly.
Short term weather forecasts of local weather can, to some degree of accuracy, be forecast using heuristics. Caution is needed since the heuristics presume a dominant weather regimes, such as mid-latitudes in the Northern hemispher. A simple approach uses the barometric pressure and the rate of change of pressure over the past few hours. Textual reports of the trend are called 'tendency' and is defined by the UK Met Office.
Tendency is computed from the change over the past 3 hours, so the barometer needs to retain a history of pressure readings. I designed a limited sequence class MovingSequence which maintains a ring of values so that we have access to aged readings within a limited period. Access mimics a regular sequence so that s.get(0) is the latest ,s.get(-1) is the oldest. If there are enough entries in the sequence, the change over a period can be calculated and from that the description of the tendency as used by the Met forecasts.
However we don't need to retain the history sequence when the barometer is pickled so we override the __getstate__ method to ignore this attribute:
A simple short-term forecast can be made from the current pressure and trend based on these heuristics:
Finally, all these processes need to be started:
One improvement would be to filter out the diurnal variation in temperature. Another would be to implement a more sophisticated algorithm such as is used in proprietory weather stations. After a lot of searching around, I found that these are based on variations of one of two weather forecast calculators which were in use in the '40s.
I dont know how pythonic this code is. I hope some kind reader will let me know. I'm sure the multiple processes could be implemented better and I'm re-inventing existing functions.