Skip to content

Commit b9e9352

Browse files
committed
TLETarget: support only astroplan Observer and use its properties for atmospheric refraction in ICRF.altaz()
- Update tests with an example without an observer and one with temperature and pressure. Also, use time above the horizon to test `TLETarget.altaz()`, and test `TLETarget.altaz()` with multiple times. - Use `@pytest.mark.remote_data` for TLE tests since they use `Observer.at_site()`.
1 parent 84c98cd commit b9e9352

File tree

2 files changed

+64
-20
lines changed

2 files changed

+64
-20
lines changed

astroplan/target.py

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def __init__(self, line1, line2, name=None, observer=None, skip_tle_check=False)
220220
Name of the target, used for plotting and representing the target
221221
as a string
222222
223-
observer : `~astropy.coordinates.EarthLocation`, `~astroplan.Observer`, optional
223+
observer : `~astroplan.Observer`, optional
224224
The location of observer.
225225
If `None`, the observer is assumed to be at sea level at the equator.
226226
@@ -237,13 +237,16 @@ def __init__(self, line1, line2, name=None, observer=None, skip_tle_check=False)
237237
self.satellite = EarthSatellite(line1, line2, name, load.timescale())
238238

239239
if observer is None:
240-
self.observer = wgs84.latlon(0, 0, 0)
240+
# Prevent circular import and usually not used
241+
from .observer import Observer
242+
self.observer = Observer(latitude=0*u.deg, longitude=0*u.deg, elevation=0*u.m)
241243
else:
242-
observer = getattr(observer, 'location', observer)
243-
longitude, latitude, height = observer.to_geodetic()
244-
self.observer = wgs84.latlon(latitude.to(u.deg).value,
245-
longitude.to(u.deg).value,
246-
height.to(u.m).value)
244+
self.observer = observer
245+
246+
longitude, latitude, height = self.observer.location.to_geodetic()
247+
self.geographic_position = wgs84.latlon(latitude.to(u.deg).value,
248+
longitude.to(u.deg).value,
249+
height.to(u.m).value)
247250

248251
@classmethod
249252
def from_string(cls, tle_string, name=None, *args, **kwargs):
@@ -300,7 +303,7 @@ def _compute_topocentric(self, time=None):
300303
ts = load.timescale()
301304
t = ts.from_astropy(time)
302305

303-
topocentric = (self.satellite - self.observer).at(t)
306+
topocentric = (self.satellite - self.geographic_position).at(t)
304307

305308
# Check for invalid TLE data. A non-None usually message means the computation went beyond
306309
# the physically sensible point. Details:
@@ -333,7 +336,13 @@ def coord(self, time=None):
333336
topocentric = self._compute_topocentric(time)
334337
ra, dec, distance = topocentric.radec()
335338
# No distance, in SkyCoord, distance is from frame origin, but here, it's from observer.
336-
return SkyCoord(ra.hours*u.hourangle, dec.degrees*u.deg, obstime=time, frame='icrs')
339+
return SkyCoord(
340+
ra.hours*u.hourangle,
341+
dec.degrees*u.deg,
342+
obstime=time,
343+
frame='icrs',
344+
location=self.observer.location,
345+
)
337346

338347
def altaz(self, time=None):
339348
"""
@@ -350,15 +359,28 @@ def altaz(self, time=None):
350359
SkyCoord object representing the target's altitude and azimuth at the specified time(s)
351360
"""
352361
topocentric = self._compute_topocentric(time)
353-
alt, az, distance = topocentric.altaz()
354-
355-
earth_location = EarthLocation(lat=self.observer.latitude.degrees*u.deg,
356-
lon=self.observer.longitude.degrees*u.deg,
357-
height=self.observer.elevation.m*u.m)
358362

359-
altaz = AltAz(alt=alt.degrees*u.deg, az=az.degrees*u.deg,
360-
obstime=time, location=earth_location)
361-
return SkyCoord(altaz)
363+
temperature_C = None
364+
pressure_mbar = None
365+
if self.observer.temperature is not None:
366+
temperature_C = self.observer.temperature.to_value(u.deg_C)
367+
if self.observer.pressure is not None:
368+
pressure_mbar = self.observer.pressure.to_value(u.mbar)
369+
370+
alt, az, distance = topocentric.altaz(
371+
temperature_C=temperature_C,
372+
pressure_mbar=pressure_mbar,
373+
)
374+
375+
# 'relative_humidity' and 'obswl' were not used in coordinate calculation
376+
altaz_frame = AltAz(
377+
location=self.observer.location,
378+
obstime=time,
379+
pressure=self.observer.pressure,
380+
temperature=self.observer.temperature,
381+
)
382+
383+
return SkyCoord(alt=alt.degrees*u.deg, az=az.degrees*u.deg, frame=altaz_frame)
362384

363385
def __repr__(self):
364386
return f'<{self.__class__.__name__} "{self.name}">'

astroplan/tests/test_target.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def test_FixedTarget_ra_dec():
5454
'SkyCoord')
5555

5656

57+
@pytest.mark.remote_data
5758
@pytest.mark.skipif('not HAS_SKYFIELD')
5859
def test_TLETarget():
5960
tle_string = ("ISS (ZARYA)\n"
@@ -62,18 +63,27 @@ def test_TLETarget():
6263
line1 = "1 25544U 98067A 23215.27256123 .00041610 00000-0 73103-3 0 9990"
6364
line2 = "2 25544 51.6403 95.2411 0000623 157.9606 345.0624 15.50085581409092"
6465
subaru = Observer.at_site('subaru') # (lon, lat, el)=(-155.476111 deg, 19.825555 deg, 4139.0 m)
66+
subaru_temp_pressure = Observer.at_site('subaru', temperature=-10*u.Celsius, pressure=0.9*u.bar)
6567
time = Time("2023-08-02 10:00", scale='utc')
6668
times = time_grid_from_range([time, time + 3.1*u.hour],
6769
time_resolution=1 * u.hour)
6870

6971
tle_target1 = TLETarget(name="ISS (ZARYA)", line1=line1, line2=line2, observer=subaru)
7072
tle_target2 = TLETarget.from_string(tle_string=tle_string, observer=subaru)
73+
tle_target_no_observer = TLETarget(name="ISS (ZARYA)", line1=line1, line2=line2, observer=None)
74+
tle_target_temp_pressure = TLETarget(
75+
name="ISS (ZARYA)", line1=line1, line2=line2, observer=subaru_temp_pressure
76+
)
7177

7278
assert tle_target1.name == "ISS (ZARYA)"
7379
assert tle_target2.name == "ISS (ZARYA)"
7480
assert repr(tle_target1) == repr(tle_target2)
7581
assert str(tle_target1) == str(tle_target2)
7682

83+
assert isinstance(tle_target_no_observer.observer, Observer)
84+
assert abs(tle_target_no_observer.observer.location.lat) < 0.001*u.deg
85+
tle_target_no_observer.coord(time) # Just needs to work
86+
7787
# Single time (Below Horizon)
7888
ra_dec1 = tle_target1.coord(time) # '08h29m26.00003243s +07d31m36.65950907s'
7989
ra_dec2 = tle_target2.coord(time)
@@ -145,10 +155,21 @@ def test_TLETarget():
145155
TLETarget.from_string(tle_string=tle_string, observer=subaru)
146156

147157
# AltAz (This is slow)
148-
altaz_observer = subaru.altaz(time, tle_target1)
149-
altaz_skyfield = tle_target1.altaz(time)
158+
altaz_observer = subaru.altaz(time_ah, tle_target1)
159+
altaz_skyfield = tle_target1.altaz(time_ah)
160+
161+
assert altaz_observer.separation(altaz_skyfield) < 21*u.arcsec
150162

151-
assert altaz_observer.separation(altaz_skyfield) < 20*u.arcsec
163+
# AltAz with atmospheric refraction (temperature and pressure)
164+
altaz_observer = subaru_temp_pressure.altaz(time_ah, tle_target_temp_pressure)
165+
altaz_skyfield = tle_target_temp_pressure.altaz(time_ah)
166+
167+
assert altaz_observer.separation(altaz_skyfield) < 26*u.arcsec
168+
169+
# AltAz with multiple times
170+
#subaru.altaz(times, tle_target1)
171+
altaz_multiple = tle_target1.altaz(times)
172+
assert len(altaz_multiple.obstime) == len(altaz_multiple) == len(times)
152173

153174
# Time too far in the future where elements stop making physical sense
154175
with pytest.warns(): # ErfaWarning: ERFA function "dtf2d" yielded 1 of "dubious year (Note 6)
@@ -203,6 +224,7 @@ def test_get_skycoord():
203224
assert len(coo) == 2
204225

205226

227+
@pytest.mark.remote_data
206228
@pytest.mark.skipif('not HAS_SKYFIELD')
207229
def test_get_skycoord_with_TLETarget():
208230
skycoord_targed = SkyCoord(10.6847083*u.deg, 41.26875*u.deg)

0 commit comments

Comments
 (0)