diff --git a/tangostationcontrol/tangostationcontrol/clients/statistics_client.py b/tangostationcontrol/tangostationcontrol/clients/statistics_client.py
index e602b410ab202906f2953ec1a722b74a2ff64734..08d2889d0b61c5bd9c9ce5468e35ac6bf82fdffb 100644
--- a/tangostationcontrol/tangostationcontrol/clients/statistics_client.py
+++ b/tangostationcontrol/tangostationcontrol/clients/statistics_client.py
@@ -99,10 +99,10 @@ class StatisticsClient(AsyncCommClient):
             def read_function():
                 if annotation.get("reshape", False):
                     # force array into the shape of the attribute
-                    if attribute.max_dim_y > 1:
-                        return self.collector.parameters[parameter].reshape(attribute.max_dim_y, attribute.max_dim_x)
+                    if attribute.dim_y > 1:
+                        return self.collector.parameters[parameter].reshape(attribute.dim_y, attribute.dim_x)
                     else:
-                        return self.collector.parameters[parameter].reshape(attribute.max_dim_x)
+                        return self.collector.parameters[parameter].reshape(attribute.dim_x)
                 else:
                     return self.collector.parameters[parameter]
         elif annotation["type"] == "udp":
diff --git a/tangostationcontrol/tangostationcontrol/devices/apsct.py b/tangostationcontrol/tangostationcontrol/devices/apsct.py
index 20b161e97b16f99d352340246770d5db9f99f3e3..6a3ef5ff8a609eecbc86242d745c7fcb97f61b1a 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apsct.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apsct.py
@@ -79,12 +79,12 @@ class APSCT(opcua_device):
 
     def read_APSCT_error_R(self):
         return ((self.proxy.APSCTTR_I2C_error_R > 0)
-               | self.alarm_val("APSCT_PCB_ID_R")
-               | ~self.proxy.APSCT_INPUT_10MHz_good_R
-               | (~self.proxy.APSCT_INPUT_PPS_good_R & ~self.proxy.ASPCT_PPS_ignore_R)
-               | (~self.proxy.APSCT_PLL_160MHz_locked_R & ~self.proxy.APSCT_PLL_200MHz_locked_R)
-               | (self.proxy.APSCT_PLL_200MHz_locked_R & self.proxy.APSCT_PLL_200MHz_error_R)
-               | (self.proxy.APSCT_PLL_160MHz_locked_R & self.proxy.APSCT_PLL_160MHz_error_R)
+               or self.alarm_val("APSCT_PCB_ID_R")
+               or (not self.proxy.APSCT_INPUT_10MHz_good_R)
+               or (not self.proxy.APSCT_INPUT_PPS_good_R and not self.proxy.APSCT_PPS_ignore_R)
+               or (not self.proxy.APSCT_PLL_160MHz_locked_R and not self.proxy.APSCT_PLL_200MHz_locked_R)
+               or (self.proxy.APSCT_PLL_200MHz_locked_R and self.proxy.APSCT_PLL_200MHz_error_R)
+               or (self.proxy.APSCT_PLL_160MHz_locked_R and self.proxy.APSCT_PLL_160MHz_error_R)
                )
 
     APSCT_TEMP_error_R            = attribute(dtype=bool)
@@ -95,13 +95,13 @@ class APSCT(opcua_device):
 
     def read_APSCT_VOUT_error_R(self):
         return ( self.alarm_val("APSCT_PWR_PPSDIST_3V3_R")
-               | self.alarm_val("APSCT_PWR_CLKDIST1_3V3_R")
-               | self.alarm_val("APSCT_PWR_CLKDIST2_3V3_R")
-               | self.alarm_val("APSCT_PWR_CTRL_3V3_R")
-               | self.alarm_val("APSCT_PWR_INPUT_3V3_R")
-               | (self.proxy.APSCT_PWR_PLL_160MHz_on_R & self.alarm_val("APSCT_PWR_PLL_160MHz_3V3_R"))
-               | (self.proxy.APSCT_PWR_PLL_200MHz_on_R & self.alarm_val("APSCT_PWR_PLL_200MHz_3V3_R"))
-               | ~self.proxy.APSCT_PWR_on_R
+               or self.alarm_val("APSCT_PWR_CLKDIST1_3V3_R")
+               or self.alarm_val("APSCT_PWR_CLKDIST2_3V3_R")
+               or self.alarm_val("APSCT_PWR_CTRL_3V3_R")
+               or self.alarm_val("APSCT_PWR_INPUT_3V3_R")
+               or (self.proxy.APSCT_PWR_PLL_160MHz_on_R and self.alarm_val("APSCT_PWR_PLL_160MHz_3V3_R"))
+               or (self.proxy.APSCT_PWR_PLL_200MHz_on_R and self.alarm_val("APSCT_PWR_PLL_200MHz_3V3_R"))
+               or (not self.proxy.APSCT_PWR_on_R)
                )
 
     # --------
