kopia lustrzana https://github.com/peterhinch/micropython-samples
astronomy: Update docs and code.
rodzic
baf4251274
commit
b3f27dfae0
|
@ -7,6 +7,7 @@
|
||||||
2. [The RiSet class](./README.md#2-the-riset-class)
|
2. [The RiSet class](./README.md#2-the-riset-class)
|
||||||
2.1 [Constructor](./README.md#21-constructor)
|
2.1 [Constructor](./README.md#21-constructor)
|
||||||
2.2 [Methods](./README.md#22-methods)
|
2.2 [Methods](./README.md#22-methods)
|
||||||
|
2.3 [Effect of local time](./README.md#23-effect-of-local-time)
|
||||||
3. [The moonphase function](./README.md#3-the-moonphase-function)
|
3. [The moonphase function](./README.md#3-the-moonphase-function)
|
||||||
4. [Utility functions](./README.md#4-utility-functions)
|
4. [Utility functions](./README.md#4-utility-functions)
|
||||||
5. [Demo script](./README.md#5-demo-script)
|
5. [Demo script](./README.md#5-demo-script)
|
||||||
|
@ -94,7 +95,8 @@ rise or set event.
|
||||||
Args (float):
|
Args (float):
|
||||||
* `lat=LAT` Latitude in degrees (-ve is South). Defaults are my location. :)
|
* `lat=LAT` Latitude in degrees (-ve is South). Defaults are my location. :)
|
||||||
* `long=LONG` Longitude in degrees (-ve is West).
|
* `long=LONG` Longitude in degrees (-ve is West).
|
||||||
* `lto=0` Local time offset in hours to UTC (-ve is West).
|
* `lto=0` Local time offset in hours to UTC (-ve is West); the value is checked
|
||||||
|
to ensure `-12 < lto < 12`. See [section 2.3](./README.md#23-effect-of-local-time).
|
||||||
|
|
||||||
## 2.2 Methods
|
## 2.2 Methods
|
||||||
|
|
||||||
|
@ -114,6 +116,7 @@ moon, 0.5 is full. See [section 3](./README.md#3-the-moonphase-function) for
|
||||||
observations about this.
|
observations about this.
|
||||||
* `set_lto(t)` Set localtime offset in hours relative to UTC. Primarily intended
|
* `set_lto(t)` Set localtime offset in hours relative to UTC. Primarily intended
|
||||||
for daylight saving time. Rise and set times are updated if the lto is changed.
|
for daylight saving time. Rise and set times are updated if the lto is changed.
|
||||||
|
The value is checked to ensure `-12 < lto < 12`. See [section 2.3](./README.md#23-effect-of-local-time).
|
||||||
|
|
||||||
The return value of the rise and set method is determined by the `variant` arg.
|
The return value of the rise and set method is determined by the `variant` arg.
|
||||||
In all cases rise and set events are identified which occur in the current 24
|
In all cases rise and set events are identified which occur in the current 24
|
||||||
|
@ -132,6 +135,27 @@ r = RiSet() # UK near Manchester
|
||||||
r = RiSet(lat=47.609722, long=-122.3306, lto=-8) # Seattle 47°36′35″N 122°19′59″W
|
r = RiSet(lat=47.609722, long=-122.3306, lto=-8) # Seattle 47°36′35″N 122°19′59″W
|
||||||
r = RiSet(lat=-33.87667, long=151.21, lto=11) # Sydney 33°52′04″S 151°12′36″E
|
r = RiSet(lat=-33.87667, long=151.21, lto=11) # Sydney 33°52′04″S 151°12′36″E
|
||||||
```
|
```
|
||||||
|
## 2.3 Effect of local time
|
||||||
|
|
||||||
|
MicroPython has no concept of local time. A hardware platform has a clock;
|
||||||
|
depending on application this might be permanently set to local winter time, or
|
||||||
|
it might be adjusted twice per year for local daylight saving time. It is the
|
||||||
|
responsibility of the application to do this if it is considered necessary.
|
||||||
|
|
||||||
|
Rise and set times are computed relative to UTC and then adjusted using the
|
||||||
|
`RiSet` instance's local time offset before being returned (see `.adjust()`).
|
||||||
|
This applies to all variants - note that the local platform epoch is on a fixed
|
||||||
|
date at 00:00:00 local time.
|
||||||
|
|
||||||
|
If the machine clock has a fixed relationship to UTC, `RiSet` instances should
|
||||||
|
have a corresponding fixed local time offset: rise and set times will be
|
||||||
|
relative to that time. If the application implements daylight saving time, the
|
||||||
|
local time offsets should be adjusted accordingly.
|
||||||
|
|
||||||
|
The constructor and the `set_day()` method set the instance's date relative to
|
||||||
|
the machine clock. They use only the date component of system time, hence they
|
||||||
|
may be run at any time of day. In continuously-running applications, `set_day()`
|
||||||
|
may be run each day just after midnight to keep a `RiSet` instance up to date.
|
||||||
|
|
||||||
# 3. The moonphase function
|
# 3. The moonphase function
|
||||||
|
|
||||||
|
@ -203,22 +227,31 @@ Code comments show times retrieved from `timeanddate.com`.
|
||||||
# 6. Scheduling events
|
# 6. Scheduling events
|
||||||
|
|
||||||
A likely use case is to enable events to be timed relative to sunrise and set.
|
A likely use case is to enable events to be timed relative to sunrise and set.
|
||||||
In simple cases this can be done with `asyncio`. This routine, called before
|
In simple cases this can be done with `asyncio`. This coroutine will execute a
|
||||||
sunrise, will perform some action at dawn and quit:
|
payload at sunrise every day. A similar coroutine might handle sunsets.
|
||||||
```python
|
```python
|
||||||
from sched.sun_moon import RiSet
|
import uasyncio as asyncio
|
||||||
import time
|
import time
|
||||||
rs = RiSet()
|
from sched.sun_moon import RiSet
|
||||||
async def wait_for_sunrise():
|
|
||||||
tsecs = time.time() % 86400 # Time now in secs since midnight
|
async def tomorrow(): # Wait until 1 minute past midnight
|
||||||
rs.set_day() # Ensure today's date
|
now = round(time.time())
|
||||||
twait = rs.sunrise() - tsecs # Time before Sunrise
|
tw = 86400 + 60 - (now % 86400) # Time from now to one minute past next midnight
|
||||||
if twait > 0: # Sunrise has not yet occurred
|
await asyncio.sleep(tw)
|
||||||
await asyncio.sleep(twait)
|
|
||||||
# Turn the lights off, or whatever
|
async def do_sunrise():
|
||||||
|
rs = RiSet() # May need args
|
||||||
|
while True:
|
||||||
|
if (now := round(time.time())) < rs.sunrise(1): # Sun has not yet risen
|
||||||
|
await asyncio.sleep(rs.sunrise(1) - now) # Wait for it to rise
|
||||||
|
# Sun has risen, execute payload
|
||||||
|
await tomorrow()
|
||||||
|
rs.set_day() # Update to new day
|
||||||
```
|
```
|
||||||
The problem with the above is ensuring that `wait_for_sunrise` is called shortly
|
This code assumes that `.sunrise()` will never return `None`. At polar latitudes
|
||||||
after midnight. A simple solution is to use the
|
waiting for sunrise in winter would require changes.
|
||||||
|
|
||||||
|
Code may be simplified by using the
|
||||||
[schedule module](https://github.com/peterhinch/micropython-async/blob/master/v3/docs/SCHEDULE.md).
|
[schedule module](https://github.com/peterhinch/micropython-async/blob/master/v3/docs/SCHEDULE.md).
|
||||||
This may be installed with
|
This may be installed with
|
||||||
```bash
|
```bash
|
||||||
|
|
|
@ -23,6 +23,10 @@ LONG = -2.102811634540558
|
||||||
# (date(2000, 1, 1) - date(1970, 1, 1)).days = 10957
|
# (date(2000, 1, 1) - date(1970, 1, 1)).days = 10957
|
||||||
|
|
||||||
# Return time now in days relative to platform epoch.
|
# Return time now in days relative to platform epoch.
|
||||||
|
# System time is set to local time, and MP has no concept of this. Hence
|
||||||
|
# time.time() returns secs since epoch 00:00:00 local time. If lto is local time
|
||||||
|
# offset to UTC, provided -12 < lto < 12, the effect of rounding ensures the
|
||||||
|
# right number of days for platform epoch at UTC.
|
||||||
def now_days() -> int:
|
def now_days() -> int:
|
||||||
secs_per_day = 86400 # 24 * 3600
|
secs_per_day = 86400 # 24 * 3600
|
||||||
t = time.time()
|
t = time.time()
|
||||||
|
@ -77,7 +81,7 @@ def quad(ym, yz, yp):
|
||||||
# hence the MJD of an integer day number will always be an integer
|
# hence the MJD of an integer day number will always be an integer
|
||||||
|
|
||||||
# Re platform comparisons get_mjd returns the same value regardless of
|
# Re platform comparisons get_mjd returns the same value regardless of
|
||||||
# the platform's epoch: integer days since 00:00 on 17 November 1858.
|
# the platform's epoch: integer days since 00:00 UTC on 17 November 1858.
|
||||||
def get_mjd(ndays: int = 0) -> int:
|
def get_mjd(ndays: int = 0) -> int:
|
||||||
secs_per_day = 86400 # 24 * 3600
|
secs_per_day = 86400 # 24 * 3600
|
||||||
days_from_epoch = now_days() + ndays # Days since platform epoch
|
days_from_epoch = now_days() + ndays # Days since platform epoch
|
||||||
|
@ -198,6 +202,8 @@ class RiSet:
|
||||||
self.sglat = sin(radians(lat))
|
self.sglat = sin(radians(lat))
|
||||||
self.cglat = cos(radians(lat))
|
self.cglat = cos(radians(lat))
|
||||||
self.long = long
|
self.long = long
|
||||||
|
if not -12 < lto < 12:
|
||||||
|
raise ValueError("Invalid local time offset.")
|
||||||
self.lto = round(lto * 3600) # Localtime offset in secs
|
self.lto = round(lto * 3600) # Localtime offset in secs
|
||||||
self.mjd = None # Current integer MJD
|
self.mjd = None # Current integer MJD
|
||||||
# Times in integer secs from midnight on current day (in local time)
|
# Times in integer secs from midnight on current day (in local time)
|
||||||
|
@ -237,23 +243,37 @@ class RiSet:
|
||||||
def moonphase(self) -> float:
|
def moonphase(self) -> float:
|
||||||
return self._phase
|
return self._phase
|
||||||
|
|
||||||
# May need to temporarily adjust self.mjd
|
|
||||||
# def is_up(self, sun=True):
|
|
||||||
# t = ((time.time() % 86400) + self.lto) / 3600
|
|
||||||
# return sin_alt(t, sun) > 0
|
|
||||||
|
|
||||||
def set_lto(self, t): # Update the offset from UTC
|
def set_lto(self, t): # Update the offset from UTC
|
||||||
|
if not -12 < t < 12:
|
||||||
|
raise ValueError("Invalid local time offset.")
|
||||||
lto = round(t * 3600) # Localtime offset in secs
|
lto = round(t * 3600) # Localtime offset in secs
|
||||||
if self.lto != lto: # changed
|
if self.lto != lto: # changed
|
||||||
self.lto = lto
|
self.lto = lto
|
||||||
self.update(self.mjd)
|
self.update(self.mjd)
|
||||||
|
|
||||||
def is_up(self, sun: bool): # Return current state of sun or moon
|
def is_up(self, sun: bool): # Return current state of sun or moon
|
||||||
t = time.time() + self.lto # UTC
|
|
||||||
t %= 86400
|
|
||||||
t /= 3600 # UTC Hour of day
|
|
||||||
self.set_day() # Ensure today's date
|
self.set_day() # Ensure today's date
|
||||||
return self.sin_alt(t, sun) > 0
|
now = round(time.time()) + self.lto # UTC
|
||||||
|
rt = self.sunrise(1) if sun else self.moonrise(1)
|
||||||
|
st = self.sunset(1) if sun else self.moonset(1)
|
||||||
|
if rt is None:
|
||||||
|
if st is None:
|
||||||
|
t = (now % 86400) / 3600 # Time as UTC hours (float)
|
||||||
|
return self.sin_alt(t, sun) > 0
|
||||||
|
return st > now
|
||||||
|
if st is None:
|
||||||
|
return rt < now
|
||||||
|
print(rt, now, st)
|
||||||
|
return rt < now < st
|
||||||
|
|
||||||
|
# This is in error by ~12 minutes: sin_alt() produces incorrect values
|
||||||
|
# unless t corresponds to an exact hour. Odd.
|
||||||
|
# def is_up(self, sun: bool):
|
||||||
|
# t = time.time() + self.lto # UTC
|
||||||
|
# t %= 86400
|
||||||
|
# t /= 3600 # UTC Hour of day
|
||||||
|
# self.set_day() # Ensure today's date
|
||||||
|
# return self.sin_alt(t, sun) > 0
|
||||||
|
|
||||||
# ***** API end *****
|
# ***** API end *****
|
||||||
# Re-calculate rise and set times
|
# Re-calculate rise and set times
|
||||||
|
|
Ładowanie…
Reference in New Issue