diff --git a/CDB/LOFAR_ConfigDb.json b/CDB/LOFAR_ConfigDb.json
index 9c243aab268d9ca13f37bfe3d7af9cfbba8222de..de32af15daedc471ada0abd83ae9c1ec4e993346 100644
--- a/CDB/LOFAR_ConfigDb.json
+++ b/CDB/LOFAR_ConfigDb.json
@@ -90,6 +90,12 @@
                 "DigitalBeam": {
                     "STAT/DigitalBeam/1": {
                         "properties": {
+                            "Beam_tracking_interval": [
+                                "1.0"
+                            ],
+                            "Beam_tracking_preparation_period": [
+                                "0.5"
+                            ]
                         }
                     }
                 }
diff --git a/tangostationcontrol/tangostationcontrol/beam/delays.py b/tangostationcontrol/tangostationcontrol/beam/delays.py
index 850213c0cc64cdb72cb567a9b8c5334a464f4965..b1d7f9976ec5de3e75784dac484c9c33584f2806 100644
--- a/tangostationcontrol/tangostationcontrol/beam/delays.py
+++ b/tangostationcontrol/tangostationcontrol/beam/delays.py
@@ -1,5 +1,4 @@
 import casacore.measures
-from math import sin, cos
 import numpy
 import datetime
 
@@ -29,26 +28,30 @@ class delay_calculator:
         result = self.measure.do_frame(frame_time)
         assert result == True
 
-    def get_direction_vector(self, pointing: dict):
-        """ Compute a direction vector for the given pointing, relative to the measure. """
-        angles = self.measure.measure(pointing, "ITRF")
-        angle0, angle1 = angles["m0"]["value"], angles["m1"]["value"]
+    def get_direction_vector(self, pointing: numpy.ndarray) -> numpy.ndarray:
+        """ Compute direction vector for a given pointing, relative to the measure. """
 
-        # Convert polar to carthesian coordinates
-        # see also https://github.com/casacore/casacore/blob/e793b3d5339d828a60339d16476bf688a19df3ec/casa/Quanta/MVDirection.cc#L67
-        direction_vector = numpy.array(
-            (cos(angle0) * cos(angle1),
-             sin(angle0) * cos(angle1),
-             sin(angle1)))
+        return self.get_direction_vector_bulk([pointing]).flatten()
+
+    def get_direction_vector_bulk(self, pointings: numpy.ndarray) -> numpy.ndarray:
+        """ Compute direction vectors for the given pointings, relative to the measure. """
 
-        return direction_vector
+        angles0 = numpy.empty(len(pointings))
+        angles1 = numpy.empty(len(pointings))
+        for idx, pointing in enumerate(pointings):
+            angles = self.measure.measure(pointing, "ITRF")
+            angles0[idx] = angles["m0"]["value"]
+            angles1[idx] = angles["m1"]["value"]
 
-    def _get_delay(self, reference_direction_vector: numpy.array, relative_itrf: numpy.array) -> float:
-        """ Compute the delay to apply, in seconds, to align signals for a distance of `relative_itrf` in the
-            direction of `reference_direction_vector`. """
-        speed_of_light = 299792458.0
+        # Convert polar to carthesian coordinates
+        # see also https://github.com/casacore/casacore/blob/e793b3d5339d828a60339d16476bf688a19df3ec/casa/Quanta/MVDirection.cc#L67
+        direction_vectors = numpy.array(
+               [numpy.cos(angles0) * numpy.cos(angles1),
+                numpy.sin(angles0) * numpy.cos(angles1),
+                numpy.sin(angles1)])
 
-        return numpy.dot(reference_direction_vector, relative_itrf) / speed_of_light
+        # Return array [directions][angles]
+        return direction_vectors.T
 
     def is_valid_direction(self, direction):
         try:
@@ -58,18 +61,34 @@ class delay_calculator:
 
         return True
 
