diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints.py b/SAS/TMSS/backend/services/scheduling/lib/constraints.py
index 91d4bca30a96d47e7250c3d5fe8d1c6ef9cadcef..6f699de78dc9b17cac1b8571c006cce09818f6ec 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/constraints.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/constraints.py
@@ -26,9 +26,6 @@ This module defines the 'API' to:
 These main methods are used in the dynamic_scheduler to pick the next best scheduling unit, and compute the midterm schedule.
 """
 
-import logging
-logger = logging.getLogger(__name__)
-
 from datetime import datetime, timedelta
 from dateutil import parser
 from typing import Callable
@@ -46,6 +43,9 @@ from lofar.sas.tmss.tmss.exceptions import *
 from lofar.sas.tmss.tmss.tmssapp.subtasks import enough_stations_available
 from lofar.sas.tmss.tmss.tmssapp.tasks import mark_independent_subtasks_in_scheduling_unit_blueprint_as_unschedulable
 
+import logging
+logger = logging.getLogger(__name__)
+
 # rough estimate of how many units and timestamp evaluations can be cached
 CACHE_SIZE = 1000 * 24 * 12 * 365
 
@@ -730,12 +730,6 @@ def evaluate_sky_transit_constraint(scheduling_unit: models.SchedulingUnitBluepr
     transit_from_limit_with_margin = transit_from_limit - 60*gridder.grid_minutes
     transit_to_limit_with_margin   = transit_to_limit   + 60*gridder.grid_minutes
 
-    # TODO: Remove this workaround when fixing bug in TMSS-2017
-    logger.info("Applying temporary sky.transit_offset=[-12h,+12h] as workaround to prevent unschedulable bug")
-    transit_from_limit = -12*60*60
-    transit_from_limit_with_margin = -12*60*60
-    transit_to_limit = 12*60*60
-    transit_to_limit_with_margin = 12*60*60
 
     # transits are only computed for target observations
     target_obs_tasks = [t for t in scheduling_unit.observation_tasks if t.is_target_observation]
@@ -768,23 +762,23 @@ def evaluate_sky_transit_constraint(scheduling_unit: models.SchedulingUnitBluepr
 
                 # transit minus half duration is by definition the optimal start_time
                 # also take the task relative start time against the su.starttime into account
-                result.optimal_start_time = transit_timestamp - target_obs_task.relative_start_time - (target_obs_task.specified_duration / 2)
+                result.optimal_start_time = transit_timestamp - (target_obs_task.specified_duration / 2) - target_obs_task.relative_start_time
 
                 # earliest_possible_start_time is the transit plus the lower limit (which is usually negative)
-                # also take the task relative start time against the su.starttime into account
-                result.earliest_possible_start_time = transit_timestamp + timedelta(seconds=transit_from_limit) - target_obs_task.relative_start_time
+                result.earliest_possible_start_time = result.optimal_start_time + timedelta(seconds=transit_from_limit)
 
                 # now check if the constraint is met, and compute/set score
                 # include the margins for gridding effects when checking offset-within-window,
                 # but not when computing score
-                offset = (transit_timestamp-task_proposed_center_time).total_seconds()
+                offset = (task_proposed_center_time-transit_timestamp).total_seconds()
                 if offset > transit_from_limit_with_margin and offset < transit_to_limit_with_margin:
                     # constraint is met. compute score.
                     # 1.0 when proposed_center_time==transit_timestamp
                     # 0.0 at either translit offset limit
-                    score_from = min(1.0, max(0.0, (transit_from_limit - offset)/transit_from_limit))
-                    score_to = min(1.0, max(0.0, (transit_to_limit - offset)/transit_to_limit))
-                    result.score = 0.5*score_from + 0.5*score_to
+                    if offset <= 0:
+                        result.score = min(1.0, max(0.0, (offset - transit_from_limit_with_margin)/abs(transit_from_limit_with_margin)))
+                    else:
+                        result.score = min(1.0, max(0.0, (transit_to_limit_with_margin - offset)/abs(transit_to_limit_with_margin)))
                 else:
                     result.score = 0
 
@@ -890,16 +884,24 @@ def evaluate_sky_min_elevation_constraint(scheduling_unit: models.SchedulingUnit
 
 def get_transit_timestamp_offset_and_elevation(scheduling_unit: models.SchedulingUnitBlueprint, proposed_start_time: datetime, gridder: Gridder=None) -> (datetime, float):
     '''Get the transit timestamp, offset (in seconds) and elevation (in radions) for the given scheduling_unit at the given proposed_start_time'''
-    if gridder is None:
-        gridder = Gridder(1)
-
     if proposed_start_time is None:
         proposed_start_time = scheduling_unit.scheduled_start_time
 
+    if proposed_start_time is None: # still none? because scheduling_unit.scheduled_start_time is None
+        return None, None, None
+
+    if gridder is None:
+        gridder = Gridder(1)
+
     observer = create_astroplan_observer_for_station('CS002')
-    transit_pointings = get_transit_offset_pointings(scheduling_unit.main_observation_task)
-    gridded_start_time = gridder.grid_time(proposed_start_time + scheduling_unit.main_observation_task.relative_start_time)
-    proposed_center_time = proposed_start_time + scheduling_unit.main_observation_task.relative_start_time + scheduling_unit.main_observation_task.specified_duration / 2
+
+    target_obs_task = scheduling_unit.main_observation_task
+    transit_pointings = get_transit_offset_pointings(target_obs_task)
+
+    # take along the relative_start_time of this task compared to the scheduling unit's start_time
+    task_proposed_start_time = proposed_start_time + target_obs_task.relative_start_time
+    task_proposed_center_time = task_proposed_start_time + target_obs_task.specified_duration / 2
+    gridded_start_time = gridder.grid_time(task_proposed_start_time)
 
     min_elevation = 1e99
     min_elevation_transit = None
@@ -915,7 +917,7 @@ def get_transit_timestamp_offset_and_elevation(scheduling_unit: models.Schedulin
         if elevation < min_elevation:
             min_elevation = elevation
             min_elevation_transit = transit_timestamp
-            min_offset = (proposed_center_time - min_elevation_transit).total_seconds()
+            min_offset = (task_proposed_center_time - min_elevation_transit).total_seconds()
 
     return (min_elevation_transit, min_offset, min_elevation)
 
@@ -1023,6 +1025,8 @@ def get_earliest_possible_start_time_for_sky_min_elevation(scheduling_unit: mode
 
 def get_earliest_possible_start_time_for_sky_transit_offset(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime=None, gridder: Gridder=None) -> datetime:
     # compute the transit time, and thus the optimal_start_time and earliest_possible_start_time
+    if gridder is None:
+        gridder = Gridder()
 
     gridded_lower_bound = gridder.grid_time(lower_bound)
     possible_start_time = gridded_lower_bound
@@ -1036,15 +1040,16 @@ def get_earliest_possible_start_time_for_sky_transit_offset(scheduling_unit: mod
         if not result.has_constraint:
             return None
 
-        if result.is_constraint_met:
+        if result.is_constraint_met and result.earliest_possible_start_time >= lower_bound:
             return result.earliest_possible_start_time
 
         # constraint is not met, advance possible_start_time and evaluate again
         if result.earliest_possible_start_time is None or result.earliest_possible_start_time < lower_bound:
-            # advance with a grid step, and evaluate again
+            # advance, and evaluate again
             possible_start_time += gridder.as_timedelta()
             continue
 
+        # constraint is not met, advance using estimate of earliest_possible_start_time
         if result.earliest_possible_start_time is not None:
             # advance straight to earliest_possible_start_time, and evaluate again to ensure the constraint is met
             next_possible_start_time = gridder.grid_time(result.earliest_possible_start_time)
@@ -1597,6 +1602,7 @@ def determine_unschedulable_reason_and_mark_unschedulable_if_needed(scheduling_u
                         unmet_constraints.append("sky min elevation")
 
                 if 'transit_offset' in scheduling_unit.scheduling_constraints_doc['sky']:
+                    # TODO: this check ignores the 'at' contraint. Fix that
                     if get_earliest_possible_start_time_for_sky_transit_offset(scheduling_unit, lower_bound, upper_bound, gridder) is None:
                         unmet_constraints.append("sky transit offset")
 
diff --git a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
index 7ecf79bd838f8b97cf4c8c28b31c116b339e663e..1b2fe30f72c31f927e7475ca2f6fa046777e0ece 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
@@ -52,16 +52,14 @@ and then triggering a new computation of a full schedule in the Scheduler.
 
 import os
 
-import logging
 
 import astropy.coordinates
 
-logger = logging.getLogger(__name__)
 from datetime import datetime, timedelta, time
 
 from lofar.sas.tmss.tmss.tmssapp import models
 from lofar.sas.tmss.tmss.tmssapp.tasks import schedule_independent_subtasks_in_scheduling_unit_blueprint, unschedule_subtasks_in_scheduling_unit_blueprint, mark_independent_subtasks_in_scheduling_unit_blueprint_as_unschedulable, mark_independent_subtasks_in_scheduling_unit_blueprint_as_schedulable, mark_independent_subtasks_in_scheduling_unit_blueprint_as_schedulable, set_scheduling_unit_blueprint_start_times, reschedule_subtasks_in_scheduling_unit_blueprint
-from lofar.sas.tmss.tmss.tmssapp.subtasks import update_subtasks_start_times_for_scheduling_unit, clear_defined_subtasks_start_stop_times_for_scheduling_unit, cancel_subtask, mark_subtasks_and_successors_as_defined
+from lofar.sas.tmss.tmss.tmssapp.subtasks import update_subtasks_start_times_for_scheduling_unit, cancel_subtask, mark_subtasks_and_successors_as_defined
 from lofar.sas.tmss.client.tmssbuslistener import *
 from lofar.common.datetimeutils import round_to_second_precision
 from threading import Thread, Event
@@ -71,6 +69,9 @@ from django.db.models import QuerySet, Q, Max
 from lofar.sas.tmss.tmss.exceptions import SchedulerInterruptedException
 from lofar.sas.tmss.services.scheduling.constraints import *
 
+import logging
+logger = logging.getLogger(__name__)
+
 
 # LOFAR needs to have a gap in between observations to (re)initialize hardware.
 DEFAULT_NEXT_STARTTIME_GAP = timedelta(seconds=180)
@@ -309,6 +310,9 @@ class Scheduler:
         # ensure upper is greater than or equal to lower
         upper_bound_stop_time = max(lower_bound_start_time, upper_bound_stop_time)
 
+        if not scheduling_units:
+            return None
+
         logger.info("find_best_next_schedulable_unit: evaluating constraints for units in window ['%s', '%s']: %s", lower_bound_start_time, upper_bound_stop_time, ','.join([str(su.id) for su in sorted(scheduling_units, key=lambda x: x.id)]) or 'None')
 
         # first, from all given scheduling_units, filter and consider only those that meet their constraints.
@@ -400,6 +404,10 @@ class Scheduler:
             while lower_bound_start_time < upper_bound_stop_time:
                 self._raise_if_triggered()  # interrupts the scheduling loop for a next round
 
+                if not candidate_units:
+                    logger.info("schedule_next_scheduling_unit: no more candidate units...")
+                    break
+
                 try:
                     # no need to irritate user in log files with sub-second scheduling precision
                     lower_bound_start_time = round_to_second_precision(lower_bound_start_time)
@@ -575,8 +583,8 @@ class Scheduler:
                             ("'%s'" % (unit.name[:32],)).ljust(34),
                             unit.scheduled_start_time,
                             unit.status.value.ljust(14),
-                            transit_offset / 60,
-                            Angle(elevation, astropy.units.rad).degree)
+                            transit_offset / 60 if transit_offset else None,
+                            Angle(elevation, astropy.units.rad).degree) if elevation else None
                     except Exception as e:
                         logger.warning(e)
                 logger.log(log_level, "-----------------------------------------------------------------")
@@ -763,6 +771,7 @@ class TMSSDynamicSchedulingMessageHandler(TMSSEventMessageHandler):
         weight_factor = models.SchedulingConstraintsWeightFactor.objects.get(id=id)
         logger.info("weight_factor %s for template %s version %s changed to %s: triggering update of dynamic schedule...",
                     weight_factor.constraint_name, weight_factor.scheduling_constraints_template.name, weight_factor.scheduling_constraints_template.version, weight_factor.weight)
+        wipe_evaluate_constraints_caches()
         mark_unschedulable_scheduling_units_for_active_projects_schedulable()
         self.scheduler.trigger()
 
diff --git a/SAS/TMSS/backend/services/scheduling/lib/subtask_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/subtask_scheduling.py
index 323fe4c4f73d37555c1da63eaae25be276405017..9246fe17d0e3d2ab1d9271ed5f8f03dda0dad9ec 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/subtask_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/subtask_scheduling.py
@@ -30,8 +30,6 @@ it schedules (rest action) all successors that are in state 'defined'.
 
 import os
 from optparse import OptionParser
-import logging
-logger = logging.getLogger(__name__)
 
 from lofar.sas.tmss.client.tmssbuslistener import *
 from lofar.sas.tmss.tmss.tmssapp.models import Subtask, SubtaskState, SubtaskType, SchedulingUnitBlueprint, TaskBlueprint
@@ -40,6 +38,9 @@ from lofar.sas.tmss.tmss.exceptions import TMSSException
 from lofar.common.datetimeutils import round_to_second_precision
 from datetime import datetime, timedelta
 
+import logging
+logger = logging.getLogger(__name__)
+
 
 class TMSSSubTaskSchedulingEventMessageHandler(TMSSEventMessageHandler):
     '''
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py b/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py
index 97074592dae60ce05fc40fce4ff34a6e1577fb02..9dedc2de6dbaa577c23702b93056fcdb57bd3d51 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py
@@ -389,7 +389,7 @@ def compute_elevation(pointing: Pointing, timestamp: datetime, station: str='CS0
 def local_sidereal_time_for_utc_and_station(timestamp: datetime = None,
                                             station: str = 'CS002',
                                             field: str = 'LBA',
-                                            kind: str = "apparent"):
+                                            kind: str = "apparent") -> astropy.coordinates.Longitude:
     """
     calculate local sidereal time for given utc time and station
     :param timestamp: timestamp as datetime object
