diff --git a/tangostationcontrol/tangostationcontrol/devices/calibration.py b/tangostationcontrol/tangostationcontrol/devices/calibration.py
index 7de1215650323a9aac188a6275af8806dba909ac..b295a47bce3be34d6d3ae170135e02c93385cd97 100644
--- a/tangostationcontrol/tangostationcontrol/devices/calibration.py
+++ b/tangostationcontrol/tangostationcontrol/devices/calibration.py
@@ -4,7 +4,9 @@
 """ Calibration Device Server for LOFAR2.0
 
 """
+import datetime
 import logging
+import numpy
 
 from tango import EventType, Database
 from tango.server import device_property, command, attribute
@@ -42,13 +44,30 @@ class Calibration(LOFARDevice):
         self.sdpfirmware_proxies: dict = {}
         self.sdp_proxies: dict = {}
         self.ant_proxies: dict = {}
+        self.last_ant_calibration_timestamp: dict[str, datetime.datetime | None] = {}
 
         # Super must be called after variable assignment due to executing init_device!
         super().__init__(cl, name)
 
+    def _calibrate_antenna_field(self, ant_proxy):
+        """Recalibrate a specific AntennaField."""
+
+        if ant_proxy.state() not in DEFAULT_COMMAND_STATES:
+            logger.warning(
+                f"Device {ant_proxy.name()} not active. Forgoing calibration."
+            )
+            return
+
+        logger.info("Re-calibrate antenna field %s", ant_proxy.name())
+
+        self.last_ant_calibration_timestamp[ant_proxy] = datetime.datetime.now()
+
+        self.calibrate_recv(ant_proxy)
+        self.calibrate_sdp(ant_proxy)
+
     @log_exceptions()
-    def _frequency_band_changed_event(self, event):
-        """Trigger on external changes in frequency settings."""
+    def _antennafield_changed_event(self, event):
+        """Trigger on key external changes in AntennaField settings."""
 
         if event.err:
             # little we can do here. note that errors are also
@@ -63,13 +82,11 @@ class Calibration(LOFARDevice):
         )
 
         if self.dev_state() not in DEFAULT_COMMAND_STATES:
-            logger.warning("Device not active. Ignore frequency band changed event")
+            logger.warning("Device not active. Ignore AntennaField changed event")
             return
 
         # frequencies changed, so we need to recalibrate
-        logger.info("Re-calibrate antenna field %s", event.device.name())
-        self.calibrate_recv(event.device)
-        self.calibrate_sdp(event.device)
+        self._calibrate_antenna_field(event.device)
 
     @log_exceptions()
     def _clock_changed_event(self, event):
@@ -91,12 +108,12 @@ class Calibration(LOFARDevice):
             return
 
         for k, ant in self.ant_proxies.items():
+            # Recalibrate associated AntennaFields
             sdpfirmware_device = ant.SDPFirmware_device_R.casefold()
             sdp_device = self.sdpfirmware_proxies[sdpfirmware_device].SDP_device_R
+
             if device_name_matches(sdp_device, event.device.name()):
-                logger.info("Re-calibrate antenna field %s", k)
-                self.calibrate_sdp(k)
-                self.calibrate_recv(k)
+                self._calibrate_antenna_field(k)
 
     # TODO(JDM): While we could read this from our control parent (StationManager),
     # doing so creates a deadlock when StationManager wants to initialise this
@@ -121,16 +138,29 @@ class Calibration(LOFARDevice):
     def AntennaFields_Monitored_R(self):
         return list(self.ant_proxies.keys())
 
+    @attribute(dtype=(numpy.int64,), max_dim_x=20)
+    def Last_AntennaField_Calibration_Timestamp_R(self):
+        return numpy.array(
+            [
+                ts.timestamp() if ts else 0
+                for ts in self.last_ant_calibration_timestamp.values()
+            ],
+            dtype=numpy.int64,
+        )
+
     @attribute(dtype=(str,), max_dim_x=20)
     def SDPs_Monitored_R(self):
         return list(self.sdp_proxies.keys())
 
-    @command
+    @command()
+    @only_in_states(DEFAULT_COMMAND_STATES)
     def download_calibration_tables(self):
+        """Download the latest calibration tables and apply them."""
+
         self._calibration_manager.sync_calibration_tables()
-        for ant in self.ant_proxies.keys():
-            self.calibrate_recv(ant)
-            self.calibrate_sdp(ant)
+
+        # Apply downloaded tables
+        self.calibrate_all()
 
     @command(dtype_in=str)
     @only_in_states(DEFAULT_COMMAND_STATES)
@@ -200,8 +230,7 @@ class Calibration(LOFARDevice):
             logger.info("Re-calibrate antenna field %s", k)
 
             try:
-                self.calibrate_sdp(k)
-                self.calibrate_recv(k)
+                self._calibrate_antenna_field(k)
             except Exception as exc:
                 logger.exception("Could not calibrate antenna field %s", k)
 
@@ -221,6 +250,7 @@ class Calibration(LOFARDevice):
         for d in devices:
             logger.debug("found antenna field device %s", str(d))
         self.ant_proxies = {d.casefold(): create_device_proxy(d) for d in devices}
+        self.last_ant_calibration_timestamp = {d.casefold(): None for d in devices}
 
         devices = db.get_device_exported_for_class(SDPFirmware.__name__)
         for d in devices:
@@ -243,11 +273,24 @@ class Calibration(LOFARDevice):
                     prx.subscribe_event(
                         "Frequency_Band_RW",
                         EventType.CHANGE_EVENT,
-                        self._frequency_band_changed_event,
+                        self._antennafield_changed_event,
                         stateless=True,
                     ),
                 )
             )
+
+            self.event_subscriptions.append(
+                (
+                    prx,
+                    prx.subscribe_event(
+                        "State",
+                        EventType.CHANGE_EVENT,
+                        self._antennafield_changed_event,
+                        stateless=True,
+                    ),
+                )
+            )
+
         for prx in self.sdpfirmware_proxies.values():
             self.event_subscriptions.append(
                 (