diff --git a/tangostationcontrol/tangostationcontrol/devices/apspu.py b/tangostationcontrol/tangostationcontrol/devices/apspu.py
index d014a0eae151d89e77c71a9f0048dfb5e01bcd6b..6be213696c16efc69ca8f20319efc03cfc0f4fe7 100644
--- a/tangostationcontrol/tangostationcontrol/devices/apspu.py
+++ b/tangostationcontrol/tangostationcontrol/devices/apspu.py
@@ -67,10 +67,10 @@ class APSPU(opcua_device):
 
     def read_APSPU_error_R(self):
         return ((self.proxy.APSPUTR_I2C_error_R > 0)
-               | self.alarm_val("APSPU_PCB_ID_R")
-               | self.alarm_val("APSPU_FAN1_RPM_R")
-               | self.alarm_val("APSPU_FAN2_RPM_R") 
-               | self.alarm_val("APSPU_FAN3_RPM_R"))
+               or self.alarm_val("APSPU_PCB_ID_R")
+               or self.alarm_val("APSPU_FAN1_RPM_R")
+               or self.alarm_val("APSPU_FAN2_RPM_R") 
+               or self.alarm_val("APSPU_FAN3_RPM_R"))
 
     APSPU_IOUT_error_R          = attribute(dtype=bool)
     APSPU_TEMP_error_R          = attribute(dtype=bool)
@@ -78,20 +78,20 @@ class APSPU(opcua_device):
 
     def read_APSPU_IOUT_error_R(self):
         return ( self.alarm_val("APSPU_LBA_IOUT_R")
-               | self.alarm_val("APSPU_RCU2A_IOUT_R")
-               | self.alarm_val("APSPU_RCU2D_IOUT_R")
+               or self.alarm_val("APSPU_RCU2A_IOUT_R")
+               or self.alarm_val("APSPU_RCU2D_IOUT_R")
                )
 
     def read_APSPU_TEMP_error_R(self):
         return ( self.alarm_val("APSPU_LBA_TEMP_R")
-               | self.alarm_val("APSPU_RCU2A_TEMP_R")
-               | self.alarm_val("APSPU_RCU2D_TEMP_R")
+               or self.alarm_val("APSPU_RCU2A_TEMP_R")
+               or self.alarm_val("APSPU_RCU2D_TEMP_R")
                )
 
     def read_APSPU_VOUT_error_R(self):
         return ( self.alarm_val("APSPU_LBA_VOUT_R")
-               | self.alarm_val("APSPU_RCU2A_VOUT_R")
-               | self.alarm_val("APSPU_RCU2D_VOUT_R")
+               or self.alarm_val("APSPU_RCU2A_VOUT_R")
+               or self.alarm_val("APSPU_RCU2D_VOUT_R")
                )
 
     # --------
diff --git a/tangostationcontrol/tangostationcontrol/devices/beam.py b/tangostationcontrol/tangostationcontrol/devices/beam.py
index 9540560a9537eca57d16e208defd44dffa8d9814..3709bc5602dda655892b6d318e59c2eef77de716 100644
--- a/tangostationcontrol/tangostationcontrol/devices/beam.py
+++ b/tangostationcontrol/tangostationcontrol/devices/beam.py
@@ -12,7 +12,7 @@ import datetime
 from json import loads
 
 from tango.server import attribute, command, device_property
-from tango import AttrWriteType, DebugIt, DevState, DeviceProxy, DevVarStringArray, DevVarDoubleArray, DevString
+from tango import AttrWriteType, DebugIt, DevState, DeviceProxy, DevVarStringArray, DevVarDoubleArray, DevString, DevSource
 from threading import Thread, Lock, Condition
 
 # Additional import
@@ -67,6 +67,16 @@ class Beam(lofar_device):
         dtype=(numpy.double,), max_dim_x=96,
         fget=lambda self: self._hbat_pointing_timestamp_r)
 
+    HBAT_tracking_enabled_R = attribute(access=AttrWriteType.READ,
+        doc="Whether the HBAT tile beam is updated periodically",
+        dtype=numpy.bool,
+        fget=lambda self: self.HBAT_beam_tracker.is_alive())
+
+    HBAT_tracking_enabled_RW = attribute(access=AttrWriteType.READ_WRITE,
+        doc="Whether the HBAT tile beam should be updated periodically",
+        dtype=numpy.bool,
+        fget=lambda self: self._hbat_tracking_enabled_rw)
+
     # Directory where the casacore measures that we use, reside. We configure ~/.casarc to
     # use the symlink /opt/IERS/current, which we switch to the actual set of files to use.
     measures_directory_R = attribute(dtype=str, access=AttrWriteType.READ, fget = lambda self: get_measures_directory())
@@ -86,8 +96,12 @@ class Beam(lofar_device):
         self._hbat_pointing_direction_r     = numpy.zeros((96,3), dtype="<U32")
         self._hbat_pointing_direction_rw    = numpy.array([["AZELGEO","0deg","90deg"]] * 96, dtype="<U32")
 
+        # Initialise tracking control
+        self._hbat_tracking_enabled_rw      = True
+
         # Set a reference of RECV device
         self.recv_proxy = DeviceProxy("STAT/RECV/1")
+        self.recv_proxy.set_source(DevSource.DEV)
 
         # Retrieve positions from RECV device
         HBAT_reference_itrf = self.recv_proxy.HBAT_reference_itrf_R
@@ -107,15 +121,15 @@ class Beam(lofar_device):
         super().configure_for_on()
 
         # Start beam tracking thread