@@ -398,19 +398,15 @@ def local_sidereal_time_for_utc_and_station(timestamp: datetime = None,
     :param kind: 'mean' or 'apparent'
     :return:
     """
-    from lofar.lta.sip import station_coordinates
-
     if timestamp is None:
         timestamp = datetime.utcnow()
-    station_coords = station_coordinates.parse_station_coordinates()
-    field_coords = station_coords["%s_%s" % (station, field)]
-    location = EarthLocation.from_geocentric(x=field_coords['x'], y=field_coords['y'], z=field_coords['z'], unit=astropy.units.m)
+    location = create_location_for_station(station)
     return local_sidereal_time_for_utc_and_longitude(timestamp=timestamp, longitude=location.lon.to_string(decimal=True), kind=kind)
 
 
 def local_sidereal_time_for_utc_and_longitude(timestamp: datetime = None,
                                               longitude: float = 6.8693028,
-                                              kind: str = "apparent"):
+                                              kind: str = "apparent") -> astropy.coordinates.Longitude:
     """
     :param timestamp: timestamp as datetime object
     :param longitude: decimal longitude of observer location (defaults to CS002 LBA center)
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
index 9647e847409c0cd9a230f2f37dc7fe0560a26cee..28f75f91f589c2ccddab103c3a0fe7d18c7fb4d3 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
@@ -28,7 +28,7 @@ from lofar.common.cobaltblocksize import CorrelatorSettings, StokesSettings, Blo
 from lofar.mac.observation_control_rpc import ObservationControlRPCClient
 from lofar.mac.pipeline_control_rpc import PipelineControlRPCClient
 
-from lofar.sas.tmss.tmss.tmssapp.conversions import antennafields_for_antennaset_and_station, create_location_for_station, Time
+from lofar.sas.tmss.tmss.tmssapp.conversions import antennafields_for_antennaset_and_station, create_location_for_station, Time, local_sidereal_time_for_utc_and_station
 from lofar.sas.tmss.tmss.exceptions import TMSSException, TooManyStationsUnavailableException
 from django.db import transaction
 from django.db.models import Q
@@ -1653,13 +1653,14 @@ def compute_scheduled_central_lst(subtask: Subtask) -> Subtask:
         scheduled_duration = subtask.scheduled_stop_time - subtask.scheduled_start_time
         scheduled_central_time = subtask.scheduled_start_time + 0.5*scheduled_duration
 
-        # convert the UTC timestamp to LST at CS002
-        cs002_location = create_location_for_station('CS002')
-        scheduled_central_lst = Time(scheduled_central_time, scale='utc', location=cs002_location).sidereal_time('apparent')
+        # convert the UTC timestamp to LST at CS002 (expressed as astropy Longitude)
+        lst_longitude = local_sidereal_time_for_utc_and_station(scheduled_central_time, 'CS002')
 
-        # convert the astropy Longitude LST time back to a native python time via datetime
-        scheduled_central_lst = datetime.fromordinal(scheduled_central_time.date().toordinal()) + timedelta(hours=scheduled_central_lst.hour)
-        subtask.scheduled_central_lst = round_to_second_precision(scheduled_central_lst).time()
+        # convert the astropy Longitude LST time back to a native python time (ignore microsecond)
+        # we're "forced" to convert via datetime, evan though the date is irrelevant (and discarded)
+        lst_longitude_as_timedelta = timedelta(hours=lst_longitude.hour)
+        lst_longitude_as_py_time = (datetime(2022, 1, 1, 0, 0, 0) + lst_longitude_as_timedelta).time().replace(microsecond=0)
+        subtask.scheduled_central_lst = lst_longitude_as_py_time
     else:
         subtask.scheduled_central_lst = None