@@ -115,13 +115,25 @@ class MoonData():
115
115
rise : Epoch time of moon rise within this 24-hour period.
116
116
set : Epoch time of moon set within this 24-hour period.
117
117
"""
118
- def __init__ (self , datetime , utc_offset ):
118
+ def __init__ (self , datetime , hours_ahead , utc_offset ):
119
119
""" Initialize MoonData object elements (see above) from a
120
- time.struct_time and a UTC offset (as a string) and a query
121
- to the MET Norway Sunrise API (also provides lunar data),
122
- documented at:
120
+ time.struct_time, hours to skip ahead (typically 0 or 24),
121
+ and a UTC offset (as a string) and a query to the MET Norway
122
+ Sunrise API (also provides lunar data), documented at:
123
123
https://api.met.no/weatherapi/sunrise/2.0/documentation
124
124
"""
125
+ if hours_ahead :
126
+ # Can't change attribute in datetime struct, need to create
127
+ # a new one which will roll the date ahead as needed. Convert
128
+ # to epoch seconds and back for the offset to work
129
+ datetime = time .localtime (time .mktime (time .struct_time (
130
+ datetime .tm_year ,
131
+ datetime .tm_mon ,
132
+ datetime .tm_mday ,
133
+ datetime .tm_hour + hours_ahead ,
134
+ datetime .tm_min ,
135
+ datetime .tm_sec ,
136
+ - 1 , - 1 , - 1 )))
125
137
# strftime() not available here
126
138
url = ('https://api.met.no/weatherapi/sunrise/2.0/.json?lat=' +
127
139
str (LATITUDE ) + '&lon=' + str (LONGITUDE ) +
@@ -140,32 +152,23 @@ def __init__(self, datetime, utc_offset):
140
152
self .age = float (moon_data ['moonphase' ]['value' ]) / 100
141
153
self .midnight = time .mktime (parse_time (
142
154
moon_data ['moonphase' ]['time' ]))
143
- self .rise = time .mktime (
144
- parse_time (moon_data ['moonrise' ]['time' ]))
145
- self .set = time .mktime (
146
- parse_time (moon_data ['moonset' ]['time' ]))
155
+ if 'moonrise' in moon_data :
156
+ self .rise = time .mktime (
157
+ parse_time (moon_data ['moonrise' ]['time' ]))
158
+ else :
159
+ self .rise = None
160
+ if 'moonset' in moon_data :
161
+ self .set = time .mktime (
162
+ parse_time (moon_data ['moonset' ]['time' ]))
163
+ else :
164
+ self .set = None
147
165
return # Success!
148
166
except :
149
167
# Moon server error (maybe), try again after 15 seconds.
150
168
# (Might be a memory error, that should be handled different)
151
169
time .sleep (15 )
152
170
153
171
154
- def next_moon_data (datetime , utc_offset ):
155
- """ Given a time.struct_time, compute and return a MoonData object
156
- for the NEXT 24 hour period ahead.
157
- """
158
- tomorrow = time .struct_time (datetime .tm_year ,
159
- datetime .tm_mon ,
160
- datetime .tm_mday ,
161
- datetime .tm_hour + 24 ,
162
- datetime .tm_min ,
163
- datetime .tm_sec ,
164
- - 1 , - 1 , - 1 )
165
- # Convert to epoch seconds and back for the +24 hour offset to work
166
- return MoonData (time .localtime (time .mktime (tomorrow )), utc_offset )
167
-
168
-
169
172
# ONE-TIME INITIALIZATION --------------------------------------------------
170
173
171
174
MATRIX = Matrix (bit_depth = BITPLANES )
@@ -257,11 +260,14 @@ def next_moon_data(datetime, utc_offset):
257
260
DATETIME , UTC_OFFSET = time .localtime (), '+00:00'
258
261
LAST_SYNC = time .mktime (DATETIME )
259
262
260
- # Poll server for moon data for current 24-hour period
261
- CURRENT_PERIOD = MoonData (DATETIME , UTC_OFFSET )
262
-
263
- # Then do same for 24 hours ahead...
264
- NEXT_PERIOD = next_moon_data (DATETIME , UTC_OFFSET )
263
+ # Poll server for moon data for current 24-hour period and +24 ahead
264
+ PERIOD = []
265
+ for DAY in range (2 ):
266
+ PERIOD .append (MoonData (DATETIME , DAY * 24 , UTC_OFFSET ))
267
+ # PERIOD[0] is the current 24-hour time period we're in. PERIOD[1] is the
268
+ # following 24 hours. Data is shifted down and new data fetched as days
269
+ # expire. Thought we might need a PERIOD[2] for certain circumstances but
270
+ # it appears not, that's changed easily enough if needed.
265
271
266
272
267
273
# MAIN LOOP ----------------------------------------------------------------
@@ -283,29 +289,28 @@ def next_moon_data(datetime, utc_offset):
283
289
# the server with repeated queries).
284
290
LAST_SYNC += 30 * 60 * 60 # 30 minutes -> seconds
285
291
286
- # If NEXT_PERIOD has expired, move that to CURRENT_PERIOD and fetch anew
287
- if NOW >= NEXT_PERIOD .midnight :
288
- CURRENT_PERIOD = NEXT_PERIOD
289
- NEXT_PERIOD = next_moon_data (time .localtime (), UTC_OFFSET )
290
- continue # NOW is stale due to server query time; refresh
292
+ # If PERIOD has expired, move data down and fetch new +24-hour data
293
+ if NOW >= PERIOD [1 ].midnight :
294
+ PERIOD [0 ] = PERIOD [1 ]
295
+ PERIOD [1 ] = MoonData (time .localtime (), 24 , UTC_OFFSET )
291
296
292
297
# Determine weighting of tomorrow's phase vs today's, using current time
293
- RATIO = ((NOW - CURRENT_PERIOD .midnight ) /
294
- (NEXT_PERIOD .midnight - CURRENT_PERIOD .midnight ))
298
+ RATIO = ((NOW - PERIOD [ 0 ] .midnight ) /
299
+ (PERIOD [ 1 ] .midnight - PERIOD [ 0 ] .midnight ))
295
300
# Determine moon phase 'age'
296
301
# 0.0 = new moon
297
302
# 0.25 = first quarter
298
303
# 0.5 = full moon
299
304
# 0.75 = last quarter
300
305
# 1.0 = new moon
301
- if CURRENT_PERIOD .age < NEXT_PERIOD .age :
302
- AGE = (CURRENT_PERIOD .age +
303
- (NEXT_PERIOD .age - CURRENT_PERIOD .age ) * RATIO ) % 1.0
306
+ if PERIOD [ 0 ] .age < PERIOD [ 1 ] .age :
307
+ AGE = (PERIOD [ 0 ] .age +
308
+ (PERIOD [ 1 ] .age - PERIOD [ 0 ] .age ) * RATIO ) % 1.0
304
309
else : # Handle age wraparound (1.0 -> 0.0)
305
310
# If tomorrow's age is less than today's, it indicates a new moon
306
311
# crossover. Add 1 to tomorrow's age when computing age delta.
307
- AGE = (CURRENT_PERIOD .age +
308
- (NEXT_PERIOD .age + 1 - CURRENT_PERIOD .age ) * RATIO ) % 1.0
312
+ AGE = (PERIOD [ 0 ] .age +
313
+ (PERIOD [ 1 ] .age + 1 - PERIOD [ 0 ] .age ) * RATIO ) % 1.0
309
314
310
315
# AGE can be used for direct lookup to moon bitmap (0 to 99) -- these
311
316
# images are pre-rendered for a linear timescale (solar terminator moves
@@ -318,26 +323,22 @@ def next_moon_data(datetime, utc_offset):
318
323
else : # Full -> last quarter -> new
319
324
PERCENT = (1 + math .cos ((AGE - 0.5 ) * 2 * math .pi )) * 50
320
325
321
- if CURRENT_PERIOD . set > CURRENT_PERIOD . rise :
322
- if CURRENT_PERIOD . rise < NOW < CURRENT_PERIOD . set :
323
- RISEN = True
324
- NEXT_EVENT = CURRENT_PERIOD . set
325
- else :
326
- RISEN = False
327
- if NOW < CURRENT_PERIOD . rise :
328
- NEXT_EVENT = CURRENT_PERIOD . rise
329
- else :
330
- NEXT_EVENT = NEXT_PERIOD . rise
331
- else :
332
- if CURRENT_PERIOD . set < NOW < CURRENT_PERIOD .rise :
326
+ # Find next rise/set event, complicated by the fact that some 24-hour
327
+ # periods might not have one or the other (but usually do) due to the
328
+ # Moon rising ~50 mins later each day. This uses a brute force approach,
329
+ # working backwards through the time periods to locate rise/ set events
330
+ # that A) exist in that 24-hour period (are not None), B) are still in
331
+ # the future, and C) are closer than the last guess. What's left at the
332
+ # end is the next rise or set (and the inverse of the event type tells
333
+ # us whether Moon's currently risen or not).
334
+ NEXT_EVENT = PERIOD [ 1 ]. midnight + 100000 # Force first match
335
+ for DAY in reversed ( PERIOD ):
336
+ if DAY . rise and NEXT_EVENT >= DAY . rise >= NOW :
337
+ NEXT_EVENT = DAY .rise
333
338
RISEN = False
334
- NEXT_EVENT = CURRENT_PERIOD . rise
335
- else :
339
+ if DAY . set and NEXT_EVENT >= DAY . set >= NOW :
340
+ NEXT_EVENT = DAY . set
336
341
RISEN = True
337
- if NOW < CURRENT_PERIOD .set :
338
- NEXT_EVENT = CURRENT_PERIOD .set
339
- else :
340
- NEXT_EVENT = NEXT_PERIOD .set
341
342
342
343
if DISPLAY .rotation in (0 , 180 ): # Horizontal 'landscape' orientation
343
344
CENTER_X = 48 # Text along right
0 commit comments