-    def convert(self, direction, antenna_itrf: list([float])):
-        # explicitly check validity to get a proper error message
-        if not self.is_valid_direction(direction):
-            raise ValueError(f"Invalid direction: {direction}")
+    def convert(self, direction, antenna_absolute_itrf: list([float])):
+        """ Get the delays for a direction and *absolute* antenna positions.
+        
+            Returns delays[antenna]. """
 
-        # obtain the direction vector for a specific pointing
-        pointing = self.measure.direction(*direction)
+        return self.convert_bulk([direction], numpy.array(antenna_absolute_itrf) - self.reference_itrf).flatten()
 
-        reference_dir_vector = self.get_direction_vector(pointing)
+    def convert_bulk(self, directions: numpy.ndarray, antenna_relative_itrfs: numpy.ndarray) -> numpy.ndarray:
+        """ Get the delays for each direction and each *relative* antenna position.
+        
+            Returns delays[antenna][direction]. """
 
-        # # compute the delays for an antennas w.r.t. the reference position
-        antenna_relative_itrf =  [subtract(pos, self.reference_itrf) for pos in antenna_itrf]
-        delays = [self._get_delay(reference_dir_vector, relative_itrf) for relative_itrf in antenna_relative_itrf]
+        # obtain the direction vector for each pointing
+        try:
+            pointings = [self.measure.direction(*direction) for direction in directions]
+        except (RuntimeError, TypeError) as e:
+            raise ValueError("Invalid direction") from e
+
+        direction_vectors = self.get_direction_vector_bulk(pointings)
+
+        # compute the corresponding delays for all directions
+        def get_delay_all_directions(relative_itrf):
+            # Dot product between relative position and angle vector determines
+            # distance. Divide by speed of light to obtain delay.
+            return numpy.inner(relative_itrf, direction_vectors) / 299792458.0
+
+        # apply for each position
+        delays = numpy.apply_along_axis(get_delay_all_directions, 1, antenna_relative_itrfs)
 
         return delays
+
diff --git a/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py
index 7f9c8d07fb3e5ceca540c2a03294dc9a92450d66..d3e6ee91b1177cb6e7306e07c448528cc476c8df 100644
--- a/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py
+++ b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py
@@ -90,8 +90,11 @@ class attribute_wrapper(attribute):
             """
             read_func_wrapper reads the attribute value, stores it and returns it"
             """
-            device.value_dict[self] = self.read_function()
-            return device.value_dict[self]
+            try:
+                device.value_dict[self] = self.read_function()
+                return device.value_dict[self]
+            except Exception as e:
+                raise e.__class__(f"Could not read attribute {comms_annotation}") from e
 
         self.fget = read_func_wrapper
 
diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py
index 9eb7533375f669acbcf652a5352deb47fdc5653e..2e985b8f10d66341a76bbe6b192dc3bf69677c8d 100644
--- a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py
+++ b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py
@@ -89,8 +89,8 @@ class DigitalBeam(beam_device):
         # a delay calculator
         self.delay_calculator = delay_calculator(reference_itrf)
 
-        # absolute positions of each antenna element
-        self.antenna_positions = antenna_itrf
+        # relatvie positions of each antenna
+        self.relative_antenna_positions = antenna_itrf - reference_itrf
 
         # use all antennas unless specified otherwise
         self._antenna_select = numpy.ones((self.NUM_ANTENNAS, self.NUM_BEAMLETS), dtype=numpy.bool_)
@@ -112,9 +112,8 @@ class DigitalBeam(beam_device):
         d = self.delay_calculator
         d.set_measure_time(timestamp)
 
-        for beamlet in range(self.NUM_BEAMLETS):
-            # calculate the delays based on the set reference position, the set time and now the set direction and antenna positions
-            delays[:, beamlet] = d.convert(pointing_direction[beamlet], self.antenna_positions)
+        # calculate the delays based on the set reference position, the set time and now the set direction and antenna positions
+        delays = d.convert_bulk(pointing_direction, self.relative_antenna_positions)
 
         return delays
 
