Skip to content
Snippets Groups Projects
Commit d5f7156b authored by Jörn Künsemöller's avatar Jörn Künsemöller
Browse files

TMSS-459: Add new endpoint 'angular_separation_from_bodies' and backend logic...

TMSS-459: Add new endpoint 'angular_separation_from_bodies' and backend logic to calculate distance of a pointing from solar system bodies
parent 905eef8d
No related branches found
No related tags found
1 merge request!284Resolve TMSS-259
...@@ -3,8 +3,9 @@ import astropy.units ...@@ -3,8 +3,9 @@ import astropy.units
from lofar.lta.sip import station_coordinates from lofar.lta.sip import station_coordinates
from datetime import datetime from datetime import datetime
from astropy.coordinates.earth import EarthLocation from astropy.coordinates.earth import EarthLocation
from astropy.coordinates import Angle from astropy.coordinates import Angle, get_body
from astroplan.observer import Observer from astroplan.observer import Observer
import astropy.time
def create_astroplan_observer_for_station(station: str) -> Observer: def create_astroplan_observer_for_station(station: str) -> Observer:
...@@ -26,18 +27,18 @@ def timestamps_and_stations_to_sun_rise_and_set(timestamps: [datetime], stations ...@@ -26,18 +27,18 @@ def timestamps_and_stations_to_sun_rise_and_set(timestamps: [datetime], stations
""" """
compute sunrise, sunset, day and night of the given stations at the given timestamps compute sunrise, sunset, day and night of the given stations at the given timestamps
:param timestamps: list of datetimes, e.g. [datetime(2020, 1, 1), datetime(2020, 1, 2)] :param timestamps: list of datetimes, e.g. [datetime(2020, 1, 1), datetime(2020, 1, 2)]
:param stations: list of station names, e.g. ["CS001"] :param stations: list of station names, e.g. ["CS002"]
:return A dict that maps station names to a nested dict that contains lists of start and end times for sunrise, sunset, etc, on each requested date. :return A dict that maps station names to a nested dict that contains lists of start and end times for sunrise, sunset, etc, on each requested date.
E.g. E.g.
{"CS001": {"CS002":
{ "sunrise": [{"start": (2020, 1, 1, 6, 0, 0)), "end": (2020, 1, 1, 6, 30, 0)}, { "sunrise": [{"start": datetime(2020, 1, 1, 6, 0, 0)), "end": datetime(2020, 1, 1, 6, 30, 0)},
{"start": (2020, 1, 2, 6, 0, 0)), "end": (2020, 1, 2, 6, 30, 0)}], {"start": datetime(2020, 1, 2, 6, 0, 0)), "end": datetime(2020, 1, 2, 6, 30, 0)}],
"sunset": [{"start": (2020, 1, 1, 18, 0, 0)), "end": (2020, 1, 1, 18, 30, 0)}, "sunset": [{"start": datetime(2020, 1, 1, 18, 0, 0)), "end": datetime(2020, 1, 1, 18, 30, 0)},
{"start": (2020, 1, 2, 18, 0, 0)), "end": (2020, 1, 2, 18, 30, 0)}], {"start": datetime(2020, 1, 2, 18, 0, 0)), "end": datetime(2020, 1, 2, 18, 30, 0)}],
"day": [{"start": (2020, 1, 1, 6, 30, 0)), "end": (2020, 1, 1, 18, 00, 0)}, "day": [{"start": datetime(2020, 1, 1, 6, 30, 0)), "end": datetime(2020, 1, 1, 18, 00, 0)},
{"start": (2020, 1, 2, 6, 30, 0)), "end": (2020, 1, 2, 18, 00, 0)}], {"start": datetime(2020, 1, 2, 6, 30, 0)), "end": datetime(2020, 1, 2, 18, 00, 0)}],
"night": [{"start": (2020, 1, 1, 18, 30, 0)), "end": (2020, 1, 2, 6, 0, 0)}, "night": [{"start": datetime(2020, 1, 1, 18, 30, 0)), "end": datetime(2020, 1, 2, 6, 0, 0)},
{"start": (2020, 1, 2, 18,3 0, 0)), "end": (2020, 1, 3, 6, 0, 0)}], {"start": datetime(2020, 1, 2, 18,3 0, 0)), "end": datetime(2020, 1, 3, 6, 0, 0)}],
} }
} }
""" """
...@@ -59,6 +60,39 @@ def timestamps_and_stations_to_sun_rise_and_set(timestamps: [datetime], stations ...@@ -59,6 +60,39 @@ def timestamps_and_stations_to_sun_rise_and_set(timestamps: [datetime], stations
return return_dict return return_dict
def coordinates_and_timestamps_to_separation_from_bodies(coords: [astropy.coordinates.SkyCoord], timestamps: [datetime], bodies: [str], stations: [str]) -> dict:
"""
compute angular distances of the given sky coordinates from the given solar system bodies at the given timestamps and stations
:param coords: list of SkyCoord objects to measure separation
:param timestamps: list of datetimes, e.g. [datetime(2020, 1, 1, 15, 0, 0), datetime(2020, 1, 1, 16, 0, 0)]
:param stations: list of station names, e.g. ["CS002"]
:param bodies: list of solar system bodies, e.g. ['sun', 'moon', 'jupiter']
:return A dict that maps station names to a list with a dict for each given coordinate, which maps each body body to a list of separation angles for each given timestamp.
E.g.
{"CS002":
[
{"sun": [Angle("0.7rad"), Angle("0.7rad")], "moon": [Angle("0.4rad"), Angle("0.4rad")], "jupiter": [Angle("2.7rad"), Angle("2.7rad")]},
{"sun": [Angle("0.8rad"), Angle("0.8rad")], "moon": [Angle("0.5rad"), Angle("0.5rad")], "jupiter": [Angle("2.6rad"), Angle("2.6rad")]}
]
}
# todo: not so sure about this data structure. SkyCoord is not hashable, so we cannot use it as a key.
# todo: We could provide coordinates in a string format and use that as keys, or limit to a single pointing to avoid confusion.
"""
return_dict = {}
for station in stations:
for coord in coords:
body_angles = {}
for body in bodies:
location = create_astroplan_observer_for_station(station).location
for timestamp in timestamps:
# get body coords at timestamp
body_coord = get_body(body=body, time=astropy.time.Time(timestamp), location=location)
angle = coord.separation(body_coord)
body_angles.setdefault(body, []).append(angle)
return_dict.setdefault(station, []).append(body_angles)
return return_dict
def local_sidereal_time_for_utc_and_station(timestamp: datetime = None, def local_sidereal_time_for_utc_and_station(timestamp: datetime = None,
station: str = 'CS002', station: str = 'CS002',
field: str = 'LBA', field: str = 'LBA',
......
...@@ -15,7 +15,9 @@ from django.apps import apps ...@@ -15,7 +15,9 @@ from django.apps import apps
from rest_framework.decorators import api_view from rest_framework.decorators import api_view
from datetime import datetime from datetime import datetime
import dateutil.parser import dateutil.parser
from lofar.sas.tmss.tmss.tmssapp.conversions import local_sidereal_time_for_utc_and_station, local_sidereal_time_for_utc_and_longitude, timestamps_and_stations_to_sun_rise_and_set from astropy.coordinates import SkyCoord
from astropy import units as u
from lofar.sas.tmss.tmss.tmssapp.conversions import local_sidereal_time_for_utc_and_station, local_sidereal_time_for_utc_and_longitude, timestamps_and_stations_to_sun_rise_and_set, coordinates_and_timestamps_to_separation_from_bodies
# Note: Decorate with @api_view to get this picked up by Swagger # Note: Decorate with @api_view to get this picked up by Swagger
...@@ -169,3 +171,62 @@ def get_sun_rise_and_set(request): ...@@ -169,3 +171,62 @@ def get_sun_rise_and_set(request):
return JsonResponse(timestamps_and_stations_to_sun_rise_and_set(timestamps, stations)) return JsonResponse(timestamps_and_stations_to_sun_rise_and_set(timestamps, stations))
@permission_classes([AllowAny])
@authentication_classes([AllowAny])
@swagger_auto_schema(method='GET',
responses={200: 'A JSON object with angular distances of the given sky coordinates from the given solar system bodies at the given timestamps and stations. \n'
'Outer list contains results per coordinate in given order, inner list per timestamp.'},
operation_description="Get angular distances of the given sky coordinates from the given solar system bodies at all given timestamps and stations. \n\n"
"Example request: /api/util/sun_rise_and_set?coordinates=,CS005&timestamps=2020-05-01,2020-09-09T11-11-00",
manual_parameters=[Parameter(name='ras', required=True, type='string', in_='query',
description="comma-separated list of right ascensions (celestial longitudes)"),
Parameter(name='decs', required=True, type='string', in_='query',
description="comma-separated list of declinations (celestial latitude)"),
Parameter(name='stations', required=False, type='string', in_='query',
description="comma-separated list of station names"),
Parameter(name='timestamps', required=False, type='string', in_='query',
description="comma-separated list of isoformat timestamps"),
Parameter(name='bodies', required=False, type='string', in_='query',
description="comma-separated list of solar system bodies")])
@api_view(['GET'])
def get_angular_separation_from_bodies(request):
'''
returns angular distances of the given sky coordinates from the given astronomical objects at the given timestamps and stations
'''
stations = request.GET.get('stations', None)
timestamps = request.GET.get('timestamps', None)
ras = request.GET.get('ras', None)
decs = request.GET.get('decs', None)
bodies = request.GET.get('bodies', None)
if timestamps is None:
timestamps = [datetime.utcnow()]
else:
timestamps = timestamps.split(',')
timestamps = [dateutil.parser.parse(timestamp) for timestamp in timestamps] # isot to datetime
if bodies is None:
bodies = ['sun', 'moon', 'jupiter']
else:
bodies = bodies.split(',')
if stations is None:
stations = ["CS002"]
else:
stations = stations.split(',')
if ras is None or decs is None:
raise ValueError("Please provide celestial coordinates in radians/J2000 via the 'ras' and 'decs' the properties.")
coords = []
longitudes = ras.split(',')
latitudes = decs.split(',')
for ra, dec in zip(longitudes, latitudes):
coords.append(SkyCoord(ra=ra, dec=dec, unit=u.rad, equinox='J2000'))
sep_dict = coordinates_and_timestamps_to_separation_from_bodies(coords=coords, stations=stations, bodies=bodies, timestamps=timestamps)
for station, v in sep_dict.items():
for coord in v:
for object, angles in coord.items():
coord[object] = [angle.rad for angle in angles]
return JsonResponse(sep_dict)
...@@ -67,6 +67,7 @@ urlpatterns = [ ...@@ -67,6 +67,7 @@ urlpatterns = [
re_path('util/sun_rise_and_set/?', views.get_sun_rise_and_set, name='get_sun_rise_and_set'), re_path('util/sun_rise_and_set/?', views.get_sun_rise_and_set, name='get_sun_rise_and_set'),
re_path('util/utc/?', views.utc, name="system-utc"), re_path('util/utc/?', views.utc, name="system-utc"),
re_path('util/lst/?', views.lst, name="conversion-lst"), re_path('util/lst/?', views.lst, name="conversion-lst"),
re_path('util/angular_separation_from_bodies/?', views.get_angular_separation_from_bodies, name='get_angular_separation_from_bodies'),
] ]
if os.environ.get('SHOW_DJANGO_DEBUG_TOOLBAR', False): if os.environ.get('SHOW_DJANGO_DEBUG_TOOLBAR', False):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment