diff --git a/SAS/TMSS/src/tmss/tmssapp/conversions.py b/SAS/TMSS/src/tmss/tmssapp/conversions.py
index a9eddb9d5d959f38df66ba196c939bdeddbf1fe1..037d4b31d57c3d921e04780963e2762997f6a339 100644
--- a/SAS/TMSS/src/tmss/tmssapp/conversions.py
+++ b/SAS/TMSS/src/tmss/tmssapp/conversions.py
@@ -1,7 +1,7 @@
 from astropy.time import Time
 import astropy.units
 from lofar.lta.sip import station_coordinates
-from datetime import datetime
+from datetime import datetime, timedelta, time as dtime
 from astropy.coordinates.earth import EarthLocation
 from astropy.coordinates import Angle, get_body
 from astroplan.observer import Observer
@@ -20,8 +20,10 @@ def create_astroplan_observer_for_station(station: str) -> Observer:
     observer = Observer(location, name="LOFAR", timezone="UTC")
     return observer
 
+
 # default angle to the horizon at which the sunset/sunrise starts and ends, as per LOFAR definition.
 SUN_SET_RISE_ANGLE_TO_HORIZON = Angle(10, unit=astropy.units.deg)
+SUN_SET_RISE_PRECISION = 15  # n_grid_points; higher is more precise but very costly; astropy defaults to 150, 15 seems to cause errors of typically one minute
 
 def timestamps_and_stations_to_sun_rise_and_set(timestamps: [datetime], stations: [str], angle_to_horizon: Angle=SUN_SET_RISE_ANGLE_TO_HORIZON) -> dict:
     """
@@ -46,13 +48,13 @@ def timestamps_and_stations_to_sun_rise_and_set(timestamps: [datetime], stations
     for station in stations:
         for timestamp in timestamps:
             observer = create_astroplan_observer_for_station(station)
-            sunrise_start = observer.sun_rise_time(time=Time(timestamp), which='previous')
+            sunrise_start = observer.sun_rise_time(time=Time(timestamp), which='previous', n_grid_points=SUN_SET_RISE_PRECISION)
             if sunrise_start.to_datetime().date() < timestamp.date():
-                sunrise_start = observer.sun_rise_time(time=Time(timestamp), horizon=-angle_to_horizon, which='next')
-            sunrise_end = observer.sun_rise_time(time=Time(timestamp), horizon=angle_to_horizon, which='next')
-            sunset_start = observer.sun_set_time(time=sunrise_end, horizon=angle_to_horizon, which='next')
-            sunset_end = observer.sun_set_time(time=sunrise_end, horizon=-angle_to_horizon, which='next')
-            sunrise_next_start = observer.sun_rise_time(time=sunset_end, horizon=-angle_to_horizon, which='next')
+                sunrise_start = observer.sun_rise_time(time=Time(timestamp), horizon=-angle_to_horizon, which='next', n_grid_points=SUN_SET_RISE_PRECISION)
+            sunrise_end = observer.sun_rise_time(time=Time(timestamp), horizon=angle_to_horizon, which='next', n_grid_points=SUN_SET_RISE_PRECISION)
+            sunset_start = observer.sun_set_time(time=sunrise_end, horizon=angle_to_horizon, which='next', n_grid_points=SUN_SET_RISE_PRECISION)
+            sunset_end = observer.sun_set_time(time=sunrise_end, horizon=-angle_to_horizon, which='next', n_grid_points=SUN_SET_RISE_PRECISION)
+            sunrise_next_start = observer.sun_rise_time(time=sunset_end, horizon=-angle_to_horizon, which='next', n_grid_points=SUN_SET_RISE_PRECISION)
             return_dict.setdefault(station, {}).setdefault("sunrise", []).append({"start": sunrise_start.to_datetime(), "end": sunrise_end.to_datetime()})
             return_dict[station].setdefault("sunset", []).append({"start": sunset_start.to_datetime(), "end": sunset_end.to_datetime()})
             return_dict[station].setdefault("day", []).append({"start": sunrise_end.to_datetime(), "end": sunset_start.to_datetime()})
diff --git a/SAS/TMSS/src/tmss/tmssapp/views.py b/SAS/TMSS/src/tmss/tmssapp/views.py
index a427ee8c33283082302861eb0366683c16b9b33a..7bee3fd449aea5f42c170720c42cba3160812b82 100644
--- a/SAS/TMSS/src/tmss/tmssapp/views.py
+++ b/SAS/TMSS/src/tmss/tmssapp/views.py
@@ -169,6 +169,7 @@ def get_sun_rise_and_set(request):
     else:
         stations = stations.split(',')
 
+    # todo: to improve speed for the frontend, we should probably precompute/cache these and return those (where available), to revisit after constraint table / TMSS-190 is done
     return JsonResponse(timestamps_and_stations_to_sun_rise_and_set(timestamps, stations))
 
 @permission_classes([AllowAny])
diff --git a/SAS/TMSS/test/t_conversions.py b/SAS/TMSS/test/t_conversions.py
index 14231c4f091c04b1f3c53b971bbf069555e6000f..8d987ce104c005598472b0781b1c2cb88cf6941d 100755
--- a/SAS/TMSS/test/t_conversions.py
+++ b/SAS/TMSS/test/t_conversions.py
@@ -165,6 +165,69 @@ class UtilREST(unittest.TestCase):
             response_date = dateutil.parser.parse(r_dict['CS002']['sunrise'][i]['start']).date()
             self.assertEqual(expected_date, response_date)
 
+    def test_util_angular_separation_from_bodies_yields_error_when_no_pointing_is_given(self):
+        r = requests.get(BASE_URL + '/util/angular_separation_from_bodies', auth=AUTH)
+
+        # assert error
+        self.assertEqual(r.status_code, 500)
+        self.assertIn("celestial coordinates", r.content.decode('utf-8'))
+
+    def test_util_angular_separation_from_bodies_returns_json_structure_with_defaults(self):
+        r = requests.get(BASE_URL + '/util/angular_separation_from_bodies?ras=1&decs=1', auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+
+        # assert defaults to core and today
+        self.assertIn('CS002', r_dict.keys())
+        for key in ['sun', 'jupiter', 'moon']:
+            self.assertIn(key, r_dict['CS002'][0])
+        self.assertEqual(type(r_dict['CS002'][0]['jupiter'][0]), float)
+
+    def test_util_angular_separation_from_bodies_considers_stations(self):
+        stations = ['CS005', 'RS305', 'DE609']
+        r = requests.get(BASE_URL + '/util/angular_separation_from_bodies?ras=1&decs=1&stations=%s' % ','.join(stations), auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+
+        # assert station is included in response and angles differ
+        angle_last = None
+        for station in stations:
+            self.assertIn(station, r_dict.keys())
+            angle = r_dict[station][0]['jupiter'][0]
+            if angle_last:
+                self.assertNotEqual(angle, angle_last)
+            angle_last = angle
+
+    def test_util_angular_separation_from_bodies_considers_timestamps(self):
+        timestamps = ['2020-01-01', '2020-02-22T16-00-00', '2020-3-11', '2020-01-01']
+        r = requests.get(BASE_URL + '/util/angular_separation_from_bodies?ras=1&decs=1&timestamps=%s' % ','.join(timestamps), auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+
+        # assert all requested timestamps yield a response and angles differ
+        self.assertEqual(len(timestamps), len(r_dict['CS002'][0]['jupiter']))
+        angle_last = None
+        for i in range(len(timestamps)):
+            angle = r_dict['CS002'][0]['jupiter'][i]
+            if angle_last:
+                self.assertNotEqual(angle, angle_last)
+            angle_last = angle
+
+    def test_util_angular_separation_from_bodies_considers_coordinates(self):
+        ras = ['1.0', '1.1', '1.2']
+        decs = ['1.0', '1.1', '1.2']
+        r = requests.get(BASE_URL + '/util/angular_separation_from_bodies?ras=%s&decs=%s' % (','.join(ras), ','.join(decs)), auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+
+        # assert all requested timestamps yield a response and angles differ
+        self.assertEqual(len(ras), len(r_dict['CS002']))
+        angle_last = None
+        for i in range(len(ras)):
+            angle = r_dict['CS002'][i]['jupiter'][0]
+            if angle_last:
+                self.assertNotEqual(angle, angle_last)
+            angle_last = angle
 
 if __name__ == "__main__":
     os.environ['TZ'] = 'UTC'