-        self.HBAT_beam_tracker.start()
+        if self._hbat_tracking_enabled_rw:
+            self.HBAT_beam_tracker.start()
     
-    @log_exceptions
+    @log_exceptions()
     def configure_for_off(self):
-        super().configure_for_off()
-
         # Stop thread object
         self.HBAT_beam_tracker.stop()
-        self.HBAT_beam_tracker = None
+
+        super().configure_for_off()
 
     # --------
     # internal functions
@@ -132,6 +146,15 @@ class Beam(lofar_device):
 
         # force update across tiles if pointing changes
         self.HBAT_beam_tracker.force_update()
+        logger.info("Pointing direction update requested")
+
+    def write_HBAT_tracking_enabled_RW(self, value):
+        self._hbat_tracking_enabled_rw = value
+
+        if value:
+            self.HBAT_beam_tracker.start()
+        else:
+            self.HBAT_beam_tracker.stop()
 
     def _HBAT_delays(self, pointing_direction: numpy.array, timestamp: datetime.datetime = datetime.datetime.now()):
         """
@@ -166,7 +189,7 @@ class Beam(lofar_device):
 
         # Record where we now point to, now that we've updated the weights.
         # Only the entries within the mask have been updated
-        mask = self.recv_proxy.Ant_mask_RW.flatten()
+        mask = self.recv_proxy.ANT_mask_RW.flatten()
         for rcu in range(96):
             if mask[rcu]:
                 self._hbat_pointing_direction_r[rcu]    = pointing_direction[rcu]
@@ -225,6 +248,7 @@ class Beam(lofar_device):
     
     @command(dtype_in=DevVarStringArray)
     @DebugIt()
+    @log_exceptions()
     @only_in_states([DevState.ON])
     def HBAT_set_pointing(self, pointing_direction: list, timestamp: datetime.datetime = datetime.datetime.now()):
         """
@@ -275,25 +299,39 @@ class BeamTracker():
 
     """ Object that encapsulates a Thread, resposible for beam tracking operations """
     def __init__(self, device: lofar_device):
-        self.thread = Thread(target=self._update_HBAT_pointing_direction)
+        self.thread = None
         self.device = device
 
         # Condition to trigger a forced update or early abort
         self.update_lock = Lock()
         self.update_condition = Condition(self.update_lock)
+
+        # Whether the pointing has to be forced updated
+        self.stale_pointing = True
     
     def start(self):
         """ Starts the Beam Tracking thread """
+        if self.thread:
+            # already started
+            return
+
         self.done = False
+        self.thread = Thread(target=self._update_HBAT_pointing_direction, name=f"BeamTracker of {self.device.get_name()}")
         self.thread.start()
+
+        logger.info("BeamTracking thread started")
     
     def is_alive(self):
         """ Returns True just before the Thread run() method starts until just after the Thread run() method terminates. """
-        return self.thread.is_alive()
+        return self.thread and self.thread.is_alive()
 
     def force_update(self):
         """ Force the pointing to be updated. """
 
+        self.stale_pointing = True
+        self.unlock_thread()
+
+    def unlock_thread(self):
         # inform the thread to stop waiting
         with self.update_lock:
             self.update_condition.notify()
@@ -301,6 +339,11 @@ class BeamTracker():
     def stop(self):
         """ Stops the Beam Tracking loop """
 
+        if not self.thread:
+            return
+        
+        logger.info("BeamTracking thread stopping")
+
         self.done = True
         self.force_update()
 
@@ -309,6 +352,10 @@ class BeamTracker():
 
         if self.is_alive():
             logger.error("BeamTracking Thread did not properly terminate")
+
+        self.thread = None
+        
+        logger.info("BeamTracking thread stopped")
     
     def _get_sleep_time(self):
         """ Computes the sleep time (in seconds) that needs to be waited for the next beam tracking update """  
@@ -324,14 +371,22 @@ class BeamTracker():
             return sleep_time + self.device.HBAT_beam_tracking_interval
         else:
             return sleep_time
-    
+
+    # @fault_on_error routes errors here. we forward them to our device
+    def Fault(self, msg):
+        self.device.Fault(msg)
+   
+    @log_exceptions()
+    @fault_on_error()
     def _update_HBAT_pointing_direction(self):
         """ Updates the beam weights using a fixed interval of time """
 
         # Check if  flag beamtracking is true
         with self.update_lock:
             while not self.done:
-                self.device.HBAT_set_pointing(numpy.array(self.device.proxy.HBAT_pointing_direction_RW).flatten())
+                self.stale_pointing = False
+                self.device._HBAT_set_pointing(self.device._hbat_pointing_direction_rw, datetime.datetime.now())
 
                 # sleep until the next update, or when interrupted (this releases the lock, allowing for notification)
-                self.update_condition.wait(self._get_sleep_time())
+                # note that we need wait_for as conditions can be triggered multiple times in succession
+                self.update_condition.wait_for(lambda: self.done or self.stale_pointing, self._get_sleep_time())
diff --git a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py
index bb9bf90099aa3b96bdc83011678481dca720191f..6943f86d4bde44010662cbcea45cb350ca271cea 100644
--- a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py
+++ b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py
@@ -430,5 +430,8 @@ class lofar_device(Device, metaclass=DeviceMeta):
             alarm_state |= value <= value.dtype.type(alarms.min_alarm)
 
         # return alarm state, with the same dimensions as the attribute
