astronomy: Apply algorithm improvements.

master
Peter Hinch 2023-12-04 12:01:27 +00:00
rodzic d3790626c4
commit 9ef5dd7f96
3 zmienionych plików z 45 dodań i 34 usunięć

Wyświetl plik

@ -14,6 +14,13 @@ algorithms I am unlikely to be able to offer an opinion, still less a fix.
The code is currently under development: the API may change. The code is currently under development: the API may change.
## Applications
There are two application areas. Firstly timing of events relative to sun or
moon rise and set times, discussed later in this doc. Secondly constructing
lunar clocks such as this one - the "lunartick":
![Image](./lunartick.jpg)
## Licensing and acknowledgements ## Licensing and acknowledgements
The code was ported from C/C++ as presented in "Astronomy on the Personal The code was ported from C/C++ as presented in "Astronomy on the Personal

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 514 KiB

Wyświetl plik

@ -7,6 +7,8 @@
# ISBN 978-3-540-67221-0 # ISBN 978-3-540-67221-0
# Also contributions from Raul Kompaß and Marcus Mendenhall: see # Also contributions from Raul Kompaß and Marcus Mendenhall: see
# https://github.com/orgs/micropython/discussions/13075 # https://github.com/orgs/micropython/discussions/13075
# Raul Kompaß perfomed major simplification of the maths for deriving rise and set_times.
import time import time
from math import sin, cos, sqrt, fabs, atan, radians, floor, pi from math import sin, cos, sqrt, fabs, atan, radians, floor, pi
@ -127,10 +129,10 @@ def minisun(t):
x = cos(l) x = cos(l)
y = coseps * sl y = coseps * sl
z = sineps * sl z = sineps * sl
rho = sqrt(1 - z * z) # rho = sqrt(1 - z * z)
# dec = (360.0 / 2 * pi) * atan(z / rho) # dec = (360.0 / 2 * pi) * atan(z / rho)
ra = ((48.0 / (2 * pi)) * atan(y / (x + rho))) % 24 # ra = ((48.0 / (2 * pi)) * atan(y / (x + rho))) % 24
return z, rho, ra return x, y, z
def minimoon(t): def minimoon(t):
@ -185,10 +187,10 @@ def minimoon(t):
w = sin(b_moon) w = sin(b_moon)
y = coseps * v - sineps * w y = coseps * v - sineps * w
z = sineps * v + coseps * w z = sineps * v + coseps * w
rho = sqrt(1.0 - z * z) # rho = sqrt(1.0 - z * z)
# dec = (360.0 / 2 * pi) * atan(z / rho) # dec = (360.0 / 2 * pi) * atan(z / rho)
ra = ((48.0 / (2 * pi)) * atan(y / (x + rho))) % 24 # ra = ((48.0 / (2 * pi)) * atan(y / (x + rho))) % 24
return z, rho, ra return x, y, z
class RiSet: class RiSet:
@ -205,6 +207,7 @@ class RiSet:
# ***** API start ***** # ***** API start *****
# Examine Julian dates either side of current one to cope with localtime. # Examine Julian dates either side of current one to cope with localtime.
# 707μs on RP2040 at standard clock and with local time == UTC
def set_day(self, day: int = 0): def set_day(self, day: int = 0):
mjd = get_mjd(day) mjd = get_mjd(day)
if self.mjd is None or self.mjd != mjd: if self.mjd is None or self.mjd != mjd:
@ -234,6 +237,11 @@ 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
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
@ -251,18 +259,16 @@ class RiSet:
mr, ms = self.rise_set(False) # Moon mr, ms = self.rise_set(False) # Moon
# Adjust for local time. Store in ._times if value is in 24-hour # Adjust for local time. Store in ._times if value is in 24-hour
# local time window # local time window
self.adjust(sr, day, 0) self.adjust((sr, ss, mr, ms), day)
self.adjust(ss, day, 1)
self.adjust(mr, day, 2)
self.adjust(ms, day, 3)
self.mjd = mjd self.mjd = mjd
def adjust(self, n, day, idx): def adjust(self, times, day):
if n is not None: for idx, n in enumerate(times):
n += self.lto + (day - 1) * 86400 if n is not None:
h = n // 3600 n += self.lto + (day - 1) * 86400
if 0 <= h < 24: h = n // 3600
self._times[idx] = n if 0 <= h < 24:
self._times[idx] = n
def _format(self, n, variant): def _format(self, n, variant):
if variant == 0: # Default: secs since Midnight (local time) if variant == 0: # Default: secs since Midnight (local time)
@ -278,9 +284,9 @@ class RiSet:
return f"{hr:02d}:{mi:02d}:{sec:02d}" return f"{hr:02d}:{mi:02d}:{sec:02d}"
# See https://github.com/orgs/micropython/discussions/13075 # See https://github.com/orgs/micropython/discussions/13075
def lmst(self, t): def lstt(self, t):
# Takes the mjd and the longitude (west negative) and then returns # Takes the mjd and the longitude (west negative) and then returns
# the local sidereal time in hours. Im using Meeus formula 11.4 # the local sidereal time in degrees. Im using Meeus formula 11.4
# instead of messing about with UTo and so on # instead of messing about with UTo and so on
# modified to use the pre-computed 't' value from sin_alt # modified to use the pre-computed 't' value from sin_alt
d = t * 36525 d = t * 36525
@ -289,24 +295,23 @@ class RiSet:
c2 = 0.98564736629 c2 = 0.98564736629
dsum = c1 * df + c2 * d # no large integer * 360 here dsum = c1 * df + c2 * d # no large integer * 360 here
lst = 280.46061837 + dsum + 0.000387933 * t * t - t * t * t / 38710000 lst = 280.46061837 + dsum + 0.000387933 * t * t - t * t * t / 38710000
return (lst % 360) / 15.0 + self.long / 15 return lst % 360
def sin_alt(self, hour, func): def sin_alt(self, hour, sun):
# Returns the sine of the altitude of the object (moon or sun) # Returns the sine of the altitude of the object (moon or sun)
# at an hour relative to the current date (mjd) # at an hour relative to the current date (mjd)
mjd1 = (self.mjd - 51544.5) + hour / 24.0 func = minisun if sun else minimoon
# mjd1 = self.mjd + hour / 24.0 mjd = (self.mjd - 51544.5) + hour / 24.0
t1 = mjd1 / 36525.0 # mjd = self.mjd + hour / 24.0
sin_dec, cos_dec, ra = func(t1) t = mjd / 36525.0
# hour angle of object: one hour = 15 degrees. Note lmst() uses longitude x, y, z = func(t)
tau = 15.0 * (self.lmst(t1) - ra) tl = self.lstt(t) + self.long # Local mean sidereal time adjusted for logitude
# sin(alt) of object using the conversion formulas return self.sglat * z + self.cglat * (x * cos(radians(tl)) + y * sin(radians(tl)))
return self.sglat * sin_dec + self.cglat * cos_dec * cos(radians(tau))
# Modified to find sunrise and sunset only, not twilight events. # Modified to find sunrise and sunset only, not twilight events.
# Calculate rise and set times of sun or moon for the current MJD. Times are
# relative to that 24 hour period.
def rise_set(self, sun): def rise_set(self, sun):
# this is my attempt to encapsulate most of the program in a function
# then this function can be generalised to find all the Sun events.
t_rise = None # Rise and set times in secs from midnight t_rise = None # Rise and set times in secs from midnight
t_set = None t_set = None
sinho = sin(radians(-0.833)) if sun else sin(radians(8 / 60)) sinho = sin(radians(-0.833)) if sun else sin(radians(8 / 60))
@ -315,12 +320,11 @@ class RiSet:
# The loop finds the sin(alt) for sets of three consecutive # The loop finds the sin(alt) for sets of three consecutive
# hours, and then tests for a single zero crossing in the interval # hours, and then tests for a single zero crossing in the interval
# or for two zero crossings in an interval for for a grazing event # or for two zero crossings in an interval for for a grazing event
func = minisun if sun else minimoon yp = self.sin_alt(0, sun) - sinho
yp = self.sin_alt(0, func) - sinho
for hour in range(1, 24, 2): for hour in range(1, 24, 2):
ym = yp ym = yp
yz = self.sin_alt(hour, func) - sinho yz = self.sin_alt(hour, sun) - sinho
yp = self.sin_alt(hour + 1, func) - sinho yp = self.sin_alt(hour + 1, sun) - sinho
nz, z1, z2, ye = quad(ym, yz, yp) # Find horizon crossings nz, z1, z2, ye = quad(ym, yz, yp) # Find horizon crossings
if nz == 1: # One crossing found if nz == 1: # One crossing found
if ym < 0.0: if ym < 0.0: