Skip to content

Commit f598772

Browse files
Fix: refract() on array should return same angles
If the refract() routine was given an array of altitudes, and some of them converged to a result more quickly than the rest, then it would over-refine those early results as it kept looping. Now, it uses a mask that lets each result stop converging as soon as that result is ready. This tightens agreement with NOVAS to 0.00001 arcseconds even for positions that include the effects of refraction.
1 parent 664b1fe commit f598772

4 files changed

Lines changed: 249 additions & 219 deletions

File tree

builders/build_novas_tests.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def main():
4040
from skyfield.api import Topos, load
4141
from skyfield.constants import AU_KM, AU_M
4242
from skyfield.data import hipparcos
43-
from skyfield.functions import BytesIO, length_of
43+
from skyfield.functions import A, BytesIO, length_of
4444
from .fixes import low_precision_ERA
4545
4646
OLD_AU_KM = 149597870.691
@@ -237,15 +237,35 @@ def test_refraction{i}():
237237
compare(r, {r!r}, 1e-9 * arcsecond)
238238
""")
239239

240+
location = novas.make_on_surface(0.0, 0.0, 0, 10.0, 1010.0)
241+
angle_in = [-5, -1, 15, 89.95]
242+
angle_out = [novas.refract(location, 90 - a, 2) for a in angle_in]
243+
244+
output(locals(), """\
245+
def test_refraction_with_angle_array():
246+
r = earthlib.refraction(A{angle_in}, 10.0, 1010.0)
247+
compare(r, {angle_out!r}, 1e-9 * arcsecond)
248+
""")
249+
240250
northpole = novas.make_on_surface(90.0, 0.0, 0.0, 10.0, 1010.0)
241251

242-
for i, angle in enumerate([-90, -2, -1, 0, 1, 3, 9, 90]):
243-
alt, az = altaz_maneuver(T0, northpole, 0.0, angle, ref=2)
252+
alt_in = []
253+
alt_out = []
254+
for i, alt in enumerate([-90, -2, -1, 0, 1, 3, 9, 90]):
255+
alt2, az = altaz_maneuver(T0, northpole, 0.0, alt, ref=2)
244256
output(locals(), """\
245257
def test_refract{i}():
246-
alt = earthlib.refract({angle!r}, 10.0, 1010.0)
247-
compare(alt, {alt!r}, 1e-9 * arcsecond)
258+
alt = earthlib.refract({alt!r}, 10.0, 1010.0)
259+
compare(alt, {alt2!r}, 1e-9 * arcsecond)
248260
""")
261+
alt_in.append(alt)
262+
alt_out.append(alt2)
263+
264+
output(locals(), """\
265+
def test_refract_with_angle_array():
266+
r = earthlib.refract(A{alt_in}, 10.0, 1010.0)
267+
compare(r, {alt_out!r}, 1e-9 * arcsecond)
268+
""")
249269

250270
usno = novas.make_on_surface(38.9215, -77.0669, 92.0, 10.0, 1010.0)
251271

@@ -408,12 +428,12 @@ def test_{slug}_topocentric_date{i}(de405):
408428
compare(az.degrees, {az!r}, 0.00001 * arcsecond)
409429
410430
alt, az, distance = apparent.altaz('standard')
411-
compare(alt.degrees, {alt2!r}, 0.0005 * arcsecond)
412-
compare(az.degrees, {az2!r}, 0.0005 * arcsecond)
431+
compare(alt.degrees, {alt2!r}, 0.00001 * arcsecond)
432+
compare(az.degrees, {az2!r}, 0.00001 * arcsecond)
413433
414434
alt, az, distance = apparent.altaz(10.0, 1010.0)
415-
compare(alt.degrees, {alt3!r}, 0.0005 * arcsecond)
416-
compare(az.degrees, {az3!r}, 0.0005 * arcsecond)
435+
compare(alt.degrees, {alt3!r}, 0.00001 * arcsecond)
436+
compare(az.degrees, {az3!r}, 0.00001 * arcsecond)
417437
418438
""")
419439

documentation/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ and satellites in orbit around the Earth.
1616
Its results should agree
1717
with the positions generated by the United States Naval Observatory
1818
and their *Astronomical Almanac*
19-
to within 0.0005 arcseconds (half a “mas” or milliarcsecond).
19+
to within 0.00001 arcseconds (one-hundredth of a “mas” or milliarcsecond).
2020

2121
* Written in pure Python.
2222
* Installs without any compilation.

skyfield/earthlib.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""Formulae for specific earth behaviors and effects."""
22

3-
from numpy import (abs, arcsin, arccos, arctan2, array, clip, cos, minimum,
4-
nan_to_num, pi, sin, sqrt, tan, where, zeros_like)
3+
from numpy import (
4+
abs, arcsin, arccos, arctan2, array, bool, clip, cos, minimum,
5+
nan_to_num, ones_like, pi, sin, sqrt, tan, where, zeros_like,
6+
)
57

68
from .constants import (AU_M, ANGVEL, DAY_S, DEG2RAD, ERAD,
79
IERS_2010_INVERSE_EARTH_FLATTENING, RAD2DEG, T0, tau)
@@ -148,10 +150,10 @@ def refraction(alt_degrees, temperature_C, pressure_mbar):
148150
def refract(alt_degrees, temperature_C, pressure_mbar):
149151
"""Given an unrefracted `alt` determine where it will appear in the sky."""
150152
alt = alt_degrees
151-
while True:
152-
alt1 = alt
153-
alt = alt_degrees + refraction(alt, temperature_C, pressure_mbar)
154-
converged = nan_to_num(abs(alt - alt1)).max() <= 3.0e-5
155-
if converged:
156-
break
153+
mask = ones_like(alt, dtype=bool)
154+
while mask.sum():
155+
alt_previous = alt
156+
alt_new = alt_degrees + refraction(alt, temperature_C, pressure_mbar)
157+
alt = mask * alt_new + ~mask * alt_previous
158+
mask = nan_to_num(abs(alt - alt_previous)) > 3.0e-5
157159
return alt

0 commit comments

Comments
 (0)