diff --git a/Framework/Algorithms/inc/MantidAlgorithms/PDCalibration.h b/Framework/Algorithms/inc/MantidAlgorithms/PDCalibration.h
index 6118c04412e14b10b7edaa34ea445a5d8faee5fb..f88dcfb91b289524a8390c62bac07983e489e09c 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/PDCalibration.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/PDCalibration.h
@@ -48,6 +48,7 @@ private:
   class FittedPeaks; // forward declare of private inner class
 
   void init() override;
+  std::map<std::string, std::string> validateInputs() override;
   void exec() override;
   API::MatrixWorkspace_sptr loadAndBin();
   API::MatrixWorkspace_sptr rebin(API::MatrixWorkspace_sptr wksp);
@@ -69,9 +70,13 @@ private:
   std::vector<double> m_peaksInDspacing;
   std::string calParams;
   std::map<detid_t, size_t> m_detidToRow;
-  double m_tofMin;
-  double m_tofMax;
-  bool m_hasDasIds;
+  double m_tofMin{0.};
+  double m_tofMax{0.};
+  double m_tzeroMin{0.};
+  double m_tzeroMax{0.};
+  double m_difaMin{0.};
+  double m_difaMax{0.};
+  bool m_hasDasIds{false};
 };
 
 } // namespace Algorithms
diff --git a/Framework/Algorithms/src/PDCalibration.cpp b/Framework/Algorithms/src/PDCalibration.cpp
index 22c16709e077d05d02039bc6c638d025c3af4a48..8bbcac1ecdd4154770147dd7bc345b91f705d0f5 100644
--- a/Framework/Algorithms/src/PDCalibration.cpp
+++ b/Framework/Algorithms/src/PDCalibration.cpp
@@ -12,11 +12,13 @@
 #include "MantidKernel/ArrayProperty.h"
 #include "MantidKernel/BoundedValidator.h"
 #include "MantidKernel/CompositeValidator.h"
+#include "MantidKernel/Diffraction.h"
 #include "MantidKernel/ListValidator.h"
 #include "MantidKernel/MandatoryValidator.h"
 #include "MantidKernel/RebinParamsValidator.h"
 #include "MantidKernel/make_unique.h"
 #include <cassert>
