diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/DDECal.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/DDECal.h
index 2bbb25cf5fcdbd8f436a2fb6e80394750e54055d..5d87ba0731ed81dc7f327760b7806b5489db4b3d 100644
--- a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/DDECal.h
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/DDECal.h
@@ -157,6 +157,8 @@ namespace LOFAR {
       MultiDirSolver   itsMultiDirSolver;
       bool itsFullMatrixMinimalization;
       bool itsApproximateTEC;
+      std::string itsStatFilename;
+      std::unique_ptr<std::ofstream> itsStatStream;
     };
 
   } //# end namespace
diff --git a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/MultiDirSolver.h b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/MultiDirSolver.h
index 517b78c276df1ca9db2b5789be6ab9172da9503d..a18de7c186ebbd7b66824c2022f1f45bcd33eb49 100644
--- a/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/MultiDirSolver.h
+++ b/CEP/DP3/DPPP_DDECal/include/DPPP_DDECal/MultiDirSolver.h
@@ -55,8 +55,10 @@ public:
   // data[i] is een pointer naar de data voor tijdstap i, vanaf die pointer staat het in volgorde als in MS (bl, chan, pol)
   // mdata[i] is een pointer voor tijdstap i naar arrays van ndir model data pointers (elk van die data pointers staat in zelfde volgorde als data)
   // solutions[ch] is een pointer voor channelblock ch naar antenna x directions oplossingen.
-  SolveResult processScalar(std::vector<Complex*>& data, std::vector<std::vector<Complex* > >& modelData,
-    std::vector<std::vector<DComplex> >& solutions, double time);
+  SolveResult processScalar(std::vector<Complex*>& data,
+    std::vector<std::vector<Complex* > >& modelData,
+    std::vector<std::vector<DComplex> >& solutions, double time,
+    std::ostream* statStream);
   
   /**
    * Same as @ref processScalar(), but solves full Jones matrices.
@@ -67,7 +69,8 @@ public:
    */
   SolveResult processFullMatrix(std::vector<Complex *>& data,
     std::vector<std::vector<Complex *> >& modelData,
-    std::vector<std::vector<DComplex> >& solutions, double time);
+    std::vector<std::vector<DComplex> >& solutions, double time,
+    std::ostream* statStream);
   
   void set_phase_only(bool phaseOnly) { _phaseOnly = phaseOnly; }
   
@@ -114,8 +117,12 @@ private:
    * Assign the solutions in nextSolutions to the solutions.
    * @returns whether the solutions have been converged.
    */
-  bool assignSolutions(std::vector<std::vector<DComplex> >& solutions,
-    std::vector<std::vector<DComplex> >& nextSolutions, bool useConstraintAccuracy) const;
+  bool assignSolutions(
+    std::vector<std::vector<DComplex> >& solutions,
+    std::vector<std::vector<DComplex> >& nextSolutions,
+    bool useConstraintAccuracy,
+    double& sum, double& normSum
+  ) const;
                              
   size_t _nAntennas, _nDirections, _nChannels, _nChannelBlocks;
   std::vector<int> _ant1, _ant2;
