diff --git a/SAS/TMSS/src/tmss/tmssapp/conversions.py b/SAS/TMSS/src/tmss/tmssapp/conversions.py index ce112f7b30b8f697baf91d4da9202899703715ba..a9eddb9d5d959f38df66ba196c939bdeddbf1fe1 100644 --- a/SAS/TMSS/src/tmss/tmssapp/conversions.py +++ b/SAS/TMSS/src/tmss/tmssapp/conversions.py @@ -3,8 +3,9 @@ import astropy.units from lofar.lta.sip import station_coordinates from datetime import datetime from astropy.coordinates.earth import EarthLocation -from astropy.coordinates import Angle +from astropy.coordinates import Angle, get_body from astroplan.observer import Observer +import astropy.time 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 """ 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 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. E.g. - {"CS001": - { "sunrise": [{"start": (2020, 1, 1, 6, 0, 0)), "end": (2020, 1, 1, 6, 30, 0)}, - {"start": (2020, 1, 2, 6, 0, 0)), "end": (2020, 1, 2, 6, 30, 0)}], - "sunset": [{"start": (2020, 1, 1, 18, 0, 0)), "end": (2020, 1, 1, 18, 30, 0)}, - {"start": (2020, 1, 2, 18, 0, 0)), "end": (2020, 1, 2, 18, 30, 0)}], - "day": [{"start": (2020, 1, 1, 6, 30, 0)), "end": (2020, 1, 1, 18, 00, 0)}, - {"start": (2020, 1, 2, 6, 30, 0)), "end": (2020, 1, 2, 18, 00, 0)}], - "night": [{"start": (2020, 1, 1, 18, 30, 0)), "end": (2020, 1, 2, 6, 0, 0)}, - {"start": (2020, 1, 2, 18,3 0, 0)), "end": (2020, 1, 3, 6, 0, 0)}], + {"CS002": + { "sunrise": [{"start": datetime(2020, 1, 1, 6, 0, 0)), "end": datetime(2020, 1, 1, 6, 30, 0)}, + {"start": datetime(2020, 1, 2, 6, 0, 0)), "end": datetime(2020, 1, 2, 6, 30, 0)}], + "sunset": [{"start": datetime(2020, 1, 1, 18, 0, 0)), "end": datetime(2020, 1, 1, 18, 30, 0)}, + {"start": datetime(2020, 1, 2, 18, 0, 0)), "end": datetime(2020, 1, 2, 18, 30, 0)}], + "day": [{"start": datetime(2020, 1, 1, 6, 30, 0)), "end": datetime(2020, 1, 1, 18, 00, 0)}, + {"start": datetime(2020, 1, 2, 6, 30, 0)), "end": datetime(2020, 1, 2, 18, 00, 0)}], + "night": [{"start": datetime(2020, 1, 1, 18, 30, 0)), "end": datetime(2020, 1, 2, 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 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, station: str = 'CS002', field: str = 'LBA', diff --git a/SAS/TMSS/src/tmss/tmssapp/views.py b/SAS/TMSS/src/tmss/tmssapp/views.py index 82aaec87db4e5af51f76837547544e85dc067ab2..57f7b497ac038c9cd0123c43b2ae22c4776a2d87 100644 --- a/SAS/TMSS/src/tmss/tmssapp/views.py +++ b/SAS/TMSS/src/tmss/tmssapp/views.py @@ -15,7 +15,9 @@ from django.apps import apps from rest_framework.decorators import api_view from datetime import datetime 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 @@ -169,3 +171,62 @@ def get_sun_rise_and_set(request): 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×tamps=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) diff --git a/SAS/TMSS/src/tmss/urls.py b/SAS/TMSS/src/tmss/urls.py index ea581ae1e510ff2af837551c2864c2ed75b9584c..7252c1b008025e2388f6dbca59517615fb115981 100644 --- a/SAS/TMSS/src/tmss/urls.py +++ b/SAS/TMSS/src/tmss/urls.py @@ -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/utc/?', views.utc, name="system-utc"), 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):