+#include <limits>
 
 namespace Mantid {
 namespace Algorithms {
@@ -43,6 +45,7 @@ DECLARE_ALGORITHM(PDCalibration)
 
 namespace { // anonymous
 const auto isNonZero = [](const double value) { return value != 0.; };
+const double CHISQ_BAD = 1.e9; // hopefully much worse than possible
 }
 
 /// private inner class
@@ -115,7 +118,7 @@ public:
 //----------------------------------------------------------------------------------------------
 /** Constructor
  */
-PDCalibration::PDCalibration() : m_tofMin(0), m_tofMax(0), m_hasDasIds(false) {}
+PDCalibration::PDCalibration() {}
 
 //----------------------------------------------------------------------------------------------
 /** Destructor
@@ -234,6 +237,12 @@ void PDCalibration::init() {
                   boost::make_shared<StringListValidator>(modes),
                   "Select calibration parameters to fit.");
 
+  declareProperty(
+      Kernel::make_unique<ArrayProperty<double>>("TZEROrange"),
+      "Range for allowable TZERO from calibration (default is all)");
+  declareProperty(Kernel::make_unique<ArrayProperty<double>>("DIFArange"),
+                  "Range for allowable DIFA from calibration (default is all)");
+
   declareProperty(Kernel::make_unique<WorkspaceProperty<API::ITableWorkspace>>(
                       "OutputCalibrationTable", "", Direction::Output),
                   "An output workspace containing the Calibration Table");
@@ -264,6 +273,30 @@ void PDCalibration::init() {
   setPropertyGroup("StartFromObservedPeakCentre", findPeaksGroup);
 }
 
+std::map<std::string, std::string> PDCalibration::validateInputs() {
+  std::map<std::string, std::string> messages;
+
+  vector<double> tzeroRange = getProperty("TZEROrange");
+  if (!tzeroRange.empty()) {
+    if (tzeroRange.size() != 2) {
+      messages["TZEROrange"] = "Require two values [min,max]";
+    } else if (tzeroRange[0] >= tzeroRange[1]) {
+      messages["TZEROrange"] = "min must be less than max";
+    }
+  }
+
+  vector<double> difaRange = getProperty("DIFArange");
+  if (!difaRange.empty()) {
+    if (difaRange.size() != 2) {
+      messages["DIFArange"] = "Require two values [min,max]";
+    } else if (difaRange[0] >= difaRange[1]) {
+      messages["DIFArange"] = "min must be less than max";
+    }
+  }
+
+  return messages;
+}
+
 //----------------------------------------------------------------------------------------------
 /** Execute the algorithm.
  */
@@ -272,6 +305,38 @@ void PDCalibration::exec() {
   m_tofMin = tofBinningParams.front();
   m_tofMax = tofBinningParams.back();
 
+  vector<double> tzeroRange = getProperty("TZEROrange");
+  if (tzeroRange.size() == 2) {
+    m_tzeroMin = tzeroRange[0];
+    m_tzeroMax = tzeroRange[1];
+
+    std::stringstream msg;
+    msg << "Using tzero range of " << m_tzeroMin << " <= "
+        << "TZERO <= " << m_tzeroMax;
+    g_log.information(msg.str());
+  } else {
+    g_log.information("Using all TZERO values");
+
+    m_tzeroMin = std::numeric_limits<double>::lowest();
+    m_tzeroMax = std::numeric_limits<double>::max();
+  }
+
+  vector<double> difaRange = getProperty("DIFArange");
+  if (difaRange.size() == 2) {
+    m_difaMin = difaRange[0];
+    m_difaMax = difaRange[1];
+
+    std::stringstream msg;
+    msg << "Using difa range of " << m_difaMin << " <= "
+        << "DIFA <= " << m_difaMax;
+    g_log.information(msg.str());
+  } else {
+    g_log.information("Using all DIFA values");
+
+    m_difaMin = std::numeric_limits<double>::lowest();
+    m_difaMax = std::numeric_limits<double>::max();
+  }
+
   m_peaksInDspacing = getProperty("PeakPositions");
   // Sort peak positions, requried for correct peak window calculations
   std::sort(m_peaksInDspacing.begin(), m_peaksInDspacing.end());
@@ -433,17 +498,16 @@ struct d_to_tof {
 void PDCalibration::fitDIFCtZeroDIFA(const std::vector<double> &d,
                                      const std::vector<double> &tof,
                                      double &difc, double &t0, double &difa) {
-  double sum = 0;
-  double sumX = 0;
-  double sumY = 0;
-  double sumX2 = 0;
-  double sumXY = 0;
-  double sumX2Y = 0;
-  double sumX3 = 0;
-  double sumX4 = 0;
+  double num_peaks = static_cast<double>(d.size());
+  double sumX = 0.;
+  double sumY = 0.;
+  double sumX2 = 0.;
+  double sumXY = 0.;
+  double sumX2Y = 0.;
+  double sumX3 = 0.;
+  double sumX4 = 0.;
 
   for (size_t i = 0; i < d.size(); ++i) {
-    sum++;
     sumX2 += d[i] * d[i];
     sumXY += d[i] * tof[i];
   }
@@ -451,7 +515,7 @@ void PDCalibration::fitDIFCtZeroDIFA(const std::vector<double> &d,
   // DIFC only
   double difc0 = sumXY / sumX2;
   // Get out early if only DIFC is needed.
-  if (calParams == "DIFC" || sum < 3) {
+  if (calParams == "DIFC" || num_peaks < 3) {
     difc = difc0;
     return;
   }
@@ -464,10 +528,10 @@ void PDCalibration::fitDIFCtZeroDIFA(const std::vector<double> &d,
 
   double difc1 = 0;
   double tZero1 = 0;
-  double determinant = sum * sumX2 - sumX * sumX;
+  double determinant = num_peaks * sumX2 - sumX * sumX;
   if (determinant != 0) {
-    difc1 = (sum * sumXY - sumX * sumY) / determinant;
-    tZero1 = sumY / sum - difc1 * sumX / sum;
+    difc1 = (num_peaks * sumXY - sumX * sumY) / determinant;
+    tZero1 = sumY / num_peaks - difc1 * sumX / num_peaks;
   }
 
   // calculated chi squared for each fit
@@ -482,13 +546,24 @@ void PDCalibration::fitDIFCtZeroDIFA(const std::vector<double> &d,
     temp = tZero1 + difc1 * d[i] - tof[i];
     chisq1 += (temp * temp);
   }
+
   // Get reduced chi-squared
-  chisq0 = chisq0 / (sum - 1);
-  chisq1 = chisq1 / (sum - 2);
+  chisq0 = chisq0 / (num_peaks - 1);
+  chisq1 = chisq1 / (num_peaks - 2);
+
+  // check that the value is reasonable, only need to check minimum
+  // side since difa is not in play - shift to a higher minimum
+  // means something went wrong
+  if (m_tofMin < Kernel::Diffraction::calcTofMin(difc1, 0., tZero1, m_tofMin) ||
+      difc1 <= 0. || tZero1 < m_tzeroMin || tZero1 > m_tzeroMax) {
+    difc1 = 0;
+    tZero1 = 0;
+    chisq1 = CHISQ_BAD;
+  }
 
   // Select difc, t0 depending on CalibrationParameters chosen and
   // number of peaks fitted.
-  if (calParams == "DIFC+TZERO" || sum == 3) {
+  if (calParams == "DIFC+TZERO" || num_peaks == 3) {
     // choose best one according to chi-squared
     if (chisq0 < chisq1) {
       difc = difc0;
@@ -509,22 +584,22 @@ void PDCalibration::fitDIFCtZeroDIFA(const std::vector<double> &d,
   double tZero2 = 0;
   double difc2 = 0;
   double difa2 = 0;
-  determinant = sum * sumX2 * sumX4 + sumX * sumX3 * sumX2 +
+  determinant = num_peaks * sumX2 * sumX4 + sumX * sumX3 * sumX2 +
                 sumX2 * sumX * sumX3 - sumX2 * sumX2 * sumX2 -
-                sumX * sumX * sumX4 - sum * sumX3 * sumX3;
+                sumX * sumX * sumX4 - num_peaks * sumX3 * sumX3;
   if (determinant != 0) {
     tZero2 =
         (sumY * sumX2 * sumX4 + sumX * sumX3 * sumX2Y + sumX2 * sumXY * sumX3 -
          sumX2 * sumX2 * sumX2Y - sumX * sumXY * sumX4 - sumY * sumX3 * sumX3) /
         determinant;
-    difc2 =
-        (sum * sumXY * sumX4 + sumY * sumX3 * sumX2 + sumX2 * sumX * sumX2Y -
-         sumX2 * sumXY * sumX2 - sumY * sumX * sumX4 - sum * sumX3 * sumX2Y) /
-        determinant;
-    difa2 =
-        (sum * sumX2 * sumX2Y + sumX * sumXY * sumX2 + sumY * sumX * sumX3 -
-         sumY * sumX2 * sumX2 - sumX * sumX * sumX2Y - sum * sumXY * sumX3) /
-        determinant;
+    difc2 = (num_peaks * sumXY * sumX4 + sumY * sumX3 * sumX2 +
+             sumX2 * sumX * sumX2Y - sumX2 * sumXY * sumX2 -
+             sumY * sumX * sumX4 - num_peaks * sumX3 * sumX2Y) /
+            determinant;
+    difa2 = (num_peaks * sumX2 * sumX2Y + sumX * sumXY * sumX2 +
+             sumY * sumX * sumX3 - sumY * sumX2 * sumX2 - sumX * sumX * sumX2Y -
+             num_peaks * sumXY * sumX3) /
+            determinant;
   }
 
   // calculated reduced chi squared for each fit
@@ -533,15 +608,33 @@ void PDCalibration::fitDIFCtZeroDIFA(const std::vector<double> &d,
     double temp = tZero2 + difc2 * d[i] + difa2 * d[i] * d[i] - tof[i];
     chisq2 += (temp * temp);
   }
-  chisq2 = chisq2 / (sum - 3);
+  chisq2 = chisq2 / (num_peaks - 3);
+
+  // check that the value is reasonable, only need to check minimum
+  // side since difa is not in play - shift to a higher minimum
+  // or a lower maximum means something went wrong
+  if (m_tofMin <
+          Kernel::Diffraction::calcTofMin(difc2, difa2, tZero2, m_tofMin) ||
+      m_tofMax <
+          Kernel::Diffraction::calcTofMax(difc2, difa2, tZero2, m_tofMax) ||
+      difc2 <= 0. || tZero2 < m_tzeroMin || tZero2 > m_tzeroMax ||
+      difa2 < m_difaMin || difa2 > m_difaMax) {
+    tZero2 = 0;
+    difc2 = 0;
+    difa2 = 0;
+    chisq2 = CHISQ_BAD;
+  }
 
   // Select difc, t0 and difa depending on CalibrationParameters chosen and
   // number of peaks fitted.
   if ((chisq0 < chisq1) && (chisq0 < chisq2)) {
     difc = difc0;
+    t0 = 0.;
+    difa = 0.;
   } else if ((chisq1 < chisq0) && (chisq1 < chisq2)) {
     difc = difc1;
     t0 = tZero1;
+    difa = 0.;
   } else {
     difc = difc2;
     t0 = tZero2;
@@ -620,19 +713,10 @@ void PDCalibration::setCalibrationValues(const detid_t detid, const double difc,
 vector<double> PDCalibration::getTOFminmax(const double difc, const double difa,
                                            const double tzero) {
   vector<double> tofminmax(2);
-  if (difa == 0) {
-    tofminmax[0] = 0.;
-    tofminmax[1] = DBL_MAX;
-  } else {
-    double tofExtrema = tzero - difc * difc / (4 * difa);
-    if (difa < 0) {
-      tofminmax[0] = 0.;
-      tofminmax[1] = tofExtrema;
-    } else {
-      tofminmax[0] = std::max(0., tofExtrema);
-      tofminmax[1] = DBL_MAX;
-    }
-  }
+
+  tofminmax[0] = Kernel::Diffraction::calcTofMin(difc, difa, tzero, m_tofMin);
+  tofminmax[1] = Kernel::Diffraction::calcTofMax(difc, difa, tzero, m_tofMax);
+
   return tofminmax;
 }
 MatrixWorkspace_sptr PDCalibration::load(const std::string filename) {
diff --git a/Framework/DataHandling/src/LoadDiffCal.cpp b/Framework/DataHandling/src/LoadDiffCal.cpp
index b4791560c59fffa19caa3a80717c57aff0979276..1ad33c13f55cce2d3d5948b2dd76f27cd3258066 100644
--- a/Framework/DataHandling/src/LoadDiffCal.cpp
+++ b/Framework/DataHandling/src/LoadDiffCal.cpp
@@ -11,6 +11,7 @@
 #include "MantidDataObjects/MaskWorkspace.h"
 #include "MantidDataObjects/TableWorkspace.h"
 #include "MantidDataObjects/Workspace2D.h"
+#include "MantidKernel/Diffraction.h"
 
 #include <cmath>
 #include <H5Cpp.h>
@@ -230,37 +231,6 @@ void LoadDiffCal::makeMaskWorkspace(const std::vector<int32_t> &detids,
   setMaskWSProperty(this, m_workspaceName, wksp);
 }
 
-namespace { // anonymous namespace
-
-double calcTofMin(const double difc, const double difa, const double tzero,
-                  const double tofmin) {
-  if (difa == 0.) {
-    if (tzero != 0.) {
-      // check for negative d-spacing
-      return std::max<double>(-1. * tzero, tofmin);
-    }
-  } else if (difa > 0) {
-    // check for imaginary part in quadratic equation
-    return std::max<double>(tzero - .25 * difc * difc / difa, tofmin);
-  }
-
-  // everything else is fine so just return supplied tofmin
-  return tofmin;
-}
-
-double calcTofMax(const double difc, const double difa, const double tzero,
-                  const double tofmax) {
-  if (difa < 0.) {
-    // check for imaginary part in quadratic equation
-    return std::min<double>(tzero - .25 * difc * difc / difa, tofmax);
-  }
-
-  // everything else is fine so just return supplied tofmax
-  return tofmax;
-}
-
-} // end of anonymous namespace
-
 void LoadDiffCal::makeCalWorkspace(const std::vector<int32_t> &detids,
                                    const std::vector<double> &difc,
                                    const std::vector<double> &difa,
@@ -315,14 +285,16 @@ void LoadDiffCal::makeCalWorkspace(const std::vector<int32_t> &detids,
       newrow << offsets[i];
 
     // calculate tof range for information
-    const double tofMinRow = calcTofMin(difc[i], difa[i], tzero[i], tofMin);
+    const double tofMinRow =
+        Kernel::Diffraction::calcTofMin(difc[i], difa[i], tzero[i], tofMin);
     std::stringstream msg;
     if (tofMinRow != tofMin) {
       msg << "TofMin shifted from " << tofMin << " to " << tofMinRow << " ";
     }
     newrow << tofMinRow;
     if (useTofMax) {
-      const double tofMaxRow = calcTofMax(difc[i], difa[i], tzero[i], tofMax);
+      const double tofMaxRow =
+          Kernel::Diffraction::calcTofMax(difc[i], difa[i], tzero[i], tofMax);
       newrow << tofMaxRow;
 
       if (tofMaxRow != tofMax) {
diff --git a/Framework/Kernel/CMakeLists.txt b/Framework/Kernel/CMakeLists.txt
index 780e0a0a8b6a917f8c0c8eadc01468045fdebe32..23b9ee22f5006e6f3807a18ac2f84d51bf2c1247 100644
--- a/Framework/Kernel/CMakeLists.txt
+++ b/Framework/Kernel/CMakeLists.txt
@@ -2,7 +2,7 @@ set ( SRC_FILES
 	src/ANN_complete.cpp
 	src/ArrayBoundedValidator.cpp
 	src/ArrayLengthValidator.cpp
-  src/ArrayOrderedPairsValidator.cpp
+	src/ArrayOrderedPairsValidator.cpp
 	src/ArrayProperty.cpp
 	src/Atom.cpp
 	src/BinFinder.cpp
@@ -18,6 +18,7 @@ set ( SRC_FILES
 	src/DateTimeValidator.cpp
 	src/DateValidator.cpp
 	src/DeltaEMode.cpp
+	src/Diffraction.cpp
 	src/DirectoryValidator.cpp
 	src/DiskBuffer.cpp
 	src/DllOpen.cpp
@@ -84,8 +85,8 @@ set ( SRC_FILES
 	src/PropertyManager.cpp
 	src/PropertyManagerDataService.cpp
 	src/PropertyManagerOwner.cpp
-    	src/PropertyManagerProperty.cpp
-    	src/PropertyNexus.cpp
+	src/PropertyManagerProperty.cpp
+	src/PropertyNexus.cpp
 	src/PropertyWithValue.cpp
 	src/ProxyInfo.cpp
 	src/PseudoRandomNumberGenerator.cpp
@@ -166,6 +167,7 @@ set ( INC_FILES
 	inc/MantidKernel/DateTimeValidator.h
 	inc/MantidKernel/DateValidator.h
 	inc/MantidKernel/DeltaEMode.h
+	inc/MantidKernel/Diffraction.h
 	inc/MantidKernel/DirectoryValidator.h
 	inc/MantidKernel/DiskBuffer.h
 	inc/MantidKernel/DllConfig.h
@@ -334,6 +336,7 @@ set ( TEST_FILES
 	DateTimeValidatorTest.h
 	DateValidatorTest.h
 	DeltaEModeTest.h
+	DiffractionTest.h
 	DirectoryValidatorTest.h
 	DiskBufferISaveableTest.h
 	DiskBufferTest.h
diff --git a/Framework/Kernel/inc/MantidKernel/Diffraction.h b/Framework/Kernel/inc/MantidKernel/Diffraction.h
new file mode 100644
index 0000000000000000000000000000000000000000..1189b518adbc95b824b14f72642ae34105db05ba
--- /dev/null
+++ b/Framework/Kernel/inc/MantidKernel/Diffraction.h
@@ -0,0 +1,44 @@
+#ifndef MANTID_KERNEL_DIFFRACTION_H_
+#define MANTID_KERNEL_DIFFRACTION_H_
+
+#include "MantidKernel/DllConfig.h"
+
+namespace Mantid {
+namespace Kernel {
+namespace Diffraction {
+
+/** Diffraction : Collection of functions useful in diffraction scattering
+
+  Copyright &copy; 2017 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
+  National Laboratory & European Spallation Source
+
+  This file is part of Mantid.
+
+  Mantid is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  Mantid is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+  File change history is stored at: <https://github.com/mantidproject/mantid>
+  Code Documentation is available at: <http://doxygen.mantidproject.org>
+*/
+
+MANTID_KERNEL_DLL double calcTofMin(const double difc, const double difa,
+                                    const double tzero,
+                                    const double tofmin = 0.);
+
+MANTID_KERNEL_DLL double calcTofMax(const double difc, const double difa,
+                                    const double tzero, const double tofmax);
+} // Diffraction
+} // namespace Kernel
+} // namespace Mantid
+
+#endif /* MANTID_KERNEL_DIFFRACTION_H_ */
diff --git a/Framework/Kernel/src/Diffraction.cpp b/Framework/Kernel/src/Diffraction.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..db586c6c7c3a9f533fc26fa6df313e344c7e0303
--- /dev/null
+++ b/Framework/Kernel/src/Diffraction.cpp
@@ -0,0 +1,46 @@
+#include "MantidKernel/Diffraction.h"
+#include <algorithm>
+
+namespace Mantid {
+namespace Kernel {
+namespace Diffraction {
+
+double calcTofMin(const double difc, const double difa, const double tzero,
+                  const double tofmin) {
+  if (difa == 0.) {
+    if (tzero != 0.) {
+      // check for negative d-spacing
+      return std::max<double>(tzero, tofmin);
+    }
+  } else if (difa > 0.) {
+    // check for imaginary part in quadratic equation
+    return std::max<double>(tzero - .25 * difc * difc / difa, tofmin);
+  }
+
+  // everything else is fine so just return supplied tofmin
+  return tofmin;
+}
+
+/**
+ * Returns the maximum TOF that can be used or tofmax. Whichever is smaller. In
+ * the case when this is a negative number, just return 0.
+ */
+double calcTofMax(const double difc, const double difa, const double tzero,
+                  const double tofmax) {
+  if (difa < 0.) {
+    // check for imaginary part in quadratic equation
+    if (tzero > 0.) {
+      // rather than calling abs multiply difa by -1
+      return std::min<double>(tzero + .25 * difc * difc / difa, tofmax);
+    } else {
+      return 0.;
+    }
+  }
+
+  // everything else is fine so just return supplied tofmax
+  return tofmax;
+}
+
+} // namespace Diffraction
+} // namespace Kernel
+} // namespace Mantid
diff --git a/Framework/Kernel/test/DiffractionTest.h b/Framework/Kernel/test/DiffractionTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..85b9e7c9673cbd7cf59ea1e4eeaf93d3d45024f2
--- /dev/null
+++ b/Framework/Kernel/test/DiffractionTest.h
@@ -0,0 +1,76 @@
+#ifndef MANTID_KERNEL_DIFFRACTIONTEST_H_
+#define MANTID_KERNEL_DIFFRACTIONTEST_H_
+
+#include <cxxtest/TestSuite.h>
+#include <limits>
+
+#include "MantidKernel/Diffraction.h"
+
+using Mantid::Kernel::Diffraction::calcTofMin;
+using Mantid::Kernel::Diffraction::calcTofMax;
+
+namespace {                // anonymous
+const double DIFC = 2100.; // sensible value
+const double TZERO = 10.;
+// intentionally goofy - reduces tzero by 1
+const double DIFA1 = .25 * DIFC * DIFC;
+// intentionally goofy - reduces tzero by .01
+const double DIFA2 = 25 * DIFC * DIFC;
+// intentionally goofy
+const double DIFA3 = -.25 * DIFC * DIFC;
+}
+
+class DiffractionTest : public CxxTest::TestSuite {
+public:
+  // This pair of boilerplate methods prevent the suite being created statically
+  // This means the constructor isn't called when running other tests
+  static DiffractionTest *createSuite() { return new DiffractionTest(); }
+  static void destroySuite(DiffractionTest *suite) { delete suite; }
+
+  void test_calcTofMin() {
+    const double TMIN = 300.;
+
+    // just difc
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, 0., 0.), 0.);
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, 0., 0., TMIN), TMIN);
+    // difc + tzero
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, 0., TZERO, 0.), TZERO);
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, 0., TZERO, TMIN), TMIN);
+
+    // difc + difa + tzero
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, DIFA1, 0., 0.), 0.);
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, DIFA1, 0., TMIN), TMIN);
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, DIFA1, TZERO, 0.), TZERO - 1.);
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, DIFA1, TZERO, TMIN), TMIN);
+
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, DIFA2, 0., 0.), 0.);
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, DIFA2, 0., TMIN), TMIN);
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, DIFA2, TZERO, 0.), TZERO - .01);
+    TS_ASSERT_EQUALS(calcTofMin(DIFC, DIFA2, TZERO, TMIN), TMIN);
+  }
+
+  void test_calcTofMax() {
+    const double TMAX = 16666.7;
+    const double TSUPERMAX = std::numeric_limits<double>::max();
+
+    // just difc
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, 0., 0., TMAX), TMAX);
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, 0., 0., TSUPERMAX), TSUPERMAX);
+    // difc + tzero
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, 0., TZERO, TMAX), TMAX);
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, 0., TZERO, TSUPERMAX), TSUPERMAX);
+
+    // difc + difa + tzero
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, DIFA1, 0., TMAX), TMAX);
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, DIFA1, 0., TSUPERMAX), TSUPERMAX);
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, DIFA1, TZERO, TMAX), TMAX);
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, DIFA1, TZERO, TSUPERMAX), TSUPERMAX);
+
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, DIFA3, 0., TMAX), 0.);
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, DIFA3, 0., TSUPERMAX), 0.);
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, DIFA3, TZERO, TMAX), TZERO - 1.);
+    TS_ASSERT_EQUALS(calcTofMax(DIFC, DIFA3, TZERO, TSUPERMAX), TZERO - 1.);
+  }
+};
+
+#endif /* MANTID_KERNEL_DIFFRACTIONTEST_H_ */
diff --git a/docs/source/release/v3.10.0/diffraction.rst b/docs/source/release/v3.10.0/diffraction.rst
index a5405a1079f88d5662a516ab77b9bbe5819753f0..3b718959d2b34d0b0b0aae0ba5c3ca5cce39d5b4 100644
--- a/docs/source/release/v3.10.0/diffraction.rst
+++ b/docs/source/release/v3.10.0/diffraction.rst
@@ -21,6 +21,7 @@ Powder Diffraction
 - Bugfix in :ref:`SNAPReduce <algm-SNAPReduce>` with loading previous normalizations
 - :ref:`SNSPowderReduction <algm-SNSPowderReduction>` now supports splitters in format of ``MatrixWorkspace`` and general ``TableWorkspace``.
 - A new NOMAD instrument definition file with corrected values.
+- :ref:`algm-PDCalibration <algm-PDCalibration>` is better at giving out physically meaningful results. It will no longer create calibrations that will convert time-of-flight to negative or imaginary d-spacing.
 
 Single Crystal Diffraction
 --------------------------