-
-
Notifications
You must be signed in to change notification settings - Fork 219
/
Copy pathmagnitudelib.py
322 lines (257 loc) · 10.6 KB
/
magnitudelib.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# -*- coding: utf-8 -*-
"""Routines for computing magnitudes.
Planetary routines adapted from:
https://arxiv.org/pdf/1808.01973.pdf
Which links to:
https://sourceforge.net/projects/planetary-magnitudes/
Which has directories with three successive versions of their magnitude
computation, the most recent of which provides the files on which this
Python code is based:
Ap_Mag_V3.f90
Ap_Mag_Output_V3.txt
Ap_Mag_Input_V3.txt
* ``r`` planet’s distance from the Sun.
* ``delta`` from Earth?
* ``ph_ang`` illumination phase angle (degrees)
"""
from numpy import array, clip, exp, log10, nan, sin, where
from .constants import RAD2DEG
from .functions import angle_between, length_of
from .naifcodes import _target_name
# See "design/planet_tilts.py" in the Skyfield repository.
_SATURN_POLE = array([0.08547883, 0.07323576, 0.99364475])
_SATURN_POLE_2D = _SATURN_POLE[:, None]
_URANUS_POLE = array([-0.21199958, -0.94155916, -0.26176809])
_URANUS_POLE_2D = _URANUS_POLE[:, None]
def planetary_magnitude(position):
"""Given the position of a planet, return its visual magnitude.
>>> from skyfield.api import load
>>> from skyfield.magnitudelib import planetary_magnitude
>>> ts = load.timescale()
>>> t = ts.utc(2020, 7, 31)
>>> eph = load('de421.bsp')
>>> astrometric = eph['earth'].at(t).observe(eph['jupiter barycenter'])
>>> print('%.2f' % planetary_magnitude(astrometric))
-2.73
The formulae are from `Mallama and Hilton “Computing Apparent
Planetary Magnitude for the Astronomical Almanac” (2018)
<https://arxiv.org/pdf/1808.01973.pdf>`_. Two of the formulae have
inherent limits:
* Saturn’s magnitude is unknown and the function will return ``nan``
(the floating-point value “Not a Number”) if the “illumination
phase angle” — the angle of the vertex observer-Saturn-Sun —
exceeds 6.5°.
* Neptune’s magnitude is unknown and will return ``nan`` if the
illumination phase angle exceeds 1.9° and the position's date is
before the year 2000.
And one formula is not fully implemented (though contributions are
welcome!):
* Skyfield does not compute which features on Mars are facing the
observer, which can introduce an error of ±0.06 magnitude.
"""
target = position.target
function = _FUNCTIONS.get(target)
if function is None:
name = _target_name(target)
raise ValueError('cannot compute the magnitude of target %s' % name)
# Shamelessly treat the Sun as sitting at the Solar System Barycenter.
sun_to_observer = position.center_barycentric.xyz.au
observer_to_planet = position.xyz.au
sun_to_planet = sun_to_observer + observer_to_planet
r = length_of(sun_to_planet)
delta = length_of(observer_to_planet)
ph_ang = angle_between(sun_to_planet, observer_to_planet) * RAD2DEG
if function is _saturn_magnitude:
if len(sun_to_planet.shape) > 1:
pole = _SATURN_POLE_2D
else:
pole = _SATURN_POLE
a = angle_between(pole, sun_to_planet)
sun_sub_lat = a * RAD2DEG - 90.0
a = angle_between(pole, observer_to_planet)
observer_sub_lat = a * RAD2DEG - 90.0
return function(r, delta, ph_ang, sun_sub_lat, observer_sub_lat)
if function is _uranus_magnitude:
if len(sun_to_planet.shape) > 1:
pole = _URANUS_POLE_2D
else:
pole = _URANUS_POLE
a = angle_between(pole, sun_to_planet)
sun_sub_lat = a * RAD2DEG - 90.0
a = angle_between(pole, observer_to_planet)
observer_sub_lat = a * RAD2DEG - 90.0
return function(r, delta, ph_ang, sun_sub_lat, observer_sub_lat)
if function is _neptune_magnitude:
year = position.t.J
return function(r, delta, ph_ang, year)
return function(r, delta, ph_ang)
def _mercury_magnitude(r, delta, ph_ang):
distance_mag_factor = 5 * log10(r * delta)
ph_ang_factor = (
6.3280e-02 * ph_ang
- 1.6336e-03 * ph_ang**2
+ 3.3644e-05 * ph_ang**3
- 3.4265e-07 * ph_ang**4
+ 1.6893e-09 * ph_ang**5
- 3.0334e-12 * ph_ang**6
)
return -0.613 + distance_mag_factor + ph_ang_factor
def _venus_magnitude(r, delta, ph_ang):
distance_mag_factor = 5 * log10(r * delta)
condition = ph_ang < 163.7
a0 = where(condition, 0.0, 236.05828 + 4.384)
a1 = where(condition, - 1.044E-03, - 2.81914E+00)
a2 = where(condition, + 3.687E-04, + 8.39034E-03)
a3 = where(condition, - 2.814E-06, 0.0)
a4 = where(condition, + 8.938E-09, 0.0)
ph_ang_factor = a4
for a in a3, a2, a1, a0:
ph_ang_factor *= ph_ang
ph_ang_factor += a
return -4.384 + distance_mag_factor + ph_ang_factor
def _earth_magnitude(r, delta, ph_ang):
distance_mag_factor = 5 * log10(r * delta)
ph_ang_factor = -1.060e-03 * ph_ang + 2.054e-04 * ph_ang**2
return -3.99 + distance_mag_factor + ph_ang_factor
def _mars_magnitude(r, delta, ph_ang):
r_mag_factor = 2.5 * log10(r * r)
delta_mag_factor = 2.5 * log10(delta * delta)
distance_mag_factor = r_mag_factor + delta_mag_factor
geocentric_phase_angle_limit = 50.0
condition = ph_ang <= geocentric_phase_angle_limit
a = where(condition, 2.267E-02, - 0.02573)
b = where(condition, - 1.302E-04, 0.0003445)
ph_ang_factor = a * ph_ang + b * ph_ang**2
# Compute the effective central meridian longitude
# eff_CM = ( sub_earth_long + sub_sun_long ) / 2.
# if ( abs ( sub_earth_long - sub_sun_long ) > 180. ):
# Eff_CM = Eff_CM + 180.
# if ( Eff_CM > 360. ):
# Eff_CM = Eff_CM - 360.
# ! Use Stirling interpolation to determine the magnitude correction
# call Mars_Stirling ( 'R', eff_CM, mag_corr_rot )
# Convert the ecliptic longitude to Ls
# Ls = h_ecl_long + Ls_offset
# if ( Ls > 360. ) Ls = Ls - 360.
# if ( Ls < 0. ) Ls = Ls + 360.
# Use Stirling interpolation to determine the magnitude correction
# call Mars_Stirling ( 'O', Ls, mag_corr_orb )
# Until effects from Mars rotation are written up:
mag_corr_rot = 0.0
mag_corr_orb = 0.0
# Add factors to determine the apparent magnitude
ap_mag = where(ph_ang <= geocentric_phase_angle_limit, -1.601, -0.367)
ap_mag += distance_mag_factor + ph_ang_factor + mag_corr_rot + mag_corr_orb
return ap_mag
def _jupiter_magnitude(r, delta, ph_ang):
distance_mag_factor = 5 * log10(r * delta)
geocentric_phase_angle_limit = 12.0
ph_ang_pi = ph_ang / 180.0
ph_ang_factor = where(
ph_ang <= geocentric_phase_angle_limit,
(6.16E-04 * ph_ang - 3.7E-04) * ph_ang,
-2.5 * log10(
((((- 1.876 * ph_ang_pi + 2.809) * ph_ang_pi - 0.062) * ph_ang_pi
- 0.363) * ph_ang_pi - 1.507) * ph_ang_pi + 1.0
),
)
ap_mag = where(
ph_ang <= geocentric_phase_angle_limit,
-9.395 + distance_mag_factor + ph_ang_factor,
-9.428 + distance_mag_factor + ph_ang_factor,
)
return ap_mag
def _saturn_magnitude(r, delta, ph_ang, sun_sub_lat, earth_sub_lat, rings=True):
# Note that sun_sub_lat and earth_sub_lat should be saturnicentric
# latitude, not saturnidetic.
r_mag_factor = 2.5 * log10(r * r)
delta_mag_factor = 2.5 * log10(delta * delta)
distance_mag_factor = r_mag_factor + delta_mag_factor
# Then take the square root of the product of the saturnicentric
# latitude of the Sun and that of Earth but set to zero when the
# signs are opposite
product = sun_sub_lat * earth_sub_lat
signs_same = product >= 0.0
square_root = product ** where(signs_same, 0.5, 0.0) # avoid sqrt(neg)
sub_lat_geoc = where(signs_same, square_root, 0.0)
# Compute the effect of phase angle and inclination
geocentric_phase_angle_limit = 6.5
geocentric_inclination_limit = 27.0
is_within_geocentric_bounds = (
(ph_ang <= geocentric_phase_angle_limit)
& (sub_lat_geoc <= geocentric_inclination_limit)
)
ap_mag = where(
is_within_geocentric_bounds,
where(
rings,
# Use equation #10 for globe+rings and geocentric circumstances.
-8.914 - 1.825 * sin(sub_lat_geoc / RAD2DEG) + 0.026 * ph_ang
- 0.378 * sin(sub_lat_geoc / RAD2DEG) * exp(-2.25 * ph_ang),
# Use equation #11 for globe-alone and geocentric circumstances
-8.95 - 3.7e-4 * ph_ang + 6.16e-4 * ph_ang**2,
),
where(
(ph_ang > geocentric_phase_angle_limit) & (~rings),
# Use equation #12 for globe-alone beyond geocentric phase
# angle limit
-8.94 + 2.446e-4 * ph_ang + 2.672e-4 * ph_ang**2
- 1.506e-6 * ph_ang**3 +4.767e-9 * ph_ang**4,
nan,
),
)
ap_mag = ap_mag + distance_mag_factor
return ap_mag
def _uranus_magnitude(r, delta, ph_ang,
sun_sub_lat_planetog, earth_sub_lat_planetog):
distance_mag_factor = 5.0 * log10 (r * delta)
sub_lat_planetog = (abs(sun_sub_lat_planetog)
+ abs(earth_sub_lat_planetog)) / 2.0
sub_lat_factor = -0.00084 * sub_lat_planetog
geocentric_phase_angle_limit = 3.1
ap_mag = -7.110 + distance_mag_factor + sub_lat_factor
ap_mag += where(
ph_ang > geocentric_phase_angle_limit,
(1.045e-4 * ph_ang + 6.587e-3) * ph_ang,
0.0,
)
return ap_mag
def _neptune_magnitude(r, delta, ph_ang, year):
r_mag_factor = 2.5 * log10(r * r)
delta_mag_factor = 2.5 * log10(delta * delta)
distance_mag_factor = r_mag_factor + delta_mag_factor
# Equation 16 compute the magnitude at unit distance as a function of time
ap_mag = clip(-6.89 - 0.0054 * (year - 1980.0), -7.00, -6.89)
ap_mag += distance_mag_factor
geocentric_phase_angle_limit = 1.9
ap_mag = where(
ph_ang > geocentric_phase_angle_limit,
# Add phase angle factor from equation 17
# Check the year because equation 17 only pertains to t > 2000.0
where(
year >= 2000.0,
ap_mag + 7.944e-3 * ph_ang + 9.617e-5 * ph_ang**2,
nan,
),
# Otherwise leave the value unchanged.
ap_mag,
)
return ap_mag
_FUNCTIONS = {
199: _mercury_magnitude,
299: _venus_magnitude,
399: _earth_magnitude,
499: _mars_magnitude,
599: _jupiter_magnitude,
699: _saturn_magnitude,
799: _uranus_magnitude,
899: _neptune_magnitude,
# Some planets can be reasonably identified with their barycenter.
1: _mercury_magnitude,
2: _venus_magnitude,
4: _mars_magnitude,
5: _jupiter_magnitude,
6: _saturn_magnitude,
7: _uranus_magnitude,
8: _neptune_magnitude,
}