-        return alarm_state
+        if is_scalar:
+            return alarm_state.item()
+        else:
+            return alarm_state
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/recv.py b/tangostationcontrol/tangostationcontrol/devices/recv.py
index db29fe62c791a6b75744954f1f45090b7f777414..b853b60406511930ae262458564bb52eff111fe1 100644
--- a/tangostationcontrol/tangostationcontrol/devices/recv.py
+++ b/tangostationcontrol/tangostationcontrol/devices/recv.py
@@ -196,9 +196,9 @@ class RECV(opcua_device):
     RECV_VOUT_error_R          = attribute(dtype=(bool,), max_dim_x=32)
 
     def read_RECV_IOUT_error_R(self):
-        return self.proxy.ANT_mask_RW & (
+        return (self.proxy.ANT_mask_RW & (
                  self.alarm_val("RCU_PWR_ANT_IOUT_R")
-               ).any(axis=1)
+               )).any(axis=1)
 
     def read_RECV_TEMP_error_R(self):
         # Don't apply the mask here --- we always want to know if things get too hot!
@@ -210,13 +210,13 @@ class RECV(opcua_device):
         return (self.proxy.ANT_mask_RW & (
                  self.alarm_val("RCU_PWR_ANT_VIN_R")
                | self.alarm_val("RCU_PWR_ANT_VOUT_R")
-               ).any(axis=1) | (self.proxy.RCU_mask_RW & (
+               )).any(axis=1) | (self.proxy.RCU_mask_RW & (
                  self.alarm_val("RCU_PWR_1V8_R")
                | self.alarm_val("RCU_PWR_2V5_R")
                | self.alarm_val("RCU_PWR_3V3_R")
                | ~self.proxy.RCU_PWR_DIGITAL_on_R
                | ~self.proxy.RCU_PWR_good_R
-               )))
+               ))
 
     # --------
     # overloaded functions
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
index eb9d1c5d41d917a98f9cdd8687630eae0ff9067a..a483aec43eacf571a6344ee1b1823cf62af96a92 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py
@@ -129,8 +129,6 @@ class SDP(opcua_device):
     FPGA_subband_weights_R = attribute_wrapper(comms_annotation=["FPGA_subband_weights_R"], datatype=numpy.uint32, dims=(12 * 512, 16))
     FPGA_subband_weights_RW = attribute_wrapper(comms_annotation=["FPGA_subband_weights_RW"], datatype=numpy.uint32, dims=(12 * 512, 16), access=AttrWriteType.READ_WRITE)
     FPGA_temp_R = attribute_wrapper(comms_annotation=["FPGA_temp_R"], datatype=numpy.float_, dims=(16,))
-    FPGA_weights_R = attribute_wrapper(comms_annotation=["FPGA_weights_R"], datatype=numpy.int16, dims=(12 * 488 * 2, 16))
-    FPGA_weights_RW = attribute_wrapper(comms_annotation=["FPGA_weights_RW"], datatype=numpy.int16, dims=(12 * 488 * 2, 16), access=AttrWriteType.READ_WRITE)
     FPGA_wg_amplitude_R = attribute_wrapper(comms_annotation=["FPGA_wg_amplitude_R"], datatype=numpy.float_, dims=(12, 16))
     FPGA_wg_amplitude_RW = attribute_wrapper(comms_annotation=["FPGA_wg_amplitude_RW"], datatype=numpy.float_, dims=(12, 16), access=AttrWriteType.READ_WRITE)
     FPGA_wg_enable_R = attribute_wrapper(comms_annotation=["FPGA_wg_enable_R"], datatype=numpy.bool_, dims=(12, 16))
@@ -211,11 +209,12 @@ class SDP(opcua_device):
     # ----------
     FPGA_error_R                 = attribute(dtype=(bool,), max_dim_x=16)
     FPGA_processing_error_R      = attribute(dtype=(bool,), max_dim_x=16)
+    FPGA_input_error_R           = attribute(dtype=(bool,), max_dim_x=16)
 
     def read_FPGA_error_R(self):
         return self.proxy.TR_fpga_mask_RW & (
                  self.proxy.TR_fpga_communication_error_R
-               | self.proxy.FPGA_firmware_version_R != ""
+               | (self.proxy.FPGA_firmware_version_R != "")
                | (self.proxy.FPGA_jesd204b_csr_dev_syncn_R == 0).any(axis=1)
                )
 
@@ -223,8 +222,12 @@ class SDP(opcua_device):
         return self.proxy.TR_fpga_mask_RW & (
                  ~self.proxy.FPGA_processing_enable_R
                | (self.proxy.FPGA_boot_image_R == 0)
-               | self.proxy.FPGA_wg_enable_R.any(axis=1)
-               | (self.proxy.FPGA_signal_input_rms_R == 0)
+               )
+
+    def read_FPGA_input_error_R(self):
+        return self.proxy.TR_fpga_mask_RW & (
+                 self.proxy.FPGA_wg_enable_R.any(axis=1)
+               | (self.proxy.FPGA_signal_input_rms_R == 0).any(axis=1)
                )
 
     # --------
