diff --git a/CDB/LOFAR_ConfigDb.json b/CDB/LOFAR_ConfigDb.json
index 57b53e5eb897d740c5ee9929fc72d699de5222b9..dde83f7e6853e8dd71d3fb408825aa97fde1829b 100644
--- a/CDB/LOFAR_ConfigDb.json
+++ b/CDB/LOFAR_ConfigDb.json
@@ -816,6 +816,82 @@
                 }
             }
         },
+        "XST": {
+            "LTS": {
+                "XST": {
+                    "LTS/XST/1": {
+                        "properties": {
+                            "Statistics_Client_Port": [
+                                "5002"
+                            ],
+                            "OPC_Server_Name": [
+                                "dop36.astron.nl"
+                            ],
+                            "OPC_Server_Port": [
+                                "4840"
+                            ],
+                            "OPC_Time_Out": [
+                                "5.0"
+                            ],
+                            "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd",
+                                "6c:2b:59:97:be:dd"
+                            ],
+                            "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250",
+                                "10.99.250.250"
+                            ],
+                            "FPGA_xst_offload_hdr_udp_destination_port_RW_default": [
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002",
+                                "5002"
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "UNB2": {
             "LTS": {
                 "UNB2": {
diff --git a/CDB/integration_ConfigDb.json b/CDB/integration_ConfigDb.json
index debeae5263b85b15821ed515521223751ff9d37c..db261d4afb70413acb7057938c322c8b293d6fe9 100644
--- a/CDB/integration_ConfigDb.json
+++ b/CDB/integration_ConfigDb.json
@@ -164,6 +164,25 @@
                     }
                 }
             }
+        },
+         "UNB2": {
+            "LTS": {
+                "UNB2": {
+                    "LTS/UNB2/1": {
+                        "properties": {
+                            "OPC_Server_Name": [
+                                "despi.astron.nl"
+                            ],
+                            "OPC_Server_Port": [
+                                "4842"
+                            ],
+                            "OPC_Time_Out": [
+                                "5.0"
+                            ]
+                        }
+                    }
+                }
+            }
         }
     }
 }
diff --git a/CDB/unb2-sim-config.json b/CDB/unb2-sim-config.json
new file mode 100644
index 0000000000000000000000000000000000000000..a98fa27492e3835867b214cfd2789caf949de460
--- /dev/null
+++ b/CDB/unb2-sim-config.json
@@ -0,0 +1,23 @@
+{
+    "servers": {
+        "UNB2": {
+            "LTS": {
+                "UNB2": {
+                    "LTS/UNB2/1": {
+                        "properties": {
+                            "OPC_Server_Name": [
+                                "unb2-sim"
+                            ],
+                            "OPC_Server_Port": [
+                                "4844"
+                            ],
+                            "OPC_Time_Out": [
+                                "5.0"
+                            ]
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/devices/common/baselines.py b/devices/common/baselines.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9b0ca8038c0d881d602df37f99203d733f283fc
--- /dev/null
+++ b/devices/common/baselines.py
@@ -0,0 +1,59 @@
+"""
+  Baseline calculation functions.
+"""
+
+import math
+
+def nr_baselines(nr_inputs: int) -> int:
+    """ Return the number of baselines (unique pairs) that exist between a given number of inputs. """
+    return nr_inputs * (nr_inputs + 1) // 2
+
+"""
+
+ Baselines are ordered like:
+   0-0, 1-0, 1-1, 2-0, 2-1, 2-2, ...
+
+ if 
+   b = baseline
+   x = stat1 (major)
+   y = stat2 (minor)
+   x >= y
+ then
+   b_xy = x * (x + 1) / 2 + y
+ let
+   u := b_x0
+ then
+     u            = x * (x + 1) / 2
+     8u           = 4x^2 + 4x
+     8u + 1       = 4x^2 + 4x + 1 = (2x + 1)^2
+     sqrt(8u + 1) = 2x + 1
+                x = (sqrt(8u + 1) - 1) / 2
+
+ Let us define
+   x'(b) = (sqrt(8b + 1) - 1) / 2
+ which increases monotonically and is a continuation of y(b).
+
+ Because y simply increases by 1 when b increases enough, we
+ can just take the floor function to obtain the discrete y(b):
+   x(b) = floor(x'(b))
+        = floor(sqrt(8b + 1) - 1) / 2)
+
+"""
+
+def baseline_index(major: int, minor: int) -> int:
+    """ Provide a total ordering of baselines: give the unique array index for the baseline (major,minor),
+        with major >= minor. """
+
+    if major < minor:
+        raise ValueError(f"major < minor: {major} < {minor}. Since we do not store the conjugates this will lead to processing errors.")
+
+    return major * (major + 1) // 2 + minor
+
+def baseline_from_index(index: int) -> tuple:
+    """ Return the (major,minor) input pair given a baseline index. """
+
+    major = int((math.sqrt(float(8 * index + 1)) - 0.99999) / 2)
+    minor = index - baseline_index(major,0)
+
+    return (major,minor)
+
diff --git a/devices/devices/sdp/sst.py b/devices/devices/sdp/sst.py
index 79fb6fb272b199d3069be03825cfd395f9d18929..a7e1e8e9958214469c08efe9a6fea16c9348eaf3 100644
--- a/devices/devices/sdp/sst.py
+++ b/devices/devices/sdp/sst.py
@@ -80,15 +80,17 @@ class SST(Statistics):
     FPGA_sst_offload_weighted_subbands_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_weighted_subbands_R"], datatype=numpy.bool_, dims=(16,))
 
     # number of packets with valid payloads
-    nof_valid_payloads_R    = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_valid_payloads"}, dims=(SSTCollector.MAX_INPUTS,), datatype=numpy.uint64)
+    nof_valid_payloads_R    = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_valid_payloads"}, dims=(SSTCollector.MAX_FPGAS,), datatype=numpy.uint64)
     # number of packets with invalid payloads
-    nof_payload_errors_R    = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_payload_errors"}, dims=(SSTCollector.MAX_INPUTS,), datatype=numpy.uint64)
+    nof_payload_errors_R    = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_payload_errors"}, dims=(SSTCollector.MAX_FPGAS,), datatype=numpy.uint64)
     # latest SSTs
     sst_R                   = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "sst_values"}, dims=(SSTCollector.MAX_SUBBANDS, SSTCollector.MAX_INPUTS), datatype=numpy.uint64)
     # reported timestamp for each row in the latest SSTs
     sst_timestamp_R         = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "sst_timestamps"}, dims=(SSTCollector.MAX_INPUTS,), datatype=numpy.uint64)
     # integration interval for each row in the latest SSTs
     integration_interval_R  = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "integration_intervals"}, dims=(SSTCollector.MAX_INPUTS,), datatype=numpy.float32)
+    # whether the subband data was calibrated by the SDP (that is, were subband weights applied)
+    subbands_calibrated_R   = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "subbands_calibrated"}, dims=(SSTCollector.MAX_INPUTS,), datatype=numpy.bool_)
 
     # --------
     # Overloaded functions
