diff --git a/CMake/config.h.in b/CMake/config.h.in
index e2270e4ff5aabf245fde9cdd2c413e182ed4bcc8..10c2186ab94fb341d734c8a0aca6d37d74a1b0ce 100644
--- a/CMake/config.h.in
+++ b/CMake/config.h.in
@@ -14,6 +14,7 @@
 #define GMRT_MOCK_MS "@GMRT_MOCK_MS@"
 #define KL_SCREEN_PATH "@KL_SCREEN_PATH@"
 #define LWA_MOCK_PATH "@LWA_MOCK_PATH@"
+#define DISH_MOCK_PATH "@DISH_MOCK_PATH@"
 #define H5_SOLUTIONS_PATH "@H5_SOLUTIONS_PATH@"
 #define SCREEN_FITTING_MS "@SCREEN_FITTING_MS@"
 #define OSKAR_MOCK_MS "@OSKAR_MOCK_MS@"
diff --git a/cpp/circularsymmetric/CMakeLists.txt b/cpp/circularsymmetric/CMakeLists.txt
index 894a2fa8424fcc0a507ee0b140682338b713e601..f5d6611b5823d345f5d6d9173bcac49d92f2caec 100644
--- a/cpp/circularsymmetric/CMakeLists.txt
+++ b/cpp/circularsymmetric/CMakeLists.txt
@@ -1,6 +1,6 @@
 # Copyright (C) 2020 ASTRON (Netherlands Institute for Radio Astronomy)
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-install(FILES atcacoefficients.h gmrtcoefficients.h meerkatcoefficients.h
-              vlacoefficients.h voltagepattern.h
+install(FILES atcacoefficients.h coefficients.h gmrtcoefficients.h
+              meerkatcoefficients.h vlacoefficients.h voltagepattern.h
         DESTINATION "include/${CMAKE_PROJECT_NAME}/circularsymmetric")
diff --git a/cpp/pointresponse/dishpoint.cc b/cpp/pointresponse/dishpoint.cc
index 114e2ac74eb4412b189b060a109335bfc11eba59..98c8a9baa4bba18b7184be8aac499d2c209891bc 100644
--- a/cpp/pointresponse/dishpoint.cc
+++ b/cpp/pointresponse/dishpoint.cc
@@ -1,38 +1,102 @@
-// Copyright (C) 2021 ASTRON (Netherlands Institute for Radio Astronomy)
+// Copyright (C) 2024 ASTRON (Netherlands Institute for Radio Astronomy)
 // SPDX-License-Identifier: GPL-3.0-or-later
 
 #include "dishpoint.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include <aocommon/uvector.h>
+
 #include "../telescope/dish.h"
 #include "../circularsymmetric/voltagepattern.h"
 #include "../circularsymmetric/vlacoefficients.h"
-
-#include <aocommon/uvector.h>
+#include "./../coords/itrfdirection.h"
+#include "./../coords/itrfconverter.h"
+#include "../common/mathutils.h"
 
 namespace everybeam {
 namespace pointresponse {
+
+DishPoint::DishPoint(const telescope::Dish& dish, double time)
+    : PointResponse(&dish, time),
+      dish_(dish),
+      pointing_(dish_.GetFieldPointingMDirection().at(0)){};
+
 void DishPoint::Response(BeamMode /* beam_mode */, std::complex<float>* buffer,
                          double ra, double dec, double freq,
                          size_t /* station_idx */, size_t field_id) {
-  const telescope::Dish& dish_telescope =
-      static_cast<const telescope::Dish&>(GetTelescope());
-
   double pdir_ra;
   double pdir_dec;
-  std::tie(pdir_ra, pdir_dec) = dish_telescope.GetFieldPointing()[field_id];
+  std::tie(pdir_ra, pdir_dec) = dish_.GetFieldPointing()[field_id];
   const double max_radius_arc_min =
-      dish_telescope.GetDishCoefficients()->MaxRadiusInArcMin();
+      dish_.GetDishCoefficients()->MaxRadiusInArcMin();
   const double reference_frequency =
-      dish_telescope.GetDishCoefficients()->ReferenceFrequency();
+      dish_.GetDishCoefficients()->ReferenceFrequency();
+
+  // The computation below is ineffecient just a single point,
+  // see https://git.astron.nl/RD/EveryBeam/-/merge_requests/335#note_88457
   circularsymmetric::VoltagePattern vp(
-      dish_telescope.GetDishCoefficients()->GetFrequencies(freq),
-      max_radius_arc_min);
+      dish_.GetDishCoefficients()->GetFrequencies(freq), max_radius_arc_min);
   const aocommon::UVector<double> coefs_vec =
-      dish_telescope.GetDishCoefficients()->GetCoefficients(freq);
+      dish_.GetDishCoefficients()->GetCoefficients(freq);
   vp.EvaluatePolynomial(coefs_vec, reference_frequency,
-                        dish_telescope.GetDishCoefficients()->AreInverted());
+                        dish_.GetDishCoefficients()->AreInverted());
   vp.Render(buffer, ra, dec, pdir_ra, pdir_dec, freq);
 }
 
+aocommon::MC2x2 DishPoint::Response(BeamMode beam_mode, size_t station_idx,
+                                    double freq, const vector3r_t& direction,
+                                    std::mutex* mutex) {
+  std::complex<float> buffer[4];
+
+  if (HasTimeUpdate()) {
+    // Compute ITRF pointing
+    {
+      std::unique_lock<std::mutex> lock(*(mutex ? mutex : &mutex_));
+      const coords::ItrfConverter itrf_converter(GetIntervalMidPoint());
+      pointing_itrf_ = itrf_converter.ToItrf(pointing_);
+    }
+    ClearTimeUpdate();
+  }
+
+  // Compute pointing_direction_angle
+  double pointing_direction_angle =
+      acos(std::max(std::min(dot(pointing_itrf_, direction), 1.0), -1.0));
+
+  const double max_radius_arc_min =
+      dish_.GetDishCoefficients()->MaxRadiusInArcMin();
+  const double reference_frequency =
+      dish_.GetDishCoefficients()->ReferenceFrequency();
+
+  // The computation below is ineffecient just a single point,
+  // see https://git.astron.nl/RD/EveryBeam/-/merge_requests/335#note_88457
+  circularsymmetric::VoltagePattern vp(
+      dish_.GetDishCoefficients()->GetFrequencies(freq), max_radius_arc_min);
+  const aocommon::UVector<double> coefs_vec =
+      dish_.GetDishCoefficients()->GetCoefficients(freq);
+  vp.EvaluatePolynomial(coefs_vec, reference_frequency,
+                        dish_.GetDishCoefficients()->AreInverted());
+
+  // Because the voltage pattern is circular symmetric, the result
+  // depends only on the angle between the pointing and the requested direction
+  // Calling Render with direction_ra=pointing_direction_angle,
+  // direction_dec=0.0 and pointing_ra=0.0, pointing_ra=dec=0.0 yields the
+  // correct result
+
+  vp.Render(buffer, pointing_direction_angle, 0.0, 0.0, 0.0, freq);
+
+  // Because of the conversion from std::complex<float> to std::complex<double>
+  // It is not possible to directly return the buffer by a
+  //    return aocommon::MC2x2(buffer);
+  // Instead, cast the buffer element by element before returning
+  aocommon::MC2x2 result;
+  for (int i = 0; i < 4; i++) {
+    result[i] = buffer[i];
+  }
+  return result;
+}
+
 void DishPoint::ResponseAllStations(BeamMode beam_mode,
                                     std::complex<float>* buffer, double ra,
                                     double dec, double freq, size_t field_id) {
diff --git a/cpp/pointresponse/dishpoint.h b/cpp/pointresponse/dishpoint.h
index 634c37487aca4660da16ba1802270dfb1818d890..41a8d382361f71b4c65601814d2ebf3b2f00b4b5 100644
--- a/cpp/pointresponse/dishpoint.h
+++ b/cpp/pointresponse/dishpoint.h
@@ -7,7 +7,10 @@
 #ifndef EVERYBEAM_POINTRESPONSE_DISHPOINT_H_
 #define EVERYBEAM_POINTRESPONSE_DISHPOINT_H_
 
+#include <casacore/measures/Measures/MDirection.h>
+
 #include "pointresponse.h"
+#include "../telescope/dish.h"
 
 namespace everybeam {
 namespace pointresponse {
@@ -19,16 +22,25 @@ namespace pointresponse {
  */
 class [[gnu::visibility("default")]] DishPoint : public PointResponse {
  public:
-  DishPoint(const telescope::Telescope* telescope_ptr, double time)
-      : PointResponse(telescope_ptr, time){};
+  DishPoint(const telescope::Dish& dish, double time);
 
   void Response(BeamMode beam_mode, std::complex<float> * buffer, double ra,
                 double dec, double freq, size_t station_idx, size_t field_id)
       override;
 
+  aocommon::MC2x2 Response(BeamMode beam_mode, size_t station_idx, double freq,
+                           const vector3r_t& direction,
+                           std::mutex* mutex = nullptr) override;
+
   void ResponseAllStations(BeamMode beam_mode, std::complex<float> * buffer,
                            double ra, double dec, double freq, size_t field_id)
       final override;
+
+ private:
+  const telescope::Dish& dish_;
+  const casacore::MDirection& pointing_;
+  vector3r_t pointing_itrf_;
+  std::mutex mutex_;
 };
 }  // namespace pointresponse
 }  // namespace everybeam
diff --git a/cpp/pointresponse/skamidpoint.cc b/cpp/pointresponse/skamidpoint.cc
index 47067cbb9ff7def2b95cc771651b5dd568dde71c..240ff1fd127327c7e271a45b39b359de4eee2a18 100644
--- a/cpp/pointresponse/skamidpoint.cc
+++ b/cpp/pointresponse/skamidpoint.cc
@@ -14,7 +14,7 @@ namespace pointresponse {
 
 SkaMidPoint::SkaMidPoint(const telescope::Telescope* telescope_ptr, double time,
                          ElementResponseModel element_response_model)
-    : DishPoint(telescope_ptr, time),
+    : PointResponse(telescope_ptr, time),
       element_response_model_(element_response_model) {
   const telescope::SkaMid& ska_mid =
       static_cast<const telescope::SkaMid&>(GetTelescope());
diff --git a/cpp/pointresponse/skamidpoint.h b/cpp/pointresponse/skamidpoint.h
index 23a08f34ed9b00e7c1657bf2a1a8b980c22ac88b..96dd84e468b6991b55da3704434296b9d963efa3 100644
--- a/cpp/pointresponse/skamidpoint.h
+++ b/cpp/pointresponse/skamidpoint.h
@@ -7,7 +7,7 @@
 #ifndef EVERYBEAM_POINTRESPONSE_SKAMIDPOINT_H_
 #define EVERYBEAM_POINTRESPONSE_SKAMIDPOINT_H_
 
-#include "dishpoint.h"
+#include "pointresponse.h"
 #include "../elementresponse.h"
 
 namespace everybeam {
@@ -17,7 +17,7 @@ class SkaMidResponse;
 
 namespace pointresponse {
 
-class SkaMidPoint final : public DishPoint {
+class SkaMidPoint final : public PointResponse {
  public:
   SkaMidPoint(const telescope::Telescope* telescope_ptr, double time,
               ElementResponseModel element_response_model);
diff --git a/cpp/telescope/dish.cc b/cpp/telescope/dish.cc
index 92f37884db6ec327520030b7a889e42c23356059..0f4160a74b54a3b2c4329dacb5afa811c2bf3381 100644
--- a/cpp/telescope/dish.cc
+++ b/cpp/telescope/dish.cc
@@ -20,12 +20,16 @@ Dish::Dish(const casacore::MeasurementSet& ms,
   casacore::MSField field_table = ms.field();
   casacore::ArrayColumn<double> pointing_dir_col(
       field_table, casacore::MSField::columnName(casacore::MSField::DELAY_DIR));
+  casacore::ScalarMeasColumn<casacore::MDirection> pointing_dir_measure_col(
+      field_table,
+      casacore::MSField::columnName(casacore::MSFieldEnums::DELAY_DIR));
 
   for (std::size_t field_id = 0; field_id != field_table.nrow(); ++field_id) {
     casacore::Array<double> pdir = pointing_dir_col(field_id);
     double pdir_ra = *pdir.cbegin();
     double pdir_dec = *(pdir.cbegin() + 1);
     field_pointing_.emplace_back(pdir_ra, pdir_dec);
+    field_pointing_mdirection_.emplace_back(pointing_dir_measure_col(field_id));
   }
   SetIsTimeRelevant(false);
 }
@@ -36,5 +40,5 @@ std::unique_ptr<GriddedResponse> Dish::GetGriddedResponse(
 }
 
 std::unique_ptr<PointResponse> Dish::GetPointResponse(double time) const {
-  return std::make_unique<DishPoint>(this, time);
+  return std::make_unique<DishPoint>(*this, time);
 }
diff --git a/cpp/telescope/dish.h b/cpp/telescope/dish.h
index cf55c0e3b9e3e7c9715b922c3b9c7892f7cf8a75..65cb866978e950c9d40f62cf2ae998056622099b 100644
--- a/cpp/telescope/dish.h
+++ b/cpp/telescope/dish.h
@@ -51,6 +51,10 @@ class [[gnu::visibility("default")]] Dish final : public Telescope {
     return field_pointing_;
   }
 
+  const std::vector<casacore::MDirection>& GetFieldPointingMDirection() const {
+    return field_pointing_mdirection_;
+  }
+
   const circularsymmetric::Coefficients* GetDishCoefficients() const {
     return dish_coefficients_.get();
   }
@@ -59,6 +63,7 @@ class [[gnu::visibility("default")]] Dish final : public Telescope {
   std::unique_ptr<circularsymmetric::Coefficients> dish_coefficients_;
   /// Store ra, dec pointing per field id from measurement set
   std::vector<std::pair<double, double>> field_pointing_;
+  std::vector<casacore::MDirection> field_pointing_mdirection_;
 };
 }  // namespace telescope
 }  // namespace everybeam
diff --git a/cpp/test/CMakeLists.txt b/cpp/test/CMakeLists.txt
index b875f7094c4a542182b5d8ddcb1cda737199ef02..617ceb1d1a45df8aea1291aeddb1bf9ce61da13d 100644
--- a/cpp/test/CMakeLists.txt
+++ b/cpp/test/CMakeLists.txt
@@ -40,6 +40,9 @@ set(KL_SCREEN_PATH
 set(LWA_MOCK_PATH
     ${DATA_DIR}/LWA_OVRO_MOCK.ms
     CACHE PATH "")
+set(DISH_MOCK_PATH
+    ${DATA_DIR}/DISH_MOCK.ms
+    CACHE PATH "")
 set(LWA_COEFF_PATH
     ${CMAKE_BINARY_DIR}/coeffs/lwa
     CACHE PATH "")
@@ -51,6 +54,7 @@ set(LOBES_COEFF_PATH
 set(TEST_FILENAMES
     runtests.cc
     tatermconfig.cc
+    tdish.cc
     telementresponsefixeddirection.cc
     tsphericalharmonicsresponse.cc
     tgmrt.cc
@@ -106,6 +110,8 @@ add_custom_target(
           SCREEN_FITTING.ms
   COMMAND ${CMAKE_SOURCE_DIR}/scripts/download_ms.sh lwa-ovro.MS.tar.bz2
           LWA_OVRO_MOCK.ms
+  COMMAND ${CMAKE_SOURCE_DIR}/scripts/download_ms.sh meerkat_test.tar.gz
+          DISH_MOCK.ms
   COMMAND ${CMAKE_SOURCE_DIR}/scripts/download_compressed_file.sh
           OVRO_LWA_FEKO_TEST.tar.gz OVRO_LWA_FEKO_TEST.h5
   COMMAND ${CMAKE_SOURCE_DIR}/scripts/download_h5_solutions.sh
diff --git a/cpp/test/tdish.cc b/cpp/test/tdish.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c4738af64b221baed6a7f3a07355983b496bc7ec
--- /dev/null
+++ b/cpp/test/tdish.cc
@@ -0,0 +1,92 @@
+// Copyright (C) 2024 ASTRON (Netherlands Institute for Radio Astronomy)
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <boost/test/unit_test.hpp>
+
+#include "config.h"
+#include "../load.h"
+#include "../options.h"
+#include "../pointresponse/dishpoint.h"
+#include "../telescope/dish.h"
+#include "../coords/itrfconverter.h"
+
+namespace everybeam {
+namespace {
+// First time stamp in mock ms"
+const double kTime = 5068498314.005126;
+const double kFrequency = 8.56313e+08;
+const double kRa = 0.90848969;
+const double kDec = -0.48149271;
+
+}  // namespace
+
+BOOST_AUTO_TEST_SUITE(dish)
+
+BOOST_AUTO_TEST_CASE(load_dish) {
+  everybeam::Options options;
+
+  casacore::MeasurementSet ms(DISH_MOCK_PATH);
+
+  std::unique_ptr<everybeam::telescope::Telescope> telescope =
+      everybeam::Load(ms, options);
+
+  // Check that we have an Dish pointer.
+  const everybeam::telescope::Dish* dish_telescope =
+      dynamic_cast<const everybeam::telescope::Dish*>(telescope.get());
+  BOOST_REQUIRE(dish_telescope);
+
+  // Assert if correct number of stations
+  BOOST_CHECK_EQUAL(dish_telescope->GetNrStations(), size_t{62});
+
+  // Assert that we have a dish point response
+  std::unique_ptr<everybeam::pointresponse::PointResponse> point_response =
+      dish_telescope->GetPointResponse(kTime);
+  everybeam::pointresponse::DishPoint* dish_point_response =
+      dynamic_cast<everybeam::pointresponse::DishPoint*>(point_response.get());
+  BOOST_REQUIRE(dish_point_response);
+
+  std::vector<std::vector<std::complex<float>>> reference_response = {
+      {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {1.0, 0.0}},
+      {{0.382599, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.382599, 0.0}}};
+
+  std::vector<std::pair<double, double>> offsets = {{0.0, 0.0}, {0.01, -0.02}};
+
+  for (size_t j = 0; j < offsets.size(); j++) {
+    // Check the two response functions
+    double ra = kRa + offsets[j].first;
+    double dec = kDec + offsets[j].second;
+    size_t station_id = 0;
+    size_t field_id = 0;
+
+    std::array<std::complex<float>, 4> point_response_buffer;
+    dish_point_response->Response(everybeam::BeamMode::kFull,
+                                  point_response_buffer.data(), ra, dec,
+                                  kFrequency, station_id, field_id);
+
+    for (std::size_t i = 0; i < 4; ++i) {
+      std::cout << point_response_buffer[i] << std::endl;
+      BOOST_CHECK(
+          std::abs(point_response_buffer[i] - reference_response[j][i]) < 1e-6);
+    }
+
+    casacore::MDirection pointing(casacore::Quantity(ra, "rad"),
+                                  casacore::Quantity(dec, "rad"),
+                                  casacore::MDirection::J2000);
+
+    const coords::ItrfConverter itrf_converter(kTime);
+    vector3r_t direction = itrf_converter.ToItrf(pointing);
+
+    aocommon::MC2x2 response = dish_point_response->Response(
+        everybeam::BeamMode::kFull, station_id, kFrequency, direction);
+
+    for (std::size_t i = 0; i < 4; ++i) {
+      std::cout << response[i] << "  " << reference_response[j][i] << std::endl;
+      BOOST_CHECK(std::abs(response[i] - std::complex<double>(
+                                             reference_response[j][i])) < 1e-6);
+    }
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}  // namespace everybeam