@@ -237,7 +240,7 @@ class SDP(opcua_device):
 
         # wait for the firmware to be loaded (ignoring masked out elements)
         mask = self.proxy.TR_fpga_mask_RW
-        self.wait_attribute("FPGA_boot_image_R", lambda attr: (attr == 1) | ~mask, 10)
+        self.wait_attribute("FPGA_boot_image_R", lambda attr: ((attr == 1) | ~mask).all(), 10)
 
     # --------
     # Commands
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py
index 0e96366bcaa2758921ef4431a82e01ac653d19ad..338c5506458f0f63bf65fd33dd34f4d3e6c4122e 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics.py
@@ -13,7 +13,7 @@
 
 # PyTango imports
 from tango.server import device_property
-from tango import DeviceProxy
+from tango import DeviceProxy, DevSource
 
 # Additional import
 import asyncio
@@ -126,6 +126,7 @@ class Statistics(opcua_device):
 
         # proxy the SDP device in case we need the FPGA mask
         self.sdp_proxy = DeviceProxy("STAT/SDP/1")
+        self.sdp_proxy.set_source(DevSource.DEV)
 
     async def _connect_statistics(self):
         # map an access helper class
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py
index 05a76d9b39d0245251c1ddeb2ee67eb9f74870a9..8c88a3a638eafdd152e0ee39be5db3dfc8e1063f 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/statistics_collector.py
@@ -151,7 +151,7 @@ class XSTCollector(StatisticsCollector):
             # When the youngest data for each subband was received
             "xst_timestamps":        numpy.zeros((self.MAX_PARALLEL_SUBBANDS,), dtype=numpy.float64),
             "xst_subbands":          numpy.zeros((self.MAX_PARALLEL_SUBBANDS,), dtype=numpy.uint16),
-            "integration_intervals": numpy.zeros((self.MAX_PARALLEL_SUBBANDS,), dtype=numpy.float32),
+            "xst_integration_intervals": numpy.zeros((self.MAX_PARALLEL_SUBBANDS,), dtype=numpy.float32),
         })
 
         return defaults
@@ -234,7 +234,7 @@ class XSTCollector(StatisticsCollector):
         self.parameters["xst_timestamps"][subband_slot]              = numpy.float64(fields.timestamp().timestamp())
         self.parameters["xst_conjugated"][subband_slot, block_index] = conjugated
         self.parameters["xst_subbands"][subband_slot]                = numpy.uint16(fields.subband_index)