diff --git a/devices/devices/sdp/statistics_collector.py b/devices/devices/sdp/statistics_collector.py
index d19ad01b2a10096ebc637b5a24d51917317afc9f..1bd8f3c12135a818526c48ecbff80408f290b7c9 100644
--- a/devices/devices/sdp/statistics_collector.py
+++ b/devices/devices/sdp/statistics_collector.py
@@ -3,7 +3,8 @@ from threading import Thread
 import logging
 import numpy
 
-from .statistics_packet import SSTPacket
+from .statistics_packet import SSTPacket, XSTPacket
+from common.baselines import nr_baselines, baseline_index, baseline_from_index
 from clients.statistics_client_thread import StatisticsClientThread
 
 logger = logging.getLogger()
@@ -11,11 +12,8 @@ logger = logging.getLogger()
 class StatisticsCollector:
     """ Base class to process statistics packets into parameters matrices. """
 
-    # Maximum number of antenna inputs we support (used to determine array sizes)
-    MAX_INPUTS = 192
-
-    # Maximum number of subbands we support (used to determine array sizes)
-    MAX_SUBBANDS = 512
+    # Maximum number of FPGAs we receive data from (used for diagnostics)
+    MAX_FPGAS = 16
 
     def __init__(self):
         self.parameters = self._default_parameters()
@@ -62,15 +60,16 @@ class SSTCollector(StatisticsCollector):
 
         defaults.update({
             # Number of packets received so far that we could parse correctly and do not have a payload error
-            "nof_valid_payloads":    numpy.zeros((self.MAX_INPUTS,), dtype=numpy.uint64),
+            "nof_valid_payloads":    numpy.zeros((self.MAX_FPGAS,), dtype=numpy.uint64),
 
             # Packets that reported a payload error
-            "nof_payload_errors":    numpy.zeros((self.MAX_INPUTS,), dtype=numpy.uint64),
+            "nof_payload_errors":    numpy.zeros((self.MAX_FPGAS,), dtype=numpy.uint64),
 
             # Last value array we've constructed out of the packets
             "sst_values":            numpy.zeros((self.MAX_INPUTS, self.MAX_SUBBANDS), dtype=numpy.uint64),
             "sst_timestamps":        numpy.zeros((self.MAX_INPUTS,), dtype=numpy.float64),
             "integration_intervals": numpy.zeros((self.MAX_INPUTS,), dtype=numpy.float32),
+            "subbands_calibrated":   numpy.zeros((self.MAX_INPUTS,), dtype=numpy.bool_),
         })
 
         return defaults
@@ -87,16 +86,120 @@ class SSTCollector(StatisticsCollector):
 
         if fields.payload_error:
             # cannot trust the data if a payload error is reported
-            self.parameters["nof_payload_errors"][input_index] += numpy.uint64(1)
+            self.parameters["nof_payload_errors"][fields.gn_index] += numpy.uint64(1)
 
             # don't raise, as packet is valid
             return
 
         # process the packet
-        self.parameters["nof_valid_payloads"][input_index]    += numpy.uint64(1)
+        self.parameters["nof_valid_payloads"][fields.gn_index]    += numpy.uint64(1)
         self.parameters["sst_values"][input_index][:fields.nof_statistics_per_packet] = fields.payload
         self.parameters["sst_timestamps"][input_index]        = numpy.float64(fields.timestamp().timestamp())
         self.parameters["integration_intervals"][input_index] = fields.integration_interval()