diff --git a/CEP/DP3/DPPP_DDECal/src/DDECal.cc b/CEP/DP3/DPPP_DDECal/src/DDECal.cc
index 9678ccab75844a00cc77b844be6264248ae4dca3..5213942dc7191cd11263b0d5d1ca77b92f7ba53d 100644
--- a/CEP/DP3/DPPP_DDECal/src/DDECal.cc
+++ b/CEP/DP3/DPPP_DDECal/src/DDECal.cc
@@ -92,7 +92,8 @@ namespace LOFAR {
         itsCoreConstraint(parset.getDouble (prefix + "coreconstraint", 0.0)),
         itsScreenCoreConstraint(parset.getDouble (prefix + "tecscreen.coreconstraint", 0.0)),
         itsFullMatrixMinimalization(false),
-        itsApproximateTEC(false)
+        itsApproximateTEC(false),
+	itsStatFilename(parset.getString(prefix + "statfilename", ""))
     {
       stringstream ss;
       ss << parset;
@@ -108,6 +109,9 @@ namespace LOFAR {
       itsMultiDirSolver.set_constraint_accuracy(parset.getDouble(prefix + "approxtolerance", tolerance*10.0));
       itsMultiDirSolver.set_step_size(parset.getDouble(prefix + "stepsize", 0.2));
 
+      if(!itsStatFilename.empty())
+	itsStatStream.reset(new std::ofstream(itsStatFilename));
+      
       // Default directions are all patches
       if (strDirections.empty()) {
         string sourceDBName = parset.getString(prefix+"sourcedb");
@@ -509,12 +513,12 @@ namespace LOFAR {
       {
         solveResult = itsMultiDirSolver.processFullMatrix(itsDataPtrs, itsModelDataPtrs,
           itsSols[itsTimeStep/itsSolInt],
-          itsAvgTime / itsSolInt);
+	  itsAvgTime / itsSolInt, itsStatStream.get());
       }
       else {
         solveResult = itsMultiDirSolver.processScalar(itsDataPtrs, itsModelDataPtrs,
           itsSols[itsTimeStep/itsSolInt],
-          itsAvgTime / itsSolInt);
+	  itsAvgTime / itsSolInt, itsStatStream.get());
       }
       itsTimerSolve.stop();
 
diff --git a/CEP/DP3/DPPP_DDECal/src/MultiDirSolver.cc b/CEP/DP3/DPPP_DDECal/src/MultiDirSolver.cc
index c73b0c4002ce5b98e12993a4d341acc5cd2d1e55..a088e164a62ce5a4bd9790f9dcaf7a20459bda65 100644
--- a/CEP/DP3/DPPP_DDECal/src/MultiDirSolver.cc
+++ b/CEP/DP3/DPPP_DDECal/src/MultiDirSolver.cc
@@ -86,9 +86,11 @@ void MultiDirSolver::makeSolutionsFinite(std::vector<std::vector<DComplex> >& so
 }
 
 bool MultiDirSolver::assignSolutions(std::vector<std::vector<DComplex> >& solutions,
-  std::vector<std::vector<DComplex> >& nextSolutions, bool useConstraintAccuracy) const
+  std::vector<std::vector<DComplex> >& nextSolutions, bool useConstraintAccuracy,
+  double& sum, double& normSum) const
 {
-  double normSum = 0.0, sum = 0.0;
+  sum = 0.0;
+  normSum = 0.0;
   //  Calculate the norm of the difference between the old and new solutions
   size_t n = 0;
   for(size_t chBlock=0; chBlock<_nChannelBlocks; ++chBlock)
@@ -103,8 +105,8 @@ bool MultiDirSolver::assignSolutions(std::vector<std::vector<DComplex> >& soluti
       solutions[chBlock][i] = nextSolutions[chBlock][i];
     }
   }
-  normSum /= n;
   sum /= n;
+  normSum /= n;
   if(useConstraintAccuracy)
     return normSum*_stepSize/sum <= _constraintAccuracy;
   else
@@ -113,7 +115,8 @@ bool MultiDirSolver::assignSolutions(std::vector<std::vector<DComplex> >& soluti
 
 MultiDirSolver::SolveResult MultiDirSolver::processScalar(std::vector<Complex *>& data,
   std::vector<std::vector<Complex *> >& modelData,
-  std::vector<std::vector<DComplex> >& solutions, double time)
+  std::vector<std::vector<DComplex> >& solutions, double time,
+  std::ostream* statStream)
 {
   const size_t nTimes = data.size();
   SolveResult result;
@@ -200,7 +203,12 @@ MultiDirSolver::SolveResult MultiDirSolver::processScalar(std::vector<Complex *>
     if(!constraintsSatisfied)
       constrainedIterations = iteration+1;
     
-    hasConverged = assignSolutions(solutions, nextSolutions, !constraintsSatisfied);
+    double sum, normSum;
+    hasConverged = assignSolutions(solutions, nextSolutions, !constraintsSatisfied, sum, normSum);
+    if(statStream != nullptr)
+    {
+      (*statStream) << iteration << '\t' << normSum*_stepSize/sum << '\t' << normSum << '\n';
+    }
     iteration++;
     
     hasPreviouslyConverged = hasConverged || hasPreviouslyConverged;
@@ -304,7 +312,8 @@ void MultiDirSolver::performScalarIteration(size_t channelBlockIndex,
 
 MultiDirSolver::SolveResult MultiDirSolver::processFullMatrix(std::vector<Complex *>& data,
   std::vector<std::vector<Complex *> >& modelData,
-  std::vector<std::vector<DComplex> >& solutions, double time)
+  std::vector<std::vector<DComplex> >& solutions, double time,
+  std::ostream* statStream)
 {
   // This algorithm is basically the same as the scalar algorithm,
   // but visibility values are extended to 2x2 matrices and concatenated
@@ -412,7 +421,12 @@ MultiDirSolver::SolveResult MultiDirSolver::processFullMatrix(std::vector<Comple
     if(!constraintsSatisfied)
       constrainedIterations = iteration+1;
     
-    hasConverged = assignSolutions(solutions, nextSolutions, !constraintsSatisfied);
+    double sum, normSum;
+    hasConverged = assignSolutions(solutions, nextSolutions, !constraintsSatisfied, sum, normSum);
+    if(statStream != nullptr)
+    {
+      (*statStream) << iteration << '\t' << normSum*_stepSize/sum << '\t' << normSum << '\n';
+    }
     iteration++;
     
     hasPreviouslyConverged = hasConverged || hasPreviouslyConverged;