diff --git a/CEP/Calibration/BBSControl/include/BBSControl/KernelProcessControl.h b/CEP/Calibration/BBSControl/include/BBSControl/KernelProcessControl.h
index 684ce55b9983e45f4de3e71d2048edec271e6f6e..49e2a4d0e719a2cad6436cbbc5d7d06a3f0f20be 100644
--- a/CEP/Calibration/BBSControl/include/BBSControl/KernelProcessControl.h
+++ b/CEP/Calibration/BBSControl/include/BBSControl/KernelProcessControl.h
@@ -32,17 +32,11 @@
 #include <BBSControl/CommandVisitor.h>
 #include <BBSControl/SolveStep.h>
 #include <BBSControl/Types.h>
-
 #include <BBSKernel/Measurement.h>
 #include <BBSKernel/VisSelection.h>
 #include <BBSKernel/VisBuffer.h>
-
-#include <PLC/ProcessControl.h>
-
 #include <ParmDB/SourceDB.h>
-
-//#include <ParmDB/ParmDBLog.h>			// logging of solver parameters in ParmDB
-
+#include <PLC/ProcessControl.h>
 #include <Common/lofar_smartptr.h>
 #include <Common/lofar_string.h>
 #include <Common/lofar_vector.h>
@@ -137,6 +131,7 @@ private:
     KernelIndex                             itsKernelIndex;
 
     // Measurement.
+    string                                  itsPath;
     Measurement::Ptr                        itsMeasurement;
     string                                  itsInputColumn;
 
@@ -150,8 +145,6 @@ private:
 
     // Connection to the global solver.
     shared_ptr<BlobStreamableConnection>    itsSolver;
-
-//	 scoped_ptr<ParmDBLog>     				  itsParmLogger;
 };
 
 //@}
diff --git a/CEP/Calibration/BBSControl/include/BBSControl/SolveStep.h b/CEP/Calibration/BBSControl/include/BBSControl/SolveStep.h
index b21dbcede5f8b3377163fd06b0ce919a388f47b8..11c5c006be8e8553721244a503dc938b8249c2b5 100644
--- a/CEP/Calibration/BBSControl/include/BBSControl/SolveStep.h
+++ b/CEP/Calibration/BBSControl/include/BBSControl/SolveStep.h
@@ -92,6 +92,11 @@ namespace LOFAR
         { return itsCellChunkSize; }
       bool                  propagate()         const
         { return itsPropagateFlag; }
+      string				logName()           const
+        { return itsLogName; }
+      string				logLevel()          const
+        { return itsLogLevel; }
+
       bool                  uvFlag()            const
         { return itsUVFlag; }
       pair<double, double>  uvRange()           const
@@ -128,9 +133,6 @@ namespace LOFAR
         { return itsBalancedEq; }
       bool                  useSVD()            const
         { return itsUseSVD; }
-
-      string				parmLogLevel()      const
-        { return itsParmLogLevel; }
       // @}
 
       // Return the command type of \c *this as a string.
@@ -165,6 +167,11 @@ namespace LOFAR
       uint32                itsCellChunkSize;
       // Propagate solutions?
       bool                  itsPropagateFlag;
+      // Solver statistics log.
+      string                itsLogName;
+      // Solver statistics log level.
+      string				itsLogLevel;
+
       // Should visbilities be flagged temporarily based on UV distance?
       bool                  itsUVFlag;
       // Interval of baseline (UV) length in wavelengths used to select samples
@@ -206,9 +213,6 @@ namespace LOFAR
       bool                  itsBalancedEq;
       // Use singular value decomposition.
       bool                  itsUseSVD;
-
-      // Solver ParmDB logging level
-      string				itsParmLogLevel;
     };
 
     // @}
diff --git a/CEP/Calibration/BBSControl/src/KernelProcessControl.cc b/CEP/Calibration/BBSControl/src/KernelProcessControl.cc
index 6aa9ec10e051031ac44da38ad928f5e15453eefe..51609455e1ec720ef0d8376d8ee56d3e1ebb8ebf 100644
--- a/CEP/Calibration/BBSControl/src/KernelProcessControl.cc
+++ b/CEP/Calibration/BBSControl/src/KernelProcessControl.cc
@@ -134,12 +134,11 @@ namespace LOFAR
         string path = ps->getString("ObservationPart.Path");
         string skyDb = ps->getString("ParmDB.Sky");
         string instrumentDb = ps->getString("ParmDB.Instrument");