+        self.parameters["subbands_calibrated"][input_index]   = fields.subband_calibrated_flag
+
+class XSTCollector(StatisticsCollector):
+    """ Class to process XST statistics packets. """
+
+    # Maximum number of antenna inputs we support (used to determine array sizes)
+    MAX_INPUTS = 192
+
+    # Maximum number of baselines we can receive
+    MAX_BASELINES = nr_baselines(MAX_INPUTS)
+
+    # Expected block size is BLOCK_LENGTH x BLOCK_LENGTH
+    BLOCK_LENGTH = 12
+
+    # Expected number of blocks: enough to cover all baselines without the conjugates (that is, the top-left triangle of the matrix).
+    MAX_BLOCKS = nr_baselines(MAX_INPUTS // BLOCK_LENGTH)
+
+    # Maximum number of subbands we support (used to determine array sizes)
+    MAX_SUBBANDS = 512
+
+    # Complex values are (real, imag). A bit silly, but we don't want magical constants.
+    VALUES_PER_COMPLEX = 2
+
+    def _default_parameters(self):
+        defaults = super()._default_parameters()
+
+        defaults.update({
+            # Number of packets received so far that we could parse correctly and do not have a payload error
+            "nof_valid_payloads":    numpy.zeros((self.MAX_FPGAS,), dtype=numpy.uint64),
+
+            # Packets that reported a payload error
+            "nof_payload_errors":    numpy.zeros((self.MAX_FPGAS,), dtype=numpy.uint64),
+
+            # Last value array we've constructed out of the packets
+            "xst_blocks":            numpy.zeros((self.MAX_BLOCKS, self.BLOCK_LENGTH * self.BLOCK_LENGTH * self.VALUES_PER_COMPLEX), dtype=numpy.int64),
+            "xst_timestamps":        numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.float64),
+            "xst_subbands":          numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.uint16),
+            "integration_intervals": numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.float32),
+        })
+
+        return defaults
+
+    def parse_packet(self, packet):
+        fields = XSTPacket(packet)
+
+        if fields.payload_error:
+            # cannot trust the data if a payload error is reported
+            self.parameters["nof_payload_errors"][fields.gn_index] += numpy.uint64(1)
+
+            # don't raise, as packet is valid
+            return
+
+        # the blocks must be of size BLOCK_LENGTH x BLOCK_LENGTH
+        if fields.nof_signal_inputs != self.BLOCK_LENGTH:
+            raise ValueError("Packet describes a block of {0} x {0} baselines, but we can only parse blocks of {1} x {1} baselines".format(fields.nof_signal_inputs, self.BLOCK_LENGTH))
+
+        # check whether set of baselines in this packet are not out of bounds
+        for antenna in (0,1):
+            if fields.first_baseline[antenna] + fields.nof_signal_inputs >= self.MAX_INPUTS:
+                # packet describes an input that is out of bounds for us
+                raise ValueError("Packet describes {0} x {0} baselines starting at {1}, but we are limited to describing MAX_INPUTS={2}".format(fields.nof_signal_inputs, fields.first_baseline, self.MAX_INPUTS))
+
+            # the blocks of baselines need to be tightly packed, and thus be provided at exact intervals
+            if fields.first_baseline[antenna] % self.BLOCK_LENGTH != 0:
+                raise ValueError("Packet describes baselines starting at %s, but we require a multiple of BLOCK_LENGTH=%d" % (fields.first_baseline, self.MAX_INPUTS))
+
+        # the payload contains complex values for the block of baselines of size BLOCK_LENGTH x BLOCK_LENGTH
+        # starting at baseline first_baseline.
+        #
+        # we honour this format, as we want to keep the metadata together with these blocks. we do need to put the blocks in a linear
+        # and tight order, however, so we calculate a block index.
+        block_index = baseline_index(fields.first_baseline[0] // self.BLOCK_LENGTH, fields.first_baseline[1] // self.BLOCK_LENGTH)
+
+        # process the packet
+        self.parameters["nof_valid_payloads"][fields.gn_index] += numpy.uint64(1)
+
+        block_index = baseline_index(fields.first_baseline[0], fields.first_baseline[1])
+
+        self.parameters["xst_blocks"][block_index][:fields.nof_statistics_per_packet] = fields.payload
+        self.parameters["xst_timestamps"][block_index]        = numpy.float64(fields.timestamp().timestamp())
+        self.parameters["xst_subbands"][block_index]          = numpy.uint16(fields.subband_index)
+        self.parameters["integration_intervals"][block_index] = fields.integration_interval()
+
+    def xst_values(self):
+        """ xst_blocks, but as a matrix[MAX_INPUTS][MAX_INPUTS] of complex values. """
+
+        matrix = numpy.zeros((self.MAX_INPUTS, self.MAX_INPUTS), dtype=numpy.complex64)
+        xst_blocks = self.parameters["xst_blocks"]
+
+        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[block_index].astype(numpy.float32).view(numpy.complex64)
+
+            # reshape into [a][b]
+            block = block.reshape(self.BLOCK_LENGTH, self.BLOCK_LENGTH)
+
+            # compute destination in matrix
+            first_baseline = baseline_from_index(block_index)
+            first_baseline = (first_baseline[0] * self.BLOCK_LENGTH, first_baseline[1] * self.BLOCK_LENGTH)
+
+            # copy block into matrix
+            matrix[first_baseline[0]:first_baseline[0]+self.BLOCK_LENGTH, first_baseline[1]:first_baseline[1]+self.BLOCK_LENGTH] = block
+
+        return matrix
 
 
 class StatisticsConsumer(Thread, StatisticsClientThread):
@@ -159,4 +262,3 @@ class StatisticsConsumer(Thread, StatisticsClientThread):
         if self.is_alive():
             # there is nothing we can do except wait (stall) longer, which could be indefinitely.
             logger.error(f"Statistics thread did not shut down after {self.DISCONNECT_TIMEOUT} seconds, just leaving it dangling. Please attach a debugger to thread ID {self.ident}.")
-
diff --git a/devices/devices/sdp/statistics_packet.py b/devices/devices/sdp/statistics_packet.py
index 6843c99e62c79b2c9afa119aaf0b3b51709269f7..9bac227071dfbdec9ea0b0fd1fa63fa36176a8d9 100644
--- a/devices/devices/sdp/statistics_packet.py
+++ b/devices/devices/sdp/statistics_packet.py
@@ -117,9 +117,9 @@ class StatisticsPacket(object):
         self.nyquist_zone_index = get_bit_value(self.source_info, 13, 14)
         self.t_adc = get_bit_value(self.source_info, 12)
         self.fsub_type = get_bit_value(self.source_info, 11)
-        self.payload_error = get_bit_value(self.source_info, 10)
-        self.beam_repositioning_flag = get_bit_value(self.source_info, 9)
-        self.subband_calibrated_flag = get_bit_value(self.source_info, 8)
+        self.payload_error = (get_bit_value(self.source_info, 10) != 0)
+        self.beam_repositioning_flag = (get_bit_value(self.source_info, 9) != 0)
+        self.subband_calibrated_flag = (get_bit_value(self.source_info, 8) != 0)
         # self.source_info 5-7 are reserved
         self.gn_index = get_bit_value(self.source_info, 0, 4)
 
@@ -210,6 +210,17 @@ class StatisticsPacket(object):
 
         return header
 
+    def payload(self, signed=False) -> numpy.array:
+        """ The payload of this packet, as a linear array. """
+
+        # derive which and how many elements to read from the packet header
+        bytecount_to_unsigned_struct_type = {1: 'b', 2: 'h', 4: 'i', 8: 'q'} if signed else {1: 'B', 2: 'H', 4: 'I', 8: 'Q'}
+        format_str = ">{}{}".format(self.nof_statistics_per_packet,
+                                    bytecount_to_unsigned_struct_type[self.nof_bytes_per_statistic])
+
+        return numpy.array(
+            struct.unpack(format_str, self.packet[self.header_size:self.header_size + struct.calcsize(format_str)]))
+
 
 class SSTPacket(StatisticsPacket):
     """
@@ -245,16 +256,8 @@ class SSTPacket(StatisticsPacket):
         return header
 
     @property
-    def payload(self) -> numpy.array:
-        """ The payload of this packet, interpreted as SST data. """
-
-        # derive which and how many elements to read from the packet header
-        bytecount_to_unsigned_struct_type = {1: 'B', 2: 'H', 4: 'I', 8: 'Q'}
-        format_str = ">{}{}".format(self.nof_statistics_per_packet,
-                                    bytecount_to_unsigned_struct_type[self.nof_bytes_per_statistic])
-
-        return numpy.array(
-            struct.unpack(format_str, self.packet[self.header_size:self.header_size + struct.calcsize(format_str)]))
+    def payload(self):
+        return super().payload(signed=False)
 
 
 class XSTPacket(StatisticsPacket):
@@ -263,9 +266,10 @@ class XSTPacket(StatisticsPacket):
 
        The following fields are exposed as properties & functions.
 
-
        subband_index:                      subband number for which this packet contains statistics.
-       baseline:                           antenna pair for which this packet contains statistics.
+       first_baseline:                     first antenna pair for which this packet contains statistics.
+
+       payload[nof_signal_inputs][nof_signal_inputs] the baselines, starting from first_baseline
     """
 
     def __init__(self, packet):
@@ -281,16 +285,20 @@ class XSTPacket(StatisticsPacket):
         super().unpack_data_id()
 
         self.subband_index = get_bit_value(self.data_id, 16, 24)
-        self.baseline = (get_bit_value(self.data_id, 8, 15), get_bit_value(self.data_id, 0, 7))
+        self.first_baseline = (get_bit_value(self.data_id, 8, 15), get_bit_value(self.data_id, 0, 7))
 
     def header(self):
         header = super().header()
 
         header["data_id"]["subband_index"] = self.subband_index
-        header["data_id"]["baseline"] = self.baseline
+        header["data_id"]["first_baseline"] = self.first_baseline
 
         return header
 
+    @property
+    def payload(self):
+        return super().payload(signed=True)
+
 
 class BSTPacket(StatisticsPacket):
     """
diff --git a/devices/devices/sdp/xst.py b/devices/devices/sdp/xst.py
new file mode 100644
index 0000000000000000000000000000000000000000..104936e107d6e86cf1ce7fe391895168697b8314
--- /dev/null
+++ b/devices/devices/sdp/xst.py
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of the XST project
+#
+#
+#
+# Distributed under the terms of the APACHE license.
+# See LICENSE.txt for more info.
+
+""" XST Device Server for LOFAR2.0
+
+"""
+
+# TODO(Corne): Remove sys.path.append hack once packaging is in place!
+import os, sys
+currentdir = os.path.dirname(os.path.realpath(__file__))
+parentdir = os.path.dirname(currentdir)
+parentdir = os.path.dirname(parentdir)
+sys.path.append(parentdir)
+
+# PyTango imports
+from tango.server import run
+from tango.server import device_property, attribute
+from tango import AttrWriteType
+# Additional import
+
+from clients.attribute_wrapper import attribute_wrapper
+from clients.opcua_client import OPCUAConnection
+from clients.statistics_client import StatisticsClient
+
+from devices.hardware_device import hardware_device
+
+from common.lofar_git import get_version
+from common.lofar_logging import device_logging_to_python, log_exceptions
+
+from devices.sdp.statistics import Statistics
+from devices.sdp.statistics_collector import XSTCollector
+
+import numpy
+
+__all__ = ["XST", "main"]
+
+class XST(Statistics):
+
+    STATISTICS_COLLECTOR_CLASS = XSTCollector
+
+    # -----------------
+    # Device Properties
+    # -----------------
+
+    FPGA_xst_offload_hdr_eth_destination_mac_RW_default = device_property(
+        dtype='DevVarStringArray',
+        mandatory=True
+    )
+
+    FPGA_xst_offload_hdr_ip_destination_address_RW_default = device_property(
+        dtype='DevVarStringArray',
+        mandatory=True
+    )
+
+    FPGA_xst_offload_hdr_udp_destination_port_RW_default = device_property(
+        dtype='DevVarUShortArray',
+        mandatory=True
+    )
+
+    FPGA_xst_processing_enable_RW_default = device_property(
+        dtype='DevVarBooleanArray',
+        mandatory=False,
+        default_value=[True] * 16
+    )
+
+    FPGA_xst_subband_select_RW_default = device_property(
+        dtype='DevVarULongArray',
+        mandatory=False,
+        default_value=[[0,102,0,0,0,0,0,0]] * 16
+    )
+
+    # ----------
+    # Attributes
+    # ----------
+
+    # FPGA control points for XSTs
+    FPGA_xst_integration_interval_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_RW"], datatype=numpy.double, dims=(8,16), access=AttrWriteType.READ_WRITE)
+    FPGA_xst_integration_interval_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_R"], datatype=numpy.double, dims=(8,16))
+    FPGA_xst_offload_enable_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE)
+    FPGA_xst_offload_enable_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_enable_R"], datatype=numpy.bool_, dims=(16,))
+    FPGA_xst_offload_hdr_eth_destination_mac_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_eth_destination_mac_RW"], datatype=numpy.str_, dims=(16,), access=AttrWriteType.READ_WRITE)
+    FPGA_xst_offload_hdr_eth_destination_mac_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_eth_destination_mac_R"], datatype=numpy.str_, dims=(16,))
+    FPGA_xst_offload_hdr_ip_destination_address_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_ip_destination_address_RW"], datatype=numpy.str_, dims=(16,), access=AttrWriteType.READ_WRITE)
+    FPGA_xst_offload_hdr_ip_destination_address_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_ip_destination_address_R"], datatype=numpy.str_, dims=(16,))
+    FPGA_xst_offload_hdr_udp_destination_port_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_udp_destination_port_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE)
+    FPGA_xst_offload_hdr_udp_destination_port_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_udp_destination_port_R"], datatype=numpy.uint16, dims=(16,))
+    FPGA_xst_processing_enable_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_processing_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE)
+    FPGA_xst_processing_enable_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_processing_enable_R"], datatype=numpy.bool_, dims=(16,))
+    FPGA_xst_subband_select_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_subband_select_RW"], datatype=numpy.uint32, dims=(8,16), access=AttrWriteType.READ_WRITE)
+    FPGA_xst_subband_select_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_subband_select_R"], datatype=numpy.uint32, dims=(8,16))
+
+    # number of packets with valid payloads
+    nof_valid_payloads_R    = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_valid_payloads"}, dims=(XSTCollector.MAX_FPGAS,), datatype=numpy.uint64)
+    # 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"}, dims=(XSTCollector.BLOCK_LENGTH * XSTCollector.BLOCK_LENGTH * XSTCollector.VALUES_PER_COMPLEX, XSTCollector.MAX_BLOCKS), datatype=numpy.int64)
+    # reported timestamp for each row in the latest XSTs
+    xst_timestamp_R         = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_timestamps"}, dims=(XSTCollector.MAX_BLOCKS,), 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_BLOCKS,), datatype=numpy.uint16)
+    # integration interval for each row in the latest XSTs
+    integration_interval_R  = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "integration_intervals"}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.float32)
+
+    # xst_R, but as a matrix of input x input
+    xst_real_R              = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),))
+    xst_imag_R              = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),))
+    xst_power_R             = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),))
+    xst_phase_R             = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),))
+
+    def read_xst_real_R(self):
+        return numpy.real(self.statistics_client.collector.xst_values())
+
+    def read_xst_imag_R(self):
+        return numpy.imag(self.statistics_client.collector.xst_values())
+
+    def read_xst_power_R(self):
+        return numpy.abs(self.statistics_client.collector.xst_values())
+
+    def read_xst_phase_R(self):
+        return numpy.angle(self.statistics_client.collector.xst_values())
+
+    # --------
+    # Overloaded functions
+    # --------
+
+    # --------
+    # Commands
+    # --------
+
+# ----------
+# Run server
+# ----------
+def main(args=None, **kwargs):
+    """Main function of the XST Device module."""
+
+    from common.lofar_logging import configure_logger
+    configure_logger()
+
+    return run((XST,), args=args, **kwargs)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/devices/integration_test/devices/test_device_unb2.py b/devices/integration_test/devices/test_device_unb2.py
new file mode 100644
index 0000000000000000000000000000000000000000..97f31ab6ee162f8183db963e92f4f03b9ee7f617
--- /dev/null
+++ b/devices/integration_test/devices/test_device_unb2.py
@@ -0,0 +1,59 @@
+# -*- 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.
+
+import time
+
+from tango import DeviceProxy
+from tango._tango import DevState
+
+from integration_test import base
+
+
+class TestDeviceUNB2(base.IntegrationTestCase):
+
+    def setUp(self):
+        """Intentionally recreate the device object in each test"""
+        super(TestDeviceUNB2, self).setUp()
+
+    def tearDown(self):
+        """Turn device Off in teardown to prevent blocking tests"""
+        d = DeviceProxy("LTS/UNB2/1")
+
+        try:
+            d.Off()
+        except Exception as e:
+            """Failing to turn Off devices should not raise errors here"""
+            print(f"Failed to turn device off in teardown {e}")
+
+    def test_device_proxy_unb2(self):
+        """Test if we can successfully create a DeviceProxy and fetch state"""
+
+        d = DeviceProxy("LTS/UNB2/1")
+
+        self.assertEqual(DevState.OFF, d.state())
+
+    def test_device_unb2_initialize(self):
+        """Test if we can transition to standby"""
+
+        d = DeviceProxy("LTS/UNB2/1")
+
+        d.initialise()
+
+        self.assertEqual(DevState.STANDBY, d.state())
+
+    def test_device_unb2_on(self):
+        """Test if we can transition to on"""
+
+        d = DeviceProxy("LTS/UNB2/1")
+
+        d.initialise()
+
+        d.on()
+
+        self.assertEqual(DevState.ON, d.state())
diff --git a/devices/test/common/test_baselines.py b/devices/test/common/test_baselines.py
new file mode 100644
index 0000000000000000000000000000000000000000..206b4ca0eccefe1012519c8236d158e52f1cdc38
--- /dev/null
+++ b/devices/test/common/test_baselines.py
@@ -0,0 +1,30 @@
+# -*- 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 common import baselines
+
+from test import base
+
+
+class TestBaselines(base.TestCase):
+    def test_nr_baselines(self):
+        # no baselines if no antennas
+        self.assertEqual(0, baselines.nr_baselines(0))
+        # one antenna only has autocorrelation
+        self.assertEqual(1, baselines.nr_baselines(1))
+        # two antennas each have autocorrelations + a baseline between each other
+        self.assertEqual(3, baselines.nr_baselines(2))
+
+    def test_baseline_indices(self):
+        """ Test whether baseline_from_index and baseline_index line up. """
+
+        for major in range(192):
+            for minor in range(major + 1):
+                idx = baselines.baseline_index(major, minor)
+                self.assertEqual((major, minor), baselines.baseline_from_index(idx), msg=f'baseline_index({major},{minor}) resulted in {idx}, and should match baseline_from_index({idx})')
diff --git a/devices/test/devices/test_statistics_collector.py b/devices/test/devices/test_statistics_collector.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3568b8e56452259b8754be3a76e862a20845fcb
--- /dev/null
+++ b/devices/test/devices/test_statistics_collector.py
@@ -0,0 +1,80 @@
+from devices.sdp.statistics_collector import XSTCollector
+from devices.sdp.statistics_packet import XSTPacket
+
+from test import base
+
+class TestXSTCollector(base.TestCase):
+    def test_valid_packet(self):
+        collector = XSTCollector()
+
+        # a valid packet as obtained from SDP, with 64-bit BE 1+1j as payload
+        packet =  b'X\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01'
+
+        # parse it ourselves to extract info nicely
+        fields = XSTPacket(packet)
+        fpga_index = fields.gn_index
+
+        # this should not throw
+        collector.process_packet(packet)
+
+        # counters should now be updated
+        self.assertEqual(1, collector.parameters["nof_packets"])
+        self.assertEqual(0, collector.parameters["nof_invalid_packets"])
+
+        self.assertEqual(1, collector.parameters["nof_valid_payloads"][fpga_index])
+        self.assertEqual(0, collector.parameters["nof_payload_errors"][fpga_index])
+
+        # check whether the data ended up in the right block, and the rest is still zero
+        xst_values = collector.xst_values()
+
+        for baseline_a in range(collector.MAX_INPUTS):
+            for baseline_b in range(collector.MAX_INPUTS):
+                if baseline_b > baseline_a:
+                    # only scan top-left triangle
+                    continue
+
+                baseline_a_was_in_packet = (fields.first_baseline[0] <= baseline_a < fields.first_baseline[0] + fields.nof_signal_inputs)
+                baseline_b_was_in_packet = (fields.first_baseline[1] <= baseline_b < fields.first_baseline[1] + fields.nof_signal_inputs)
+
+                if baseline_a_was_in_packet and baseline_b_was_in_packet:
+                    self.assertEqual(1+1j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] did not end up in XST matrix.')
+                else:
+                    self.assertEqual(0+0j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] was not in packet, but was written to the XST matrix.')
+
+    def test_invalid_packet(self):
+        collector = XSTCollector()
+
+        # an invalid packet
+        packet =  b'S\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01'
+
+        # this should throw
+        with self.assertRaises(ValueError):
+            collector.process_packet(packet)
+
+        # counters should now be updated
+        self.assertEqual(1, collector.parameters["nof_packets"])
+        self.assertEqual(1, collector.parameters["nof_invalid_packets"])
+
+        self.assertListEqual([0] * collector.MAX_FPGAS, list(collector.parameters["nof_valid_payloads"]))
+        self.assertListEqual([0] * collector.MAX_FPGAS, list(collector.parameters["nof_payload_errors"]))
+
+    def test_payload_error(self):
+        collector = XSTCollector()
+
+        # an valid packet with a payload error
+        packet =  b'X\x05\x00\x00\x00\x00\x00\x00\x14\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01'
+
+        # parse it ourselves to extract info nicely
+        fields = XSTPacket(packet)
+        fpga_index = fields.gn_index
+
+        # this should not throw
+        collector.process_packet(packet)
+
+        # counters should now be updated
+        self.assertEqual(1, collector.parameters["nof_packets"])
+        self.assertEqual(0, collector.parameters["nof_invalid_packets"])
+
+        self.assertEqual(0, collector.parameters["nof_valid_payloads"][fpga_index])
+        self.assertEqual(1, collector.parameters["nof_payload_errors"][fpga_index])
+
diff --git a/docker-compose/Makefile b/docker-compose/Makefile
index 686f88f9e4887039cbe5206a5a88ecb8df9aed8c..40479deef5c00a426346b287fab30f1f8adc7e94 100644
--- a/docker-compose/Makefile
+++ b/docker-compose/Makefile
@@ -132,13 +132,13 @@ pull: ## pull the images from the Docker hub
 
 build: ## rebuild images
 	# docker-compose does not support build dependencies, so manage those here
-	$(DOCKER_COMPOSE_ARGS) docker-compose -f lofar-device-base.yml -f networks.yml build
-	$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build $(SERVICE)
+	$(DOCKER_COMPOSE_ARGS) docker-compose -f lofar-device-base.yml -f networks.yml build --progress=plain
+	$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build --progress=plain $(SERVICE)
 
 build-nocache: ## rebuild images from scratch
 	# docker-compose does not support build dependencies, so manage those here
-	$(DOCKER_COMPOSE_ARGS) docker-compose -f lofar-device-base.yml -f networks.yml build
-	$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build --no-cache $(SERVICE)
+	$(DOCKER_COMPOSE_ARGS) docker-compose -f lofar-device-base.yml -f networks.yml build --progress=plain
+	$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build --no-cache --progress=plain $(SERVICE)
 
 up: minimal  ## start the base TANGO system and prepare all services
 	$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) up --no-start
diff --git a/docker-compose/device-xst.yml b/docker-compose/device-xst.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7e57a2f9ab507f193710787aac187c67e1d738fe
--- /dev/null
+++ b/docker-compose/device-xst.yml
@@ -0,0 +1,44 @@
+#
+# Docker compose file that launches an interactive iTango session.
+#
+# Connect to the interactive session with 'docker attach itango'.
+# Disconnect with the Docker deattach sequence: <CTRL>+<P> <CTRL>+<Q>
+#
+# Defines:
+#   - itango: iTango interactive session
+#
+# Requires:
+#   - lofar-device-base.yml
+#
+version: '2'
+
+services:
+  device-xst:
+    image: device-xst
+    # build explicitly, as docker-compose does not understand a local image
+    # being shared among services.
+    build:
+        context: lofar-device-base
+        args:
+            SOURCE_IMAGE: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}-tango-itango:${TANGO_ITANGO_VERSION}
+    container_name: ${CONTAINER_NAME_PREFIX}device-xst
+    networks:
+        - control
+        - data
+    ports:
+        - "5002:5002/udp" # port to receive XST UDP packets on
+        - "5706:5706" # unique port for this DS
+    volumes:
+        - ${TANGO_LOFAR_CONTAINER_MOUNT}
+    environment:
+      - TANGO_HOST=${TANGO_HOST}
+    entrypoint:
+      - /usr/local/bin/wait-for-it.sh
+      - ${TANGO_HOST}
+      - --timeout=30
+      - --strict
+      - --
+      # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA
+      # can't know about our Docker port forwarding
+      - python3 -u ${TANGO_LOFAR_CONTAINER_DIR}/devices/devices/sdp/xst.py LTS -v -ORBendPoint giop:tcp:0:5706 -ORBendPointPublish giop:tcp:${HOSTNAME}:5706
+    restart: on-failure
diff --git a/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py b/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
index 504cdd2736714aef4a4744e51c98e17d8ba630c7..df75d5962a1327041995aa04c41d6d1e1c2ae914 100644
--- a/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
+++ b/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py
@@ -2,7 +2,8 @@
 recv = DeviceProxy("LTS/RECV/1")
 sdp = DeviceProxy("LTS/SDP/1")
 sst = DeviceProxy("LTS/SST/1")
+xst = DeviceProxy("LTS/XST/1")
 unb2 = DeviceProxy("LTS/UNB2/1")
 
 # Put them in a list in case one wants to iterate
-devices = [recv, sdp, sst, unb2]
+devices = [recv, sdp, sst, xst, unb2]
diff --git a/docker-compose/unb2-sim.yml b/docker-compose/unb2-sim.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e031e20f54ad6addec1fdbabf972661d6f4c8f9a
--- /dev/null
+++ b/docker-compose/unb2-sim.yml
@@ -0,0 +1,20 @@
+#
+# Docker compose file that launches a UNB2 simulator
+#
+# Defines:
+#   - unb2-sim
+#
+version: '2'
+
+services:
+  unb2-sim:
+    build:
+        context: unb2-sim
+    container_name: ${CONTAINER_NAME_PREFIX}unb2-sim
+    networks:
+      - control
+    volumes:
+        - ${HOME}:/hosthome
+    ports:
+        - "4844:4844"
+    restart: on-failure
diff --git a/docker-compose/unb2-sim/Dockerfile b/docker-compose/unb2-sim/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..cbb4ce80185ee1cf0a84aabc457a7983e57b5651
--- /dev/null
+++ b/docker-compose/unb2-sim/Dockerfile
@@ -0,0 +1,10 @@
+FROM ubuntu:20.04
+
+COPY requirements.txt /requirements.txt
+
+RUN apt-get update && apt-get install -y python3 python3-pip python3-yaml git && \
+    pip3 install -r requirements.txt && \
+    git clone --depth 1 --branch master https://git.astron.nl/lofar2.0/pypcc
+
+WORKDIR /pypcc
+CMD ["python3","pypcc2.py","--simulator", "--port=4844", "--config=UNB2"]
diff --git a/docker-compose/unb2-sim/requirements.txt b/docker-compose/unb2-sim/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5badf0e57069366adedc43ca2c2b59dc7dc4b35d
--- /dev/null
+++ b/docker-compose/unb2-sim/requirements.txt
@@ -0,0 +1,4 @@
+#check if this file is necessary
+opcua
+numpy
+recordclass>=0.16,<0.16.1
\ No newline at end of file
diff --git a/jupyter-notebooks/UNB2_notebook.ipynb b/jupyter-notebooks/UNB2_notebook.ipynb
index 3e87179f3a0f0ef951da6a078ba5df3610a6696d..e140631ffc4ea0cee80e2374ec6b5f1289dbba24 100644
--- a/jupyter-notebooks/UNB2_notebook.ipynb
+++ b/jupyter-notebooks/UNB2_notebook.ipynb
@@ -2,7 +2,7 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": 25,
    "id": "waiting-chance",
    "metadata": {},
    "outputs": [],
@@ -12,7 +12,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 26,
    "id": "moving-alexandria",
    "metadata": {},
    "outputs": [],
@@ -22,7 +22,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 28,
    "id": "ranking-aluminum",
    "metadata": {
     "scrolled": false
@@ -55,7 +55,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 29,
    "id": "0caa8146",
    "metadata": {},
    "outputs": [
@@ -63,68 +63,68 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "version_R *L2SS-268-LR1_2_Read_hardware_status_of_UB2c_from_SDPHW [1007a5c5462b1aa3e8f81268f890f6c058413218]\n",
+      "version_R *L2SS-268-LR1_2_Read_hardware_status_of_UB2c_from_SDPHW [b2db449162be8e52013dbbd1a44d6d90a12491b5]\n",
       "UNB2_Power_ON_OFF_RW [False False]\n",
       "UNB2_Front_Panel_LED_RW [0 0]\n",
       "UNB2_Front_Panel_LED_R [0 0]\n",
       "UNB2_mask_RW [False False]\n",
       "UNB2_I2C_bus_STATUS_R [False False]\n",
-      "UNB2_EEPROM_Unique_ID_R [5947666 5947666]\n",
-      "UNB2_DC_DC_48V_12V_VIN_R [29.18505859 29.18505859]\n",
-      "UNB2_DC_DC_48V_12V_VOUT_R [12.00146484 11.98486328]\n",
-      "UNB2_DC_DC_48V_12V_IOUT_R [3.625 3.625]\n",
-      "UNB2_DC_DC_48V_12V_TEMP_R [37. 37.]\n",
-      "UNB2_POL_QSFP_N01_VOUT_R [3.28686523 3.28686523]\n",
-      "UNB2_POL_QSFP_N01_IOUT_R [1.55078125 1.55078125]\n",
-      "UNB2_POL_QSFP_N01_TEMP_R [33.75 33.75]\n",
-      "UNB2_POL_QSFP_N23_VOUT_R [3.28710938 3.28710938]\n",
-      "UNB2_POL_QSFP_N23_IOUT_R [1.25195312 1.25195312]\n",
-      "UNB2_POL_QSFP_N23_TEMP_R [40.625 40.625]\n",
-      "UNB2_POL_SWITCH_1V2_VOUT_R [1.19970703 1.19970703]\n",
-      "UNB2_POL_SWITCH_1V2_IOUT_R [1.73632812 1.73632812]\n",
-      "UNB2_POL_SWITCH_1V2_TEMP_R [45.125 45.125]\n",
-      "UNB2_POL_SWITCH_PHY_VOUT_R [1.00024414 1.00024414]\n",
-      "UNB2_POL_SWITCH_PHY_IOUT_R [0.52050781 0.52050781]\n",
-      "UNB2_POL_SWITCH_PHY_TEMP_R [46.1875 46.1875]\n",
-      "UNB2_POL_CLOCK_VOUT_R [2.49951172 2.49951172]\n",
-      "UNB2_POL_CLOCK_IOUT_R [0.94042969 0.94042969]\n",
-      "UNB2_POL_CLOCK_TEMP_R [42.875 42.875]\n",
-      "UNB2_FPGA_DDR4_SLOT_TEMP_R [[27.5  27.5  29.25 27.75 28.75 29.25 28.5  28.5 ]\n",
-      " [27.5  27.5  29.25 27.75 28.75 29.25 28.5  28.5 ]]\n",
-      "UNB2_FPGA_POL_CORE_IOUT_R [[5.921875   4.109375   3.76171875 3.55859375]\n",
-      " [5.921875   4.1015625  3.76171875 3.55859375]]\n",
-      "UNB2_FPGA_POL_CORE_TEMP_R [[30.84375 31.46875 32.4375  34.75   ]\n",
-      " [30.84375 31.5     32.375   34.6875 ]]\n",
-      "UNB2_FPGA_POL_ERAM_VOUT_R [[0.8996582  0.90014648 0.90014648 0.8996582 ]\n",
-      " [0.8996582  0.8996582  0.90014648 0.8996582 ]]\n",
-      "UNB2_FPGA_POL_ERAM_IOUT_R [[0.08764648 0.0880127  0.18725586 0.08703613]\n",
-      " [0.02593994 0.0880127  0.18725586 0.08703613]]\n",
-      "UNB2_FPGA_POL_ERAM_TEMP_R [[38.75   39.25   41.     41.4375]\n",
-      " [38.75   39.25   41.     41.4375]]\n",
-      "UNB2_FPGA_POL_RXGXB_VOUT_R [[0.90014648 0.89990234 0.90014648 0.90014648]\n",
-      " [0.90014648 0.89990234 0.90014648 0.90014648]]\n",
-      "UNB2_FPGA_POL_RXGXB_IOUT_R [[0.49755859 0.41113281 0.40234375 0.48876953]\n",
-      " [0.49755859 0.41113281 0.40234375 0.48876953]]\n",
-      "UNB2_FPGA_POL_RXGXB_TEMP_R [[34.75   38.0625 36.5    38.1875]\n",
-      " [34.75   38.0625 36.5    38.1875]]\n",
-      "UNB2_FPGA_POL_TXGXB_VOUT_R [[0.89990234 0.90014648 0.90014648 0.89990234]\n",
-      " [0.89990234 0.90014648 0.90014648 0.89990234]]\n",
-      "UNB2_FPGA_POL_TXGXB_IOUT_R [[0.17480469 0.12219238 0.06433105 0.13110352]\n",
-      " [0.17480469 0.12219238 0.06433105 0.13110352]]\n",
-      "UNB2_FPGA_POL_HGXB_VOUT_R [[1.80004883 1.79956055 1.79980469 1.79980469]\n",
-      " [1.80004883 1.79956055 1.79980469 1.79980469]]\n",
-      "UNB2_FPGA_POL_HGXB_IOUT_R [[0.67089844 0.76269531 0.80664062 0.7265625 ]\n",
-      " [0.67089844 0.76269531 0.80664062 0.7265625 ]]\n",
-      "UNB2_FPGA_POL_HGXB_TEMP_R [[40.375  41.8125 44.3125 40.625 ]\n",
-      " [40.375  41.8125 44.3125 40.625 ]]\n",
-      "UNB2_FPGA_POL_PGM_VOUT_R [[1.80029297 1.80004883 1.79931641 1.80029297]\n",
-      " [1.80029297 1.80004883 1.79931641 1.80029297]]\n",
-      "UNB2_FPGA_POL_PGM_IOUT_R [[0.13818359 0.17089844 0.31542969 0.10656738]\n",
-      " [0.1550293  0.17089844 0.31542969 0.10656738]]\n",
-      "UNB2_FPGA_POL_PGM_TEMP_R [[40.0625 42.1875 44.3125 40.3125]\n",
-      " [40.0625 42.1875 44.3125 40.3125]]\n",
-      "State <function __get_command_func.<locals>.f at 0x7f636d295510>\n",
-      "Status <function __get_command_func.<locals>.f at 0x7f636d295510>\n"
+      "UNB2_EEPROM_Unique_ID_R [0 0]\n",
+      "UNB2_DC_DC_48V_12V_VIN_R [0. 0.]\n",
+      "UNB2_DC_DC_48V_12V_VOUT_R [0. 0.]\n",
+      "UNB2_DC_DC_48V_12V_IOUT_R [0. 0.]\n",
+      "UNB2_DC_DC_48V_12V_TEMP_R [0. 0.]\n",
+      "UNB2_POL_QSFP_N01_VOUT_R [0. 0.]\n",
+      "UNB2_POL_QSFP_N01_IOUT_R [0. 0.]\n",
+      "UNB2_POL_QSFP_N01_TEMP_R [0. 0.]\n",
+      "UNB2_POL_QSFP_N23_VOUT_R [0. 0.]\n",
+      "UNB2_POL_QSFP_N23_IOUT_R [0. 0.]\n",
+      "UNB2_POL_QSFP_N23_TEMP_R [0. 0.]\n",
+      "UNB2_POL_SWITCH_1V2_VOUT_R [0. 0.]\n",
+      "UNB2_POL_SWITCH_1V2_IOUT_R [0. 0.]\n",
+      "UNB2_POL_SWITCH_1V2_TEMP_R [0. 0.]\n",
+      "UNB2_POL_SWITCH_PHY_VOUT_R [0. 0.]\n",
+      "UNB2_POL_SWITCH_PHY_IOUT_R [0. 0.]\n",
+      "UNB2_POL_SWITCH_PHY_TEMP_R [0. 0.]\n",
+      "UNB2_POL_CLOCK_VOUT_R [0. 0.]\n",
+      "UNB2_POL_CLOCK_IOUT_R [0. 0.]\n",
+      "UNB2_POL_CLOCK_TEMP_R [0. 0.]\n",
+      "UNB2_FPGA_DDR4_SLOT_TEMP_R [[0. 0. 0. 0. 0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0. 0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_CORE_IOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_CORE_TEMP_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_ERAM_VOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_ERAM_IOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_ERAM_TEMP_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_RXGXB_VOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_RXGXB_IOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_RXGXB_TEMP_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_TXGXB_VOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_TXGXB_IOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_HGXB_VOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_HGXB_IOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_HGXB_TEMP_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_PGM_VOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_PGM_IOUT_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "UNB2_FPGA_POL_PGM_TEMP_R [[0. 0. 0. 0.]\n",
+      " [0. 0. 0. 0.]]\n",
+      "State <function __get_command_func.<locals>.f at 0x7f4c210e1ea0>\n",
+      "Status <function __get_command_func.<locals>.f at 0x7f4c210e1ea0>\n"
      ]
     }
    ],
@@ -138,7 +138,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 78,
+   "execution_count": 30,
    "id": "929965c2",
    "metadata": {},
    "outputs": [
@@ -171,7 +171,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 82,
+   "execution_count": 22,
    "id": "6813164e",
    "metadata": {},
    "outputs": [
@@ -201,7 +201,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 74,
+   "execution_count": 23,
    "id": "e9b32ec7",
    "metadata": {},
    "outputs": [
@@ -210,7 +210,7 @@
      "output_type": "stream",
      "text": [
       "Old values:\n",
-      " [ True  True]\n",
+      " [False False]\n",
       "Values to be set:\n",
       " [False False]\n",
       "Values read back after setting:\n",
@@ -231,25 +231,21 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 24,
    "id": "transsexual-battle",
    "metadata": {},
    "outputs": [
     {
-     "ename": "DevFailed",
-     "evalue": "DevFailed[\nDevError[\n    desc = Read value for attribute FPGA_mask_RW has not been updated\n  origin = Device_3Impl::read_attributes_no_except\n  reason = API_AttrValueNotSet\nseverity = ERR]\n\nDevError[\n    desc = Failed to read_attribute on device lts/sdp/1, attribute FPGA_mask_RW\n  origin = DeviceProxy::read_attribute()\n  reason = API_AttributeFailed\nseverity = ERR]\n]",
+     "ename": "AttributeError",
+     "evalue": "FPGA_mask_RW",
      "output_type": "error",
      "traceback": [
       "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[0;31mDevFailed\u001b[0m                                 Traceback (most recent call last)",
+      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
       "\u001b[0;32m/tmp/ipykernel_22/2885399456.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m values = [\n\u001b[0;32m----> 2\u001b[0;31m     \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFPGA_mask_RW\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"FPGA_mask_RW\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m     \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFPGA_scrap_R\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"FPGA_scrap_R\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m     \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFPGA_scrap_RW\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"FPGA_scrap_RW\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m     \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFPGA_status_R\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"FPGA_status_R\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__getattr\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m    319\u001b[0m     \u001b[0mattr_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__get_attr_cache\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname_l\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    320\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 321\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0m__get_attribute_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    322\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    323\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mname_l\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__get_pipe_cache\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__get_attribute_value\u001b[0;34m(self, attr_info, name)\u001b[0m\n\u001b[1;32m    281\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__get_attribute_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    282\u001b[0m     \u001b[0m_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menum_class\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 283\u001b[0;31m     \u001b[0mattr_value\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    284\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0menum_class\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    285\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0menum_class\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mattr_value\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/green.py\u001b[0m in \u001b[0;36mgreener\u001b[0;34m(obj, *args, **kwargs)\u001b[0m\n\u001b[1;32m    193\u001b[0m             \u001b[0mgreen_mode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0maccess\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'green_mode'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    194\u001b[0m             \u001b[0mexecutor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_object_executor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgreen_mode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 195\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mexecutor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwait\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwait\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    196\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    197\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mgreener\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/green.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, fn, args, kwargs, wait, timeout)\u001b[0m\n\u001b[1;32m    107\u001b[0m         \u001b[0;31m# Sychronous (no delegation)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    108\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masynchronous\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0min_executor_context\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 109\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    110\u001b[0m         \u001b[0;31m# Asynchronous delegation\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    111\u001b[0m         \u001b[0maccessor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdelegate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__read_attribute\u001b[0;34m(self, value, extract_as)\u001b[0m\n\u001b[1;32m    439\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    440\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__DeviceProxy__read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextract_as\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mExtractAs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumpy\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 441\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0m__check_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextract_as\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    442\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    443\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__check_read_attribute\u001b[0;34m(dev_attr)\u001b[0m\n\u001b[1;32m    155\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__check_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdev_attr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    156\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mdev_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhas_failed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 157\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mDevFailed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mdev_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_err_stack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    158\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0mdev_attr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    159\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
-      "\u001b[0;31mDevFailed\u001b[0m: DevFailed[\nDevError[\n    desc = Read value for attribute FPGA_mask_RW has not been updated\n  origin = Device_3Impl::read_attributes_no_except\n  reason = API_AttrValueNotSet\nseverity = ERR]\n\nDevError[\n    desc = Failed to read_attribute on device lts/sdp/1, attribute FPGA_mask_RW\n  origin = DeviceProxy::read_attribute()\n  reason = API_AttributeFailed\nseverity = ERR]\n]"
+      "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__getattr\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m    353\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_pipe\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    354\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 355\u001b[0;31m     \u001b[0msix\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mraise_from\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mAttributeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcause\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    356\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    357\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/six.py\u001b[0m in \u001b[0;36mraise_from\u001b[0;34m(value, from_value)\u001b[0m\n",
+      "\u001b[0;31mAttributeError\u001b[0m: FPGA_mask_RW"
      ]
     }
    ],
diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh
index a919b892767ef6a7d4eec83ba400cae79294190b..93b13300eb92afe2c95c7cb5c3292869019d9d96 100755
--- a/sbin/run_integration_test.sh
+++ b/sbin/run_integration_test.sh
@@ -8,8 +8,8 @@ fi
 
 # Start and stop sequence
 cd "$LOFAR20_DIR/docker-compose" || exit 1
-make stop device-sdp device-pcc device-sst sdptr-sim recv-sim
-make start databaseds dsconfig  jupyter elk
+make stop device-sdp device-recv device-sst device-unb2 sdptr-sim recv-sim unb2-sim
+make start databaseds dsconfig jupyter elk
 
 # Give dsconfig and databaseds time to start
 sleep 15
@@ -24,7 +24,7 @@ make start sdptr-sim recv-sim
 # Give the simulators time to start
 sleep 5
 
-make start device-sdp device-pcc device-sst
+make start device-sdp device-recv device-sst device-unb2
 
 # Give the devices time to start
 sleep 5