diff --git a/tangostationcontrol/tangostationcontrol/test/beam/test_delays.py b/tangostationcontrol/tangostationcontrol/test/beam/test_delays.py
index b984660d677c81349c134d9be7c7441924253fc6..f2c537aafea96a8c28388bff40637bd8ca5eea73 100644
--- a/tangostationcontrol/tangostationcontrol/test/beam/test_delays.py
+++ b/tangostationcontrol/tangostationcontrol/test/beam/test_delays.py
@@ -1,12 +1,13 @@
 import datetime
+import time
+import logging
+import numpy
+import numpy.testing
 
 from tangostationcontrol.beam.delays import delay_calculator
 from tangostationcontrol.test import base
 
 
-
-
-
 class TestDelays(base.TestCase):
     def test_init(self):
         """
@@ -94,7 +95,27 @@ class TestDelays(base.TestCase):
         # calculate the delays based on the set reference position, the set time and now the set direction and antenna positions.
         delays = d.convert(direction, antenna_itrf)
 
-        self.assertListEqual(delays, [0.0], msg=f"delays = {delays}")
+        self.assertListEqual(delays.tolist(), [0.0], msg=f"delays = {delays}")
+
+    def test_regression(self):
+        reference_itrf = [3826577.066, 461022.948, 5064892.786]  # CS002LBA, in ITRF2005 epoch 2012.5
+        d = delay_calculator(reference_itrf)
+
+        # set the antenna position identical to the reference position
+        antenna_itrf = [[3826923.503, 460915.488, 5064643.517]]  # CS001LBA, in ITRF2005 epoch 2012.5
+
+        # # set the timestamp to solve for
+        timestamp = datetime.datetime(2000, 1, 1, 0, 0, 0)
+        d.set_measure_time(timestamp)
+
+        # # obtain the direction vector for a specific pointing
+        direction = "J2000", "0deg", "90deg"
+
+        # calculate the delays based on the set reference position, the set time and now the set direction and antenna positions.
+        delays = d.convert(direction, antenna_itrf)
+
+        # check for regression
+        self.assertAlmostEqual(-8.31467564781444e-07, delays[0], delta=1.0e-10)
 
     def test_light_second_delay(self):
         """
@@ -123,3 +144,39 @@ class TestDelays(base.TestCase):
 
         self.assertAlmostEqual(0.1, delays[0], 6, f"delays[0] = {delays[0]}")
 
+
+    def test_convert_bulk(self):
+        d = delay_calculator([0, 0, 0])
+        timestamp = datetime.datetime(2022, 3, 1, 0, 0, 0) # timestamp does not actually matter, but casacore doesn't know that.
+        d.set_measure_time(timestamp)
+
+        # generate different positions and directions
+        positions = numpy.array([[i,2,3] for i in range(5)])
+        directions = numpy.array([["J2000", f"{i}deg", f"{i}deg"] for i in range(90)])
+
+        bulk_result = d.convert_bulk(directions, positions)
+
+        # verify parallellisation along direction axis
+        for i, single_dir in enumerate(directions):
+            single_dir_result = d.convert_bulk([single_dir], positions)
+            numpy.testing.assert_equal(single_dir_result[:, 0], bulk_result[:, i])
+
+        # verify parallellisation along position axis
+        for i, single_pos in enumerate(positions):
+            single_pos_result = d.convert_bulk(directions, [single_pos])
+            numpy.testing.assert_equal(single_pos_result[0, :], bulk_result[i, :])
+
+    def test_convert_bulk_speed(self):
+        d = delay_calculator([0, 0, 0])
+        timestamp = datetime.datetime(2022, 3, 1, 0, 0, 0) # timestamp does not actually matter, but casacore doesn't know that.
+        d.set_measure_time(timestamp)
+
+        positions = numpy.array([[1,2,3]] * 96)
+        directions = numpy.array([["J2000", "0deg", "0deg"]] * 488)
+
+        count = 10
+        before = time.monotonic_ns()
+        for _ in range(count):
+            _ = d.convert_bulk(directions, positions)
+        after = time.monotonic_ns()
+        logging.error(f"convert_bulk averaged {(after - before)/count/1e6} ms to convert 488 directions for 96 antennas.")