-        string solverDb=ps->getString("ParmLog", "solver");
-        string loggingLevel=ps->getString("ParmLoglevel", "NONE");
 
         try {
           // Open observation part.
           LOG_INFO_STR("Observation part: " << filesys << " : " << path);
+          itsPath = path;
           itsMeasurement.reset(new MeasurementAIPS(path));
         }
         catch(Exception &e) {
@@ -171,32 +170,6 @@ namespace LOFAR
           return false;
         }
 
-//       if(loggingLevel!="NONE")	// If no parmDBLogging is set, skip the initialization
-//       {
-//			try {
-//				// Open ParmDBLog ParmDB for solver logging
-//				LOG_INFO_STR("Solver log table: " << solverDb);
-//				LOG_INFO_STR("Solver logging level: " << loggingLevel);
-//
-//				// Depending on value read from parset file for logging level call constructor
-//				// with the corresponding enum value
-//				//
-//				if(loggingLevel=="PERSOLUTION")
-//					itsParmLogger.reset(new ParmDBLog(solverDb, ParmDBLog::PERSOLUTION));
-//				if(loggingLevel=="PERSOLUTION_CORRMATRIX")
-//					itsParmLogger.reset(new ParmDBLog(solverDb, ParmDBLog::PERSOLUTION_CORRMATRIX));
-//				if(loggingLevel=="PERITERATION")
-//					itsParmLogger.reset(new ParmDBLog(solverDb, ParmDBLog::PERITERATION));
-//				if(loggingLevel=="PERITERATION_CORRMATRIX")
-//					itsParmLogger.reset(new ParmDBLog(solverDb, ParmDBLog::PERITERATION_CORRMATRIX));
-//			}
-//			  catch(Exception &e) {
-//				 LOG_ERROR_STR("Failed to open instrument model parameter database: "
-//					<< instrumentDb);
-//				 return false;
-//			  }
-//		  }
-
         string key = ps->getString("BBDB.Key", "default");
         itsCalSession.reset(new CalSession(key,
           ps->getString("BBDB.Name"),
@@ -205,12 +178,6 @@ namespace LOFAR
           ps->getString("BBDB.Host", "localhost"),
           ps->getString("BBDB.Port", "")));
 
-
-        // Get the GlobalParameterSet and write it into the MS/History table
-        ParameterSet parset = itsCalSession->getParset();
-        //LOG_DEBUG_STR("KernelProcessControl::init() Parset = " << parset);
-        itsMeasurement->writeHistory(parset);
-
         // Poll until Control is ready to accept workers.
         while(itsCalSession->getState() == CalSession::WAITING_FOR_CONTROL) {
           sleep(3);
@@ -219,20 +186,22 @@ namespace LOFAR
         // Try to register as kernel.
         if(!itsCalSession->registerAsKernel(filesys, path,
           itsMeasurement->grid())) {
-          LOG_ERROR_STR("Could not register as kernel. There may be stale"
-            " state in the database for key: " << key);
-          //LOG_ERROR("Registration denied.");
+          LOG_ERROR_STR("Could not register as kernel. There may be stale state"
+            " in the database for key: " << key);
           return false;
         }
-
         LOG_INFO_STR("Registration OK.");
+
+        // Get the global ParameterSet and write it into the HISTORY table.
+        itsMeasurement->writeHistory(itsCalSession->getParset());
+
         setState(RUN);
       }
       catch(Exception& e) {
         LOG_ERROR_STR(e);
         return false;
       }
-           
+
       return true;
     }
 
@@ -911,7 +880,13 @@ namespace LOFAR
           command.rmsThreshold().end());
         options.setEpsilon(command.epsilon().begin(), command.epsilon().end());
 
-        estimate(itsChunk, blMask, crMask, model, solGrid,
+        // Open solution log.
+        casa::Path path(itsPath);
+        path.append(command.logName());
+        ParmDBLog log(path.absoluteName(),
+          ParmDBLoglevel(command.logLevel()).get(), itsChunkCount != 0);
+
+        estimate(log, itsChunk, blMask, crMask, model, solGrid,
           ParmManager::instance().makeSubset(command.parms(),
           command.exclParms(), model->parms()), options);
       }
diff --git a/CEP/Calibration/BBSControl/src/SolveStep.cc b/CEP/Calibration/BBSControl/src/SolveStep.cc
index b5585dab1f8f69617f17317c585ee944ac42c648..2af235b8ce768ce5775ea2f49b6c35be53e55828 100644
--- a/CEP/Calibration/BBSControl/src/SolveStep.cc
+++ b/CEP/Calibration/BBSControl/src/SolveStep.cc
@@ -91,11 +91,18 @@ namespace LOFAR
           << endl << indent << itsCellSize
           << endl << indent << "Cell chunk size: " << itsCellChunkSize
           << endl << indent << "Propagate solutions: " << boolalpha