-        self.parameters["integration_intervals"][subband_slot]       = fields.integration_interval()
+        self.parameters["xst_integration_intervals"][subband_slot]   = fields.integration_interval()
 
     def xst_values(self, subband_indices=range(MAX_PARALLEL_SUBBANDS)):
         """ xst_blocks, but as a matrix[len(subband_indices)][MAX_INPUTS][MAX_INPUTS] of complex values.
@@ -246,7 +246,7 @@ class XSTCollector(StatisticsCollector):
         xst_blocks = self.parameters["xst_blocks"]
         xst_conjugated = self.parameters["xst_conjugated"]
 
-        for subband_index in subband_indices:
+        for matrix_idx, subband_index in enumerate(subband_indices):
             for block_index in range(self.MAX_BLOCKS):
                 # convert real/imag int to complex float values. this works as real/imag come in pairs
                 block = xst_blocks[subband_index][block_index].astype(numpy.float32).view(numpy.complex64)
@@ -263,7 +263,7 @@ class XSTCollector(StatisticsCollector):
                 first_baseline = (first_baseline[0] * self.BLOCK_LENGTH, first_baseline[1] * self.BLOCK_LENGTH)
 
                 # copy block into matrix
-                matrix[subband_index][first_baseline[0]:first_baseline[0]+self.BLOCK_LENGTH, first_baseline[1]:first_baseline[1]+self.BLOCK_LENGTH] = block
+                matrix[matrix_idx][first_baseline[0]:first_baseline[0]+self.BLOCK_LENGTH, first_baseline[1]:first_baseline[1]+self.BLOCK_LENGTH] = block
 
         return matrix
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py
index 38c9f76562530ff7735c54214b8bef3d023bbc9c..8be9cdb483ef4f21818791009a19e8fbc2b91cb9 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py
@@ -116,15 +116,15 @@ class XST(Statistics):
     # number of packets with invalid payloads
     nof_payload_errors_R    = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_payload_errors"}, dims=(XSTCollector.MAX_FPGAS,), datatype=numpy.uint64)
     # latest XSTs
-    xst_blocks_R            = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_blocks", "reshape": True}, dims=(XSTCollector.BLOCK_LENGTH * XSTCollector.BLOCK_LENGTH * XSTCollector.VALUES_PER_COMPLEX, XSTCollector.MAX_BLOCKS), datatype=numpy.int64)
+    xst_blocks_R            = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_blocks", "reshape": True}, dims=(XSTCollector.MAX_BLOCKS * XSTCollector.BLOCK_LENGTH * XSTCollector.BLOCK_LENGTH * XSTCollector.VALUES_PER_COMPLEX, XSTCollector.MAX_PARALLEL_SUBBANDS), datatype=numpy.int64)
     # whether the values in the block are conjugated and transposed
-    xst_conjugated_R        = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_conjugated", "reshape": True}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.bool_)
+    xst_conjugated_R        = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_conjugated", "reshape": True}, dims=(XSTCollector.MAX_BLOCKS, XSTCollector.MAX_PARALLEL_SUBBANDS), datatype=numpy.bool_)
     # reported timestamp for each subband in the latest XSTs
     xst_timestamp_R         = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_timestamps"}, dims=(XSTCollector.MAX_PARALLEL_SUBBANDS,), datatype=numpy.uint64)
     # which subband the XSTs describe
     xst_subbands_R          = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_subbands"}, dims=(XSTCollector.MAX_PARALLEL_SUBBANDS,), datatype=numpy.uint16)
     # integration interval for each subband in the latest XSTs
-    integration_interval_R  = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "integration_intervals"}, dims=(XSTCollector.MAX_PARALLEL_SUBBANDS,), datatype=numpy.float32)
+    xst_integration_interval_R  = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_integration_intervals"}, dims=(XSTCollector.MAX_PARALLEL_SUBBANDS,), datatype=numpy.float32)
 
     # xst_R, but as a matrix of subband x (input x input)
     xst_real_R              = attribute(max_dim_x=XSTCollector.MAX_INPUTS * XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_PARALLEL_SUBBANDS, dtype=((numpy.float32,),))
@@ -145,57 +145,57 @@ class XST(Statistics):
         return numpy.angle(self.statistics_client.collector.xst_values()).reshape(XSTCollector.MAX_PARALLEL_SUBBANDS, XSTCollector.MAX_INPUTS * XSTCollector.MAX_INPUTS)
 
     # xst_R, but as a matrix of input x input, for each specific subband index
-    xst_0_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(0))
-    xst_0_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(0))
-    xst_0_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(0))
-    xst_0_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(0))
-
-    xst_1_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(1))
-    xst_1_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(1))
-    xst_1_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(1))
-    xst_1_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(1))
-
-    xst_2_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(2))
-    xst_2_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(2))
-    xst_2_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(2))
-    xst_2_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(2))
-
-    xst_3_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(3))
-    xst_3_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(3))
-    xst_3_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(3))
-    xst_3_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(3))
-
-    xst_4_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(4))
-    xst_4_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(4))
-    xst_4_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(4))
-    xst_4_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(4))
-
-    xst_5_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(5))
-    xst_5_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(5))
-    xst_5_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(5))
-    xst_5_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(5))
-
-    xst_6_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(6))
-    xst_6_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(6))
-    xst_6_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(6))
-    xst_6_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(6))
-
-    xst_7_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real(7))
-    xst_7_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag(7))
-    xst_7_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power(7))
-    xst_7_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase(7))
+    xst_0_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(0))
+    xst_0_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(0))
+    xst_0_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(0))
+    xst_0_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(0))
+
+    xst_1_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(1))
+    xst_1_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(1))
+    xst_1_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(1))
+    xst_1_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(1))
+
+    xst_2_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(2))
+    xst_2_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(2))
+    xst_2_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(2))
+    xst_2_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(2))
+
+    xst_3_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(3))
+    xst_3_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(3))
+    xst_3_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(3))
+    xst_3_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(3))
+
+    xst_4_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(4))
+    xst_4_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(4))
+    xst_4_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(4))
+    xst_4_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(4))
+
+    xst_5_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(5))
+    xst_5_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(5))
+    xst_5_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(5))
+    xst_5_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(5))
+
+    xst_6_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(6))
+    xst_6_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(6))
+    xst_6_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(6))
+    xst_6_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(6))
+
+    xst_7_real_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_real_R(7))
+    xst_7_imag_R            = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_imag_R(7))
+    xst_7_power_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_power_R(7))
+    xst_7_phase_R           = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),), fget = lambda self: self.read_xst_N_phase_R(7))
 
     def read_xst_N_real_R(self, subband_idx):
-        return numpy.real(self.statistics_client.collector.xst_values(subband_idx)[0])
+        return numpy.real(self.statistics_client.collector.xst_values([subband_idx])[0])
 
     def read_xst_N_imag_R(self, subband_idx):
-        return numpy.imag(self.statistics_client.collector.xst_values(subband_idx)[0])
+        return numpy.imag(self.statistics_client.collector.xst_values([subband_idx])[0])
 
     def read_xst_N_power_R(self, subband_idx):
-        return numpy.abs(self.statistics_client.collector.xst_values(subband_idx)[0])
+        return numpy.abs(self.statistics_client.collector.xst_values([subband_idx])[0])
 
     def read_xst_N_phase_R(self, subband_idx):
-        return numpy.angle(self.statistics_client.collector.xst_values(subband_idx)[0])
+        return numpy.angle(self.statistics_client.collector.xst_values([subband_idx])[0])
 
     # ----------
     # Summarising Attributes
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/base.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/base.py
index db9f51eb7b82d081c5b83ebe04dc998a8e5f1506..7a2609e811432a8c772a239186e16f2f442ba7a3 100644
--- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/base.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/base.py
@@ -61,9 +61,30 @@ class AbstractTestBases:
                 self.self.assertListEqual([], self.proxy.opcua_missing_attributes_R)
 
         def test_device_on(self):
-            """Test if we can transition to on"""
+            """Test if we can transition off -> standby -> on"""
 
             self.proxy.initialise()
             self.proxy.on()
 
             self.assertEqual(DevState.ON, self.proxy.state())
+
+        def test_device_warm_boot(self):
+            """Test if we can transition off -> on using a warm boot"""
+
+            self.proxy.warm_boot()
+
+            self.assertEqual(DevState.ON, self.proxy.state())
+
+        def test_device_read_all_attributes(self):
+            """Test if we can read all of the exposed attributes in the ON state.
+            
+               This test covers the reading logic of all attributes. """
+
+            self.proxy.initialise()
+            self.proxy.on()
+
+            for attribute_name in self.proxy.get_attribute_list():
+                try:
+                    _ = self.proxy.read_attribute(attribute_name).value
+                except Exception as e:
+                    raise AssertionError(f"Could not read attribute {attribute_name} from device {self.name}") from e
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beam.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beam.py
index 21c93eab4f434350fa091523a421837513aadf9c..381678118b123793767aff753e4461b7916fe5bd 100644
--- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beam.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_beam.py
@@ -12,7 +12,6 @@ import numpy
 import datetime
 import json
 
-from tango import DevState
 from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy
 
 from .base import AbstractTestBases
@@ -36,11 +35,8 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase):
         # setup RECV
         recv_proxy = TestDeviceProxy("STAT/RECV/1")
         recv_proxy.off()
-        recv_proxy.initialise()
-        self.assertEqual(DevState.STANDBY, recv_proxy.state())
+        recv_proxy.warm_boot()
         recv_proxy.set_defaults()
-        recv_proxy.on()
-        self.assertEqual(DevState.ON, recv_proxy.state())
         return recv_proxy
     
     def test_HBAT_delays_dims(self):
@@ -48,12 +44,7 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase):
         self.setup_recv_proxy()
 
         # setup BEAM
-        self.proxy.init()
-        self.proxy.Initialise()
-        self.assertEqual(DevState.STANDBY, self.proxy.state())
-        self.proxy.set_defaults()
-        self.proxy.on()
-        self.assertEqual(DevState.ON, self.proxy.state())
+        self.proxy.warm_boot()
 
         # verify HBAT_delays method returns the correct dimensions
         HBAT_delays = self.proxy.HBAT_delays(self.pointing_direction)
@@ -64,12 +55,8 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase):
         recv_proxy = self.setup_recv_proxy()
 
         # setup BEAM
-        self.proxy.init()
-        self.proxy.Initialise()
-        self.assertEqual(DevState.STANDBY, self.proxy.state())
-        self.proxy.set_defaults()
-        self.proxy.on()
-        self.assertEqual(DevState.ON, self.proxy.state())
+        self.proxy.warm_boot()
+        self.proxy.HBAT_tracking_enabled_RW = False
 
         # Verify attribute is present (all zeros if never used before)
         HBAT_delays_r1 = numpy.array(recv_proxy.read_attribute('HBAT_BF_delay_steps_RW').value)
@@ -89,8 +76,8 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase):
         # setup RECV as well
         recv_proxy = self.setup_recv_proxy()
 
-        self.proxy.initialise()
-        self.proxy.on()
+        self.proxy.warm_boot()
+        self.proxy.HBAT_tracking_enabled_RW = False
 
         # Point to Zenith
         self.proxy.HBAT_set_pointing(numpy.array([["AZELGEO","0deg","90deg"]] * 96).flatten())
@@ -101,12 +88,39 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase):
 
         numpy.testing.assert_equal(calculated_HBAT_delay_steps, expected_HBAT_delay_steps)
 
+    def test_pointing_across_horizon(self):
+        # setup RECV as well
+        recv_proxy = self.setup_recv_proxy()
+
+        self.proxy.warm_boot()
+        self.proxy.HBAT_tracking_enabled_RW = False
+
+        # point at north on the horizon
+        self.proxy.HBAT_set_pointing(["AZELGEO","0deg","0deg"] * 96)
+
+        # obtain delays of the X polarisation of all the elements of the first tile
+        north_beam_delay_steps = recv_proxy.HBAT_BF_delay_steps_RW[0].reshape(2,4,4)[0]
+
+        # delays must differ under rotation, or our test will give a false positive
+        self.assertNotEqual(north_beam_delay_steps.tolist(), numpy.rot90(north_beam_delay_steps).tolist())
+
+        for angle in (90,180,270):
+            # point at angle degrees (90=E, 180=S, 270=W)
+            self.proxy.HBAT_set_pointing(["AZELGEO",f"{angle}deg","0deg"] * 96)
+
+            # obtain delays of the X polarisation of all the elements of the first tile
+            angled_beam_delay_steps = recv_proxy.HBAT_BF_delay_steps_RW[0].reshape(2,4,4)[0]
+
+            expected_delay_steps = numpy.rot90(north_beam_delay_steps, k=-(angle/90))
+
+            self.assertListEqual(expected_delay_steps.tolist(), angled_beam_delay_steps.tolist(), msg=f"angle={angle}")
+
     def test_delays_same_as_LOFAR_ref_pointing(self):
         # setup RECV as well
         recv_proxy = self.setup_recv_proxy()
 
-        self.proxy.initialise()
-        self.proxy.on()
+        self.proxy.warm_boot()
+        self.proxy.HBAT_tracking_enabled_RW = False
 
         # Point to LOFAR 1 ref pointing (0.929342, 0.952579, J2000)
         pointings = numpy.array([["J2000", "0.929342rad", "0.952579rad"]] * 96).flatten()
@@ -131,3 +145,20 @@ class TestDeviceBeam(AbstractTestBases.TestDeviceBase):
         expected_HBAT_delay_steps = numpy.array([24, 25, 27, 29, 17, 18, 20, 21, 10, 11, 13, 14, 3, 4, 5, 7] * 2, dtype=numpy.int64)
         numpy.testing.assert_equal(calculated_HBAT_delay_steps[0], expected_HBAT_delay_steps)
         numpy.testing.assert_equal(calculated_HBAT_delay_steps[48], expected_HBAT_delay_steps)
+
+    def test_beam_tracking(self):
+        # setup RECV as well
+        recv_proxy = self.setup_recv_proxy()
+
+        self.proxy.warm_boot()
+
+        # check if we're really tracking
+        self.assertTrue(self.proxy.HBAT_tracking_enabled_R)
+
+        # point somewhere
+        new_pointings = [("J2000",f"{tile}deg","0deg") for tile in range(96)]
+        self.proxy.HBAT_pointing_direction_RW = new_pointings
+
+        # check pointing
+        self.assertListEqual(new_pointings, list(self.proxy.HBAT_pointing_direction_R))
+
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sdp.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sdp.py
index 3a075e697c473c9800257b4796f21f7b79e5e430..570d8ce6cee1bc2f1ab833c9d93acdd11cc270f7 100644
--- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sdp.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sdp.py
@@ -19,7 +19,6 @@ class TestDeviceSDP(AbstractTestBases.TestDeviceBase):
     def test_device_sdp_read_attribute(self):
         """Test if we can read an attribute obtained over OPC-UA"""
 
-        self.proxy.initialise()
-        self.proxy.on()
+        self.proxy.warm_boot()
 
         self.assertListEqual([True]*16, list(self.proxy.TR_fpga_communication_error_R))
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sst.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sst.py
index 60675e121364b52fb692b2f8461001bfdc78b50a..2398206083533a5fcc40051b6a80e42923a38347 100644
--- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sst.py
+++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_sst.py
@@ -12,6 +12,7 @@ import sys
 import time
 
 from tango._tango import DevState
+from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy
 
 from .base import AbstractTestBases
 
@@ -22,14 +23,24 @@ class TestDeviceSST(AbstractTestBases.TestDeviceBase):
         """Intentionally recreate the device object in each test"""
         super().setUp("STAT/SST/1")
 
+    def test_device_read_all_attributes(self):
+        # We need to connect to SDP first to read some of our attributes
+        self.sdp_proxy = self.setup_sdp()
+
+        super().test_device_read_all_attributes()
+    
+    def setup_sdp(self):
+        # setup SDP, on which this device depends
+        sdp_proxy = TestDeviceProxy("STAT/SDP/1")
+        sdp_proxy.off()
+        sdp_proxy.warm_boot()
+        sdp_proxy.set_defaults()
+        return sdp_proxy
+
     def test_device_sst_send_udp(self):
         port_property = {"Statistics_Client_TCP_Port": "4998"}
         self.proxy.put_property(port_property)
-        self.proxy.initialise()
-
-        self.assertEqual(DevState.STANDBY, self.proxy.state())
-
-        self.proxy.on()
+        self.proxy.warm_boot()
 
         self.assertEqual(DevState.ON, self.proxy.state())
 
@@ -44,13 +55,7 @@ class TestDeviceSST(AbstractTestBases.TestDeviceBase):
     def test_device_sst_connect_tcp_receive(self):
         port_property = {"Statistics_Client_TCP_Port": "5101"}
         self.proxy.put_property(port_property)
-        self.proxy.initialise()
-
-        self.assertEqual(DevState.STANDBY, self.proxy.state())
-
-        self.proxy.on()
-
-        self.assertEqual(DevState.ON, self.proxy.state())
+        self.proxy.warm_boot()
 
         time.sleep(2)
 
diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_xst.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_xst.py
new file mode 100644
index 0000000000000000000000000000000000000000..071108ee57e025c30fc423d7ce2312b66d5012a7
--- /dev/null
+++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_xst.py
@@ -0,0 +1,32 @@
+
+# -*- coding: utf-8 -*-
+#
+# This file is part of the LOFAR 2.0 Station Software
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+from .base import AbstractTestBases
+
+from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy
+
+class TestDeviceSST(AbstractTestBases.TestDeviceBase):
+
+    def setUp(self):
+        """Intentionally recreate the device object in each test"""
+        super().setUp("STAT/XST/1")
+
+    def test_device_read_all_attributes(self):
+        # We need to connect to SDP first to read some of our attributes
+        self.sdp_proxy = self.setup_sdp()
+
+        super().test_device_read_all_attributes()
+    
+    def setup_sdp(self):
+        # setup SDP, on which this device depends
+        sdp_proxy = TestDeviceProxy("STAT/SDP/1")
+        sdp_proxy.off()
+        sdp_proxy.warm_boot()
+        sdp_proxy.set_defaults()
+        return sdp_proxy