-          << itsPropagateFlag << noboolalpha
-          << endl << indent << "Flag on UV interval: " << boolalpha << itsUVFlag
-          << noboolalpha;
-        if(itsUVFlag)
+          << itsPropagateFlag << noboolalpha;
+
+        os << endl << indent << "Log:";
         {
+          Indent id;
+          os << endl << indent << "Name: " << itsLogName
+            << endl << indent << "Level: " << itsLogLevel;
+        }
+
+        os << endl << indent << "Flag on UV interval: " << boolalpha
+          << itsUVFlag << noboolalpha;
+        if(itsUVFlag) {
           Indent id;
           os << endl << indent << "UV interval: [" << itsUVRange.first
             << "," << itsUVRange.second << "] (wavelenghts)";
@@ -103,8 +110,7 @@ namespace LOFAR
 
         os << endl << indent << "Outlier rejection: " << boolalpha
           << itsRejectFlag << noboolalpha;
-        if(itsRejectFlag)
-        {
+        if(itsRejectFlag) {
           Indent id;
           os << endl << indent << "RMS threshold: "
             << itsRMSThreshold;
@@ -112,8 +118,7 @@ namespace LOFAR
 
         os << endl << indent << "Resample observed data: " << boolalpha
           << itsResampleFlag << noboolalpha;
-        if(itsResampleFlag)
-        {
+        if(itsResampleFlag) {
           Indent id;
           os << endl << indent << "Resample cell size: ";
           {
@@ -127,8 +132,7 @@ namespace LOFAR
 
         os << endl << indent << "Phase shift observed data: " << boolalpha
           << itsShiftFlag << noboolalpha;
-        if(itsShiftFlag)
-        {
+        if(itsShiftFlag) {
           Indent id;
           os << endl << indent << "Direction: " << itsDirectionASCII;
         }
@@ -180,6 +184,9 @@ namespace LOFAR
       ps.replace(prefix + "CellSize.Time", toString(itsCellSize.time));
       ps.replace(prefix + "CellChunkSize", toString(itsCellChunkSize));
       ps.replace(prefix + "PropagateSolutions", toString(itsPropagateFlag));
+      ps.replace(prefix + "Log.Name", itsLogName);
+      ps.replace(prefix + "Log.Level", itsLogLevel);
+
       if(itsUVFlag)
       {
         ps.replace(prefix + "UVRange", "[" + toString(itsUVRange.first)
@@ -240,6 +247,9 @@ namespace LOFAR
       itsCellSize.time = pss.getUint32("CellSize.Time");
       itsCellChunkSize = pss.getUint32("CellChunkSize");
       itsPropagateFlag = pss.getBool("PropagateSolutions", false);
+      itsLogName = pss.getString("Log.Name", "solver_log");
+      itsLogLevel = pss.getString("Log.Level", "NONE");
+
       setUVRange(pss);
 
       itsRejectFlag = pss.getBool("OutlierRejection.Enable", false);
diff --git a/CEP/Calibration/BBSKernel/include/BBSKernel/Estimate.h b/CEP/Calibration/BBSKernel/include/BBSKernel/Estimate.h
index 8e45203dbef138a2b83c57ee9aaaf6433562c99f..13945da725a277e15d21a7219c2bf2faec95aa6e 100644
--- a/CEP/Calibration/BBSKernel/include/BBSKernel/Estimate.h
+++ b/CEP/Calibration/BBSKernel/include/BBSKernel/Estimate.h
@@ -35,6 +35,8 @@
 //# For the definition of SolverOptions...
 #include <BBSKernel/Solver.h>
 
+#include <ParmDB/ParmDBLog.h>
+
 namespace LOFAR
 {
 namespace BBS
@@ -134,7 +136,8 @@ private:
 // the visibilities simulated using \p model. An independent solution is
 // computed for each cell in \p grid. Only those baselines and correlations
 // selected in \p baselines and \p correlations will be used.
-void estimate(const VisBuffer::Ptr &buffer,
+void estimate(ParmDBLog &log,
+    const VisBuffer::Ptr &buffer,
     const BaselineMask &baselines,
     const CorrelationMask &correlations,
     const MeasurementExpr::Ptr &model,
diff --git a/CEP/Calibration/BBSKernel/include/BBSKernel/Measurement.h b/CEP/Calibration/BBSKernel/include/BBSKernel/Measurement.h
index 1d9e28ca7e4faf2bdaafe5a9e6a3184ca2ce9ac1..54a29395acd539f49d8bbb3a45610dcb7d57e180 100644
--- a/CEP/Calibration/BBSKernel/include/BBSKernel/Measurement.h
+++ b/CEP/Calibration/BBSKernel/include/BBSKernel/Measurement.h
@@ -60,6 +60,8 @@ public:
         bool writeCovariance = false,
         bool writeFlags = true, flag_t flagMask = ~flag_t(0)) = 0;
 
+    virtual void writeHistory(const ParameterSet &parset) const = 0;
+
     // Apply the given filter to the baselines contained in the Measurement and
     // return the result as a (boolean) baseline mask.
     virtual BaselineMask asMask(const string &filter) const = 0;
@@ -70,8 +72,6 @@ public:
     Instrument::ConstPtr instrument() const;
     const VisDimensions &dimensions() const;
 
-    virtual void writeHistory(ParameterSet &parset) const = 0;
-
     // Convenience functions that delegate to VisDimensions (refer to the
     // documentation of VisDimensions for their documentation).
     // @{
diff --git a/CEP/Calibration/BBSKernel/include/BBSKernel/MeasurementAIPS.h b/CEP/Calibration/BBSKernel/include/BBSKernel/MeasurementAIPS.h
index a90ee8135df15a7164e98fcbab921f40664e4a32..e8278ac7d9c9e69be0aa666acd169304ff23c0e8 100644
--- a/CEP/Calibration/BBSKernel/include/BBSKernel/MeasurementAIPS.h
+++ b/CEP/Calibration/BBSKernel/include/BBSKernel/MeasurementAIPS.h
@@ -65,10 +65,10 @@ public:
         const string &column = "CORRECTED_DATA", bool writeCovariance = false,
         bool writeFlags = true, flag_t flagMask = ~flag_t(0));
 
+    virtual void writeHistory(const ParameterSet &parset) const;
+
     virtual BaselineMask asMask(const string &filter) const;
-    
-    //void writeHistory(ParameterSet &parset) const;
-    
+
     // @}
 
 private:
@@ -109,8 +109,6 @@ private:
     VisDimensions getDimensionsImpl(const casa::Table &tab_selection,
         const casa::Slicer &slicer) const;
 
-    void writeHistory(ParameterSet &parset) const;
-
     casa::MeasurementSet    itsMS;
     casa::Table             itsMainTableView;
 
diff --git a/CEP/Calibration/BBSKernel/src/Estimate.cc b/CEP/Calibration/BBSKernel/src/Estimate.cc
index 168f4b2ed82781597a83ec42d46843bfd6e4ed0a..7ccccc6b83e8bc3946283d5e4bdc840c86e9deec 100644
--- a/CEP/Calibration/BBSKernel/src/Estimate.cc
+++ b/CEP/Calibration/BBSKernel/src/Estimate.cc
@@ -90,6 +90,9 @@ namespace
     // State kept for a single cell in the solution grid.
     struct Cell
     {
+        // Flag that indicates if processing has completed.
+        bool            done;
+
         // LSQ solver and current estimates for the coefficients.
         casa::LSQFit    solver;
         vector<double>  coeff;
@@ -200,13 +203,29 @@ namespace
     // \pre The range starting at \p cell should contain exactly one Cell
     // instance for each cell in the range [\p start, \p end].
     template <typename T>
-    IterationStatus iterate(const Location &start, const Location &end,
-        const ParmGroup &solvables, const EstimateOptions &options, T cell);
+    IterationStatus iterate(ParmDBLog &log, const Grid &grid,
+        const Location &start, const Location &end, const ParmGroup &solvables,
+        const EstimateOptions &options, T cell);
+
+    // Decode casa::LSQFit ready codes and update the status counts.
+    void updateIterationStatus(const Cell &cell, IterationStatus &status);
+
+    // Write a map that maps parameter names to positions in the solution vector
+    // to the solver log. This is necessary to be able to correctly interpret
+    // the solution vectors in the log afterwards.
+    void logCoefficientIndex(ParmDBLog &log, const ParmGroup &solvables);
+
+    // Write the configuration of the least squares solver to the solver log.
+    void logLSQOptions(ParmDBLog &log, const EstimateOptions &options);
+
+    // Log solver statistics for the given cell.
+    void logCellStats(ParmDBLog &log, const Box &box, Cell &cell);
 
     // Function that does the actual iteration over chunk of cells and can be
     // specialised using \p T_CELL_PROCESSOR.
     template <typename T_CELL_PROCESSOR>
-    void estimateImpl(const VisBuffer::Ptr &buffer,
+    void estimateImpl(ParmDBLog &log,
+        const VisBuffer::Ptr &buffer,
         const BaselineMask &baselines,
         const CorrelationMask &correlations,
         const MeasurementExpr::Ptr &model,
@@ -215,7 +234,8 @@ namespace
         const EstimateOptions &options);
 } //# anonymous namespace
 
-void estimate(const VisBuffer::Ptr &buffer,
+void estimate(ParmDBLog &log,
+    const VisBuffer::Ptr &buffer,
     const BaselineMask &baselines,
     const CorrelationMask &correlations,
     const MeasurementExpr::Ptr &model,
@@ -230,18 +250,18 @@ void estimate(const VisBuffer::Ptr &buffer,
             {
                 case EstimateOptions::AMPLITUDE:
                     estimateImpl<CellProcessorReal<SampleModifierAmplitude,
-                        WeightModifierL1> >(buffer, baselines, correlations,
-                        model, grid, solvables, options);
+                        WeightModifierL1> >(log, buffer, baselines,
+                        correlations, model, grid, solvables, options);
                     break;
                 case EstimateOptions::PHASE:
                     estimateImpl<CellProcessorComplex<SampleModifierPhase,
-                        WeightModifierL1> >(buffer, baselines, correlations,
-                        model, grid, solvables, options);
+                        WeightModifierL1> >(log, buffer, baselines,
+                        correlations, model, grid, solvables, options);
                     break;
                 case EstimateOptions::COMPLEX:
                     estimateImpl<CellProcessorComplex<SampleModifierComplex,
-                        WeightModifierL1> >(buffer, baselines, correlations,
-                        model, grid, solvables, options);
+                        WeightModifierL1> >(log, buffer, baselines,
+                        correlations, model, grid, solvables, options);
                     break;
                 default:
                     THROW(BBSKernelException, "Invalid mode selected.");
@@ -253,18 +273,18 @@ void estimate(const VisBuffer::Ptr &buffer,
             {
                 case EstimateOptions::AMPLITUDE:
                     estimateImpl<CellProcessorReal<SampleModifierAmplitude,
-                        WeightModifierL2> >(buffer, baselines, correlations,
-                        model, grid, solvables, options);
+                        WeightModifierL2> >(log, buffer, baselines,
+                        correlations, model, grid, solvables, options);
                     break;
                 case EstimateOptions::PHASE:
                     estimateImpl<CellProcessorComplex<SampleModifierPhase,
-                        WeightModifierL2> >(buffer, baselines, correlations,
-                        model, grid, solvables, options);
+                        WeightModifierL2> >(log, buffer, baselines,
+                        correlations, model, grid, solvables, options);
                     break;
                 case EstimateOptions::COMPLEX:
                     estimateImpl<CellProcessorComplex<SampleModifierComplex,
-                        WeightModifierL2> >(buffer, baselines, correlations,
-                        model, grid, solvables, options);
+                        WeightModifierL2> >(log, buffer, baselines,
+                        correlations, model, grid, solvables, options);
                     break;
                 default:
                     THROW(BBSKernelException, "Invalid mode selected.");
@@ -438,7 +458,8 @@ const string &EstimateOptions::asString(Algorithm in)
 namespace
 {
     template <typename T_CELL_PROCESSOR>
-    void estimateImpl(const VisBuffer::Ptr &buffer,
+    void estimateImpl(ParmDBLog &log,
+        const VisBuffer::Ptr &buffer,
         const BaselineMask &baselines,
         const CorrelationMask &correlations,
         const MeasurementExpr::Ptr &model,
@@ -489,6 +510,10 @@ namespace
         // Assign solution grid to solvables.
         ParmManager::instance().setGrid(grid, solvables);
 
+        // Log information that holds for all chunks.
+        logCoefficientIndex(log, solvables);
+        logLSQOptions(log, options);
+
         // ---------------------------------------------------------------------
         // Process each chunk of cells in a loop.
         // ---------------------------------------------------------------------
@@ -578,8 +603,8 @@ namespace
 
                 // Perform a single iteration.
                 timerIterate.start();
-                status = iterate(chunkStart, chunkEnd, solvables, options,
-                    cells.begin());
+                status = iterate(log, grid, chunkStart, chunkEnd, solvables,
+                    options, cells.begin());
                 timerIterate.stop();
 
                 // Notify model that solvables have changed.
@@ -658,31 +683,28 @@ namespace
     }
 
     template <typename T>
-    IterationStatus iterate(const Location &start, const Location &end,
-        const ParmGroup &solvables, const EstimateOptions &options, T cell)
+    IterationStatus iterate(ParmDBLog &log, const Grid &grid,
+        const Location &start, const Location &end, const ParmGroup &solvables,
+        const EstimateOptions &options, T cell)
     {
-//        LOG_DEBUG_STR("================================================================================" << endl);
-
         IterationStatus status = {0, 0, 0, 0, 0};
         for(CellIterator it(start, end); !it.atEnd(); ++it, ++cell)
         {
+            // If processing on the cell is already done, only update the status
+            // counts and continue to the next cell.
+            if(cell->done)
+            {
+                updateIterationStatus(*cell, status);
+                continue;
+            }
+
             // Compute RMS.
             if(cell->count > 0)
             {
                 cell->rms = sqrt(cell->rms / cell->count);
             }
 
-//            if(!cell->done)
-//            {
-//                LOG_DEBUG_STR("cell: (" << it->first << "," << it->second
-//                    << ") rms: " << cell->rms << " count: " << cell->count
-//                    << " flag: " << cell->flag << " outliers: " << cell->outliers
-//                    << " threshold: " << cell->threshold << " ("
-//                    << cell->thresholdIdx << ") epsilon: " << cell->epsilon << " ("
-//                    << cell->epsilonIdx << ")");
-//            }
-
-            // Turn outlier detection of by default. May be enabled later on.
+            // Turn outlier detection off by default. May be enabled later on.
             cell->flag = false;
 
             // Perform a single iteration if the cell has not yet converged or
@@ -747,54 +769,137 @@ namespace
                  ++cell->thresholdIdx;
             }
 
-            // Decode and record the solver status.
-            switch(cell->solver.isReady())
+            // Log solution statistics.
+            if(!options.robust()
+                && (options.algorithm() == EstimateOptions::L2))
             {
-                case casa::LSQFit::NONREADY:
-                    ++status.nActive;
-                    break;
-
-                case casa::LSQFit::SOLINCREMENT:
-                case casa::LSQFit::DERIVLEVEL:
-                    ++status.nConverged;
-                    break;
-
-                case casa::LSQFit::MAXITER:
-                    ++status.nStopped;
-                    break;
-
-                case casa::LSQFit::NOREDUCTION:
-                    ++status.nNoReduction;
-                    break;
-
-                case casa::LSQFit::SINGULAR:
-                    ++status.nSingular;
-                    break;
-
-                default:
-                    // This assert triggers if an casa::LSQFit ready
-                    // code is encountered that is not covered above.
-                    // The most likely cause is that the
-                    // casa::LSQFit::ReadyCode enumeration has changes
-                    // in which case the code above needs to be changed
-                    // accordingly.
-                    ASSERT(false);
-                    break;
+                logCellStats(log, grid.getCell(*it), *cell);
             }
 
-            // If not yet converged or failed, reset state for the next
-            // iteration.
-            if(!cell->solver.isReady())
+            if(cell->solver.isReady())
             {
+                cell->done = true;
+            }
+            else
+            {
+                // If not yet converged or failed, reset state for the next
+                // iteration.
                 cell->rms = 0.0;
                 cell->count = 0;
                 cell->outliers = 0;
             }
+
+            updateIterationStatus(*cell, status);
         }
 
         return status;
     }
 
+    void updateIterationStatus(const Cell &cell, IterationStatus &status)
+    {
+        // casa::LSQFit::isReady() is incorrectly labelled non-const.
+        casa::LSQFit &solver = const_cast<casa::LSQFit&>(cell.solver);
+
+        // Decode and record the solver status.
+        switch(solver.isReady())
+        {
+            case casa::LSQFit::NONREADY:
+                ++status.nActive;
+                break;
+
+            case casa::LSQFit::SOLINCREMENT:
+            case casa::LSQFit::DERIVLEVEL:
+                ++status.nConverged;
+                break;
+
+            case casa::LSQFit::MAXITER:
+                ++status.nStopped;
+                break;
+
+            case casa::LSQFit::NOREDUCTION:
+                ++status.nNoReduction;
+                break;
+
+            case casa::LSQFit::SINGULAR:
+                ++status.nSingular;
+                break;
+
+            default:
+                // This assert triggers if an casa::LSQFit ready
+                // code is encountered that is not covered above.
+                // The most likely cause is that the
+                // casa::LSQFit::ReadyCode enumeration has changes
+                // in which case the code above needs to be changed
+                // accordingly.
+                ASSERT(false);
+                break;
+        }
+    }
+
+    void logCoefficientIndex(ParmDBLog &log, const ParmGroup &solvables)
+    {
+        CoeffIndex index;
+        for(ParmGroup::const_iterator it = solvables.begin(),
+            end = solvables.end(); it != end; ++it)
+        {
+            ParmProxy::Ptr parm = ParmManager::instance().get(*it);
+            index.insert(parm->getName(), parm->getCoeffCount());
+        }
+
+        log.addParmKeywords(index);
+    }
+
+    void logLSQOptions(ParmDBLog &log, const EstimateOptions &options)
+    {
+        log.addSolverKeywords(options.lsqOptions());
+    }
+
+    void logCellStats(ParmDBLog &log, const Box &box, Cell &cell)
+    {
+        const ParmDBLoglevel level = log.getLoggingLevel();
+        if(level.is(ParmDBLoglevel::NONE))
+        {
+            return;
+        }
+
+        // Get some statistics from the solver. Note that the chi squared is
+        // valid for the _previous_ iteration. The solver cannot compute the chi
+        // squared directly after an iteration, because it needs the new
+        // condition equations for that and these are computed by the kernel.
+        casa::uInt rank, nun, np, ncon, ner, *piv;
+        casa::Double *nEq, *known, *constr, *er, *sEq, *sol, prec, nonlin;
+        cell.solver.debugIt(nun, np, ncon, ner, rank, nEq, known, constr, er,
+            piv, sEq, sol, prec, nonlin);
+
+        if(level.is(ParmDBLoglevel::PERITERATION)
+            || (!cell.solver.isReady()
+            && level.is(ParmDBLoglevel::PERITERATION_CORRMATRIX))
+            || (cell.solver.isReady() && level.is(ParmDBLoglevel::PERSOLUTION)))
+        {
+        	log.add(box.lower().first, box.upper().first, box.lower().second,
+                box.upper().second, cell.solver.nIterations(), 0,
+                cell.solver.isReady(), rank, cell.solver.getDeficiency(),
+                cell.solver.getChi(), nonlin, cell.coeff,
+                cell.solver.readyText());
+        }
+        else if(cell.solver.isReady()
+            && (level.is(ParmDBLoglevel::PERSOLUTION_CORRMATRIX)
+            || level.is(ParmDBLoglevel::PERITERATION_CORRMATRIX)))
+        {
+            casa::Array<double> corrMatrix(casa::IPosition(1,
+                cell.solver.nUnknowns() * cell.solver.nUnknowns()));
+
+            bool status = cell.solver.getCovariance(corrMatrix.data());
+            ASSERT(status);
+
+            log.add(box.lower().first, box.upper().first, box.lower().second,
+                box.upper().second, cell.solver.nIterations(), 0,
+                cell.solver.isReady(), rank, cell.solver.getDeficiency(),
+                cell.solver.getChi(), nonlin, cell.coeff,
+                cell.solver.readyText(), corrMatrix);
+        }
+    }
+
     template <typename T>
     void initCells(const Location &start, const Location &end,
         const ParmGroup &solvables, size_t nCoeff,
@@ -802,6 +907,9 @@ namespace
     {
         for(CellIterator it(start, end); !it.atEnd(); ++it, ++cell)
         {
+            // Processing has not completed yet.
+            cell->done = false;
+
             // Initalize LSQ solver.
             cell->solver = casa::LSQFit(static_cast<casa::uInt>(nCoeff));
             configLSQSolver(cell->solver, options.lsqOptions());
diff --git a/CEP/Calibration/BBSKernel/src/MeasurementAIPS.cc b/CEP/Calibration/BBSKernel/src/MeasurementAIPS.cc
index 11d0a79ecf9db58ae4cdc1e030d18c741006a98c..f7a13365b56c60ba0ada36adb7ed8a3e0eef1eaf 100644
--- a/CEP/Calibration/BBSKernel/src/MeasurementAIPS.cc
+++ b/CEP/Calibration/BBSKernel/src/MeasurementAIPS.cc
@@ -120,54 +120,6 @@ VisDimensions MeasurementAIPS::dimensions(const VisSelection &selection)
         getCellSlicer(selection));
 }
 
-
-void MeasurementAIPS::writeHistory(ParameterSet &parset) const
-{
-  Table histtab(itsMS.keywordSet().asTable("HISTORY"));
-  histtab.reopenRW();
-  ScalarColumn<double> time        (histtab, "TIME");
-  ScalarColumn<int>    obsId       (histtab, "OBSERVATION_ID");
-  ScalarColumn<String> message     (histtab, "MESSAGE");
-  ScalarColumn<String> application (histtab, "APPLICATION");
-  ScalarColumn<String> priority    (histtab, "PRIORITY");
-  ScalarColumn<String> origin      (histtab, "ORIGIN");
-  ArrayColumn<String>  parms       (histtab, "APP_PARAMS");
-  ArrayColumn<String>  cli         (histtab, "CLI_COMMAND");
-  // Put all parset entries in a Vector<String>.
-  // Some WSRT MSs have a FixedShape APP_PARAMS and CLI_COMMAND column.
-  // For them, put all params in a single vector element (with newlines).
-  bool fixedShaped = 
-    (parms.columnDesc().options() & ColumnDesc::FixedShape) != 0;
-  Vector<String> appvec;
-  Vector<String> clivec;
-  if (fixedShaped) {
-    appvec.resize(1);
-    clivec.resize(1);
-    ostringstream ostr;
-    parset.writeStream (ostr);
-    appvec[0] = ostr.str();
-  } else {
-    appvec.resize (parset.size());
-    Array<String>::contiter viter = appvec.cbegin();
-    for (ParameterSet::const_iterator iter = parset.begin();
-         iter != parset.end(); ++iter, ++viter) {
-      *viter = iter->first + '=' + iter->second.get();
-    }
-  }
-  
-  uint rownr = histtab.nrow();
-  histtab.addRow();
-  time.put        (rownr, Time().modifiedJulianDay()*24.*3600.);
-  obsId.put       (rownr, 0);
-  message.put     (rownr, "parameters");
-  application.put (rownr, "BBS");
-  priority.put    (rownr, "NORMAL");
-  origin.put      (rownr, Version::getInfo<BBSKernelVersion>("BBS", "other"));
-  parms.put       (rownr, appvec);
-  cli.put         (rownr, clivec);    
-}
-
-
 VisBuffer::Ptr MeasurementAIPS::read(const VisSelection &selection,
     const string &column) const
 {
@@ -507,7 +459,6 @@ void MeasurementAIPS::write(VisBuffer::Ptr buffer,
 
                 c_covariance.putSlice(i, slicerCov, covariance);
             }
-            
         }
         writeTimer.stop();
 
@@ -529,6 +480,57 @@ void MeasurementAIPS::write(VisBuffer::Ptr buffer,
     LOG_DEBUG_STR("Write time: " << writeTimer);
 }
 
+void MeasurementAIPS::writeHistory(const ParameterSet &parset) const
+{
+    Table histtab = getSubTable("HISTORY");
+    histtab.reopenRW();
+
+    ScalarColumn<double> time       (histtab, "TIME");
+    ScalarColumn<int>    obsId      (histtab, "OBSERVATION_ID");
+    ScalarColumn<String> message    (histtab, "MESSAGE");
+    ScalarColumn<String> application(histtab, "APPLICATION");
+    ScalarColumn<String> priority   (histtab, "PRIORITY");
+    ScalarColumn<String> origin     (histtab, "ORIGIN");
+    ArrayColumn<String>  parms      (histtab, "APP_PARAMS");
+    ArrayColumn<String>  cli        (histtab, "CLI_COMMAND");
+
+    // Put all parset entries in a Vector<String>.
+    Vector<String> appvec;
+    Vector<String> clivec;
+    if(parms.columnDesc().isFixedShape())
+    {
+        // Some WSRT MSs have a FixedShape APP_PARAMS and CLI_COMMAND column.
+        // For them, put all params in a single vector element (with newlines).
+        appvec.resize(1);
+        clivec.resize(1);
+
+        ostringstream ostr;
+        parset.writeStream(ostr);
+        appvec[0] = ostr.str();
+    }
+    else
+    {
+        appvec.resize (parset.size());
+        Array<String>::contiter viter = appvec.cbegin();
+        for(ParameterSet::const_iterator iter = parset.begin();
+            iter != parset.end(); ++iter, ++viter)
+        {
+            *viter = iter->first + '=' + iter->second.get();
+        }
+    }
+
+    uint rownr = histtab.nrow();
+    histtab.addRow();
+    time.put       (rownr, Time().modifiedJulianDay() * 24.0 * 3600.0);
+    obsId.put      (rownr, itsIdObservation);
+    message.put    (rownr, "parameters");
+    application.put(rownr, "BBS");
+    priority.put   (rownr, "NORMAL");
+    origin.put     (rownr, Version::getInfo<BBSKernelVersion>("BBS", "other"));
+    parms.put      (rownr, appvec);
+    cli.put        (rownr, clivec);
+}
+
 BaselineMask MeasurementAIPS::asMask(const string &filter) const
 {
     // Find all unique baselines.