diff --git a/.gitattributes b/.gitattributes
index 330172a49cf8a441de3aab2ebff0777b476b7756..cb42d5500f666ff55664a04f7f732c72ea977eb8 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -300,7 +300,7 @@ CEP/DP3/DPPP/etc/DPPP.log_prop -text
 CEP/DP3/DPPP/include/DPPP/Apply.h -text
 CEP/DP3/DPPP/include/DPPP/ApplyBeam.h -text
 CEP/DP3/DPPP/include/DPPP/ApplyBeam.tcc -text
-CEP/DP3/DPPP/include/DPPP/ApplyCal.h -text svneol=unset#text/plain
+CEP/DP3/DPPP/include/DPPP/ApplyCal.h -text
 CEP/DP3/DPPP/include/DPPP/Baseline.h -text
 CEP/DP3/DPPP/include/DPPP/Cursor.h -text
 CEP/DP3/DPPP/include/DPPP/CursorUtilCasa.h -text
@@ -317,6 +317,7 @@ CEP/DP3/DPPP/include/DPPP/H5ParmPredict.h -text
 CEP/DP3/DPPP/include/DPPP/ModelComponent.h -text
 CEP/DP3/DPPP/include/DPPP/ModelComponentVisitor.h -text
 CEP/DP3/DPPP/include/DPPP/MultiMSReader.h -text
+CEP/DP3/DPPP/include/DPPP/OneApplyCal.h -text svneol=unset#text/plain
 CEP/DP3/DPPP/include/DPPP/Patch.h -text
 CEP/DP3/DPPP/include/DPPP/PhaseFitter.h -text
 CEP/DP3/DPPP/include/DPPP/PointSource.h -text
@@ -336,7 +337,7 @@ CEP/DP3/DPPP/share/HBAdefault -text
 CEP/DP3/DPPP/share/LBAdefault -text
 CEP/DP3/DPPP/src/Apply.cc -text
 CEP/DP3/DPPP/src/ApplyBeam.cc -text
-CEP/DP3/DPPP/src/ApplyCal.cc -text svneol=unset#text/plain
+CEP/DP3/DPPP/src/ApplyCal.cc -text
 CEP/DP3/DPPP/src/DemixInfo.cc -text
 CEP/DP3/DPPP/src/DemixWorker.cc -text
 CEP/DP3/DPPP/src/DemixerNew.cc -text
@@ -350,6 +351,7 @@ CEP/DP3/DPPP/src/H5ParmPredict.cc -text
 CEP/DP3/DPPP/src/ModelComponent.cc -text
 CEP/DP3/DPPP/src/ModelComponentVisitor.cc -text
 CEP/DP3/DPPP/src/MultiMSReader.cc -text
+CEP/DP3/DPPP/src/OneApplyCal.cc -text svneol=unset#text/plain
 CEP/DP3/DPPP/src/Patch.cc -text
 CEP/DP3/DPPP/src/PhaseFitter.cc -text
 CEP/DP3/DPPP/src/PointSource.cc -text
@@ -388,6 +390,8 @@ CEP/DP3/DPPP/test/tGainCal.tab.tgz -text
 CEP/DP3/DPPP/test/tGainCal_ref -text
 CEP/DP3/DPPP/test/tH5Parm.cc -text
 CEP/DP3/DPPP/test/tH5Parm.sh -text
+CEP/DP3/DPPP/test/tMultiApplyCal.run -text
+CEP/DP3/DPPP/test/tMultiApplyCal.sh -text
 CEP/DP3/DPPP/test/tNDPPP-generic.in_MS.tgz -text svneol=unset#application/x-gzip
 CEP/DP3/DPPP/test/tNDPPP.in_MS.tgz -text svneol=unset#application/x-compressed-tar
 CEP/DP3/DPPP/test/tPredict.run -text
diff --git a/CEP/DP3/DPPP/include/DPPP/ApplyCal.h b/CEP/DP3/DPPP/include/DPPP/ApplyCal.h
index e047a263b2378713310a719f268a75f6482c0f5f..cf458c442a8b585056348c7a58e617b5b733a981 100644
--- a/CEP/DP3/DPPP/include/DPPP/ApplyCal.h
+++ b/CEP/DP3/DPPP/include/DPPP/ApplyCal.h
@@ -1,4 +1,4 @@
-//# ApplyCal.h: DPPP step class to apply a calibration correction to the data
+//# ApplyCal.h: DPPP step class to ApplyCal visibilities from a source model
 //# Copyright (C) 2013
 //# ASTRON (Netherlands Institute for Radio Astronomy)
 //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
@@ -17,44 +17,43 @@
 //# You should have received a copy of the GNU General Public License along
 //# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
 //#
-//# $Id: ApplyCal.h 21598 2012-07-16 08:07:34Z diepen $
+//# $Id:
 //#
 //# @author Tammo Jan Dijkema
 
-#ifndef DPPP_APPLYCAL_H
-#define DPPP_APPLYCAL_H
+#ifndef DPPP_ApplyCal_H
+#define DPPP_ApplyCal_H
 
 // @file
-// @brief DPPP step class to apply a calibration correction to the data
+// @brief DPPP step class to apply multiple calibration solutions
 
 #include <DPPP/DPInput.h>
 #include <DPPP/DPBuffer.h>
-#include <DPPP/H5Parm.h>
-#include <ParmDB/ParmFacade.h>
-#include <ParmDB/ParmSet.h>
-#include <ParmDB/Parm.h>
-#include <casacore/casa/Arrays/Cube.h>
-#include <casacore/casa/Arrays/ArrayMath.h>
-#include <DPPP/FlagCounter.h>
+
+#include <DPPP/OneApplyCal.h>
+
+#include <utility>
 
 namespace LOFAR {
+
+  class ParameterSet;
+
   namespace DPPP {
     // @ingroup NDPPP
 
-    // This class is a DPStep class applying calibration parameters to the data.
+    // This class is a DPStep class to apply multiple ParmDB or H5Parm
+    // solutions to data.
+
     class ApplyCal: public DPStep
     {
     public:
-
-      enum CorrectType {GAIN, FULLJONES, TEC, CLOCK, ROTATIONANGLE, SCALARPHASE, PHASE,
-                        ROTATIONMEASURE, SCALARAMPLITUDE, AMPLITUDE};
-
       // Construct the object.
       // Parameters are obtained from the parset using the given prefix.
       ApplyCal (DPInput*, const ParameterSet&, const string& prefix,
                 bool substep=false, std::string predictDirection="");
 
-      ApplyCal();
+      // Empty constructor
+      ApplyCal ();
 
       virtual ~ApplyCal();
 
@@ -66,19 +65,20 @@ namespace LOFAR {
       // Finish the processing of this step and subsequent steps.
       virtual void finish();
 
-      // Update the general info.
-      virtual void updateInfo (const DPInfo&);
+      // Set the next step. It squeezes in the actual OneApplyCal steps
+      // between this ApplyCal step and the next step.
+      virtual void setNextStep (DPStep::ShPtr nextStep);
 
-      // Show the step parameters.
-      virtual void show (std::ostream&) const;
+      // Show the step. When ApplyCal is a step in the main chain, this does
+      // nothing; the nextStep mechanism in DPRun will call show on the actual
+      // OneApplyCals.
+      virtual void show(std::ostream&) const;
 
-      // Show the timings.
+      // Show the timings. When ApplyCal is a step in the main chain, this does
+      // nothing; the nextStep mechanism in DPRun will call show on the actual
+      // OneApplyCals.
       virtual void showTimings (std::ostream&, double duration) const;
 
-      bool invert() {
-        return itsInvert;
-      }
-
       // Invert a 2x2 matrix in place
       static void invert (casacore::DComplex* v, double sigmaMMSE=0);
 
@@ -117,50 +117,11 @@ namespace LOFAR {
                                 float* weight);
 
     private:
-      // Read parameters from the associated parmdb and store them in itsParms
-      void updateParms (const double bufStartTime);
-
-      // If needed, show the flag counts.
-      virtual void showCounts (std::ostream&) const;
-
-      void initDataArrays();
-
-      // Check the number of polarizations in the parmdb or h5parm
-      uint nPol(const std::string& parmName);
-
-      static std::string correctTypeToString(CorrectType);
-      static CorrectType stringToCorrectType(const string&);
-
       //# Data members.
-      DPInput*         itsInput;
-      DPBuffer         itsBuffer;
-      string      itsName;
-      string      itsParmDBName;
-      bool             itsUseH5Parm;
-      boost::shared_ptr<BBS::ParmFacade> itsParmDB;
-      H5Parm           itsH5Parm;
-      H5Parm::SolTab   itsSolTab;
-      CorrectType      itsCorrectType;
-      bool             itsInvert;
-      uint             itsTimeSlotsPerParmUpdate;
-      double           itsSigmaMMSE;
-      bool             itsUpdateWeights;
-
-      uint             itsCount; // number of steps
-
-      // Expressions to search for in itsParmDB
-      vector<casacore::String>   itsParmExprs;
-
-      // parameters, numparms, antennas, time x frequency
-      casacore::Cube<casacore::DComplex> itsParms;
-      uint            itsTimeStep; // time step within current chunk
-      uint            itsNCorr;
-      double          itsTimeInterval;
-      double          itsLastTime; // last time of current chunk
-      FlagCounter     itsFlagCounter;
-      bool            itsUseAP;      //# use ampl/phase or real/imag
-      hsize_t         itsDirection;
-      NSTimer         itsTimer;
+      bool             itsIsSubstep;
+      string           itsName;
+
+      std::vector<OneApplyCal::ShPtr> itsApplyCals;
     };
 
   } //# end namespace
diff --git a/CEP/DP3/DPPP/include/DPPP/CMakeLists.txt b/CEP/DP3/DPPP/include/DPPP/CMakeLists.txt
index 12ddf1a6b3e06286bcff33209af6b3987cd08b1a..ce149ffe8b6b2b4fa133a2a55ae2164c466d0cc1 100644
--- a/CEP/DP3/DPPP/include/DPPP/CMakeLists.txt
+++ b/CEP/DP3/DPPP/include/DPPP/CMakeLists.txt
@@ -15,7 +15,7 @@ set(inst_HEADERS
   ModelComponentVisitor.h
   DemixerNew.h DemixInfo.h DemixWorker.h
   ApplyBeam.h ApplyBeam.tcc
-  Predict.h
+  Predict.h OneApplyCal.h
   GainCal.h StefCal.h PhaseFitter.h
   StManParsetKeys.h H5Parm.h DummyStep.h H5ParmPredict.h
 )
diff --git a/CEP/DP3/DPPP/include/DPPP/DPStep.h b/CEP/DP3/DPPP/include/DPPP/DPStep.h
index 5bb207d2bb0ee4d7eab620fa18abfe121a58b44e..981197b47960df5a71adec51b1d890491ca0991b 100644
--- a/CEP/DP3/DPPP/include/DPPP/DPStep.h
+++ b/CEP/DP3/DPPP/include/DPPP/DPStep.h
@@ -116,7 +116,7 @@ namespace LOFAR {
       { return itsPrevStep; }
 
       // Set the next step.
-      void setNextStep (DPStep::ShPtr nextStep)
+      virtual void setNextStep (DPStep::ShPtr nextStep)
         { itsNextStep = nextStep;
           nextStep->setPrevStep(this);
         }
@@ -129,11 +129,11 @@ namespace LOFAR {
       DPInfo& info()
         { return itsInfo; }
 
-    private:
       // Update the general info (called by setInfo).
       // The default implementation copies the info.
       virtual void updateInfo (const DPInfo&);
 
+    private:
       //# Data members.
       DPStep::ShPtr itsNextStep;
       DPStep*       itsPrevStep; // Normal pointer for back links, prevent
diff --git a/CEP/DP3/DPPP/include/DPPP/DummyStep.h b/CEP/DP3/DPPP/include/DPPP/DummyStep.h
index a7bd04060868104cc12646345ac0fce60330e5e1..9611cc0b86ea7153a9b4f2b43f4f360e64f18124 100644
--- a/CEP/DP3/DPPP/include/DPPP/DummyStep.h
+++ b/CEP/DP3/DPPP/include/DPPP/DummyStep.h
@@ -29,16 +29,7 @@
 
 #include <DPPP/DPInput.h>
 #include <DPPP/DPBuffer.h>
-#include <DPPP/Patch.h>
-#include <DPPP/SourceDBUtil.h>
-#include <DPPP/ApplyBeam.h>
-#include <DPPP/ModelComponent.h>
-#include <StationResponse/Station.h>
-#include <StationResponse/Types.h>
-#include <casacore/casa/Arrays/Cube.h>
-#include <casacore/casa/Quanta/MVEpoch.h>
-#include <casacore/measures/Measures/MEpoch.h>
-#include <casacore/casa/Arrays/ArrayMath.h>
+
 #include <utility>
 
 namespace LOFAR {
@@ -48,10 +39,7 @@ namespace LOFAR {
   namespace DPPP {
     // @ingroup NDPPP
 
-    // This class is a DPStep class to DummyStep visibilities with optionally beam
-
-    typedef std::pair<size_t, size_t> Baseline;
-    typedef std::pair<ModelComponent::ConstPtr, Patch::ConstPtr> Source;
+    // This class is an empty DPStep subclass to use as implementation template
 
     class DummyStep: public DPStep
     {
diff --git a/CEP/DP3/DPPP/include/DPPP/H5ParmPredict.h b/CEP/DP3/DPPP/include/DPPP/H5ParmPredict.h
index a82968e6098fb4009de7882ddb94aecf7678b5dc..ad1a464583613167d05c7aac11033c1b7213d17d 100644
--- a/CEP/DP3/DPPP/include/DPPP/H5ParmPredict.h
+++ b/CEP/DP3/DPPP/include/DPPP/H5ParmPredict.h
@@ -30,6 +30,9 @@
 #include <DPPP/DPInput.h>
 #include <DPPP/DPBuffer.h>
 #include <DPPP/Predict.h>
+
+#include <DPPP/H5Parm.h>
+
 #include <utility>
 
 namespace LOFAR {
diff --git a/CEP/DP3/DPPP/include/DPPP/OneApplyCal.h b/CEP/DP3/DPPP/include/DPPP/OneApplyCal.h
new file mode 100644
index 0000000000000000000000000000000000000000..f7e87b7d0dd7af39181919e3574debc66e8d3d3a
--- /dev/null
+++ b/CEP/DP3/DPPP/include/DPPP/OneApplyCal.h
@@ -0,0 +1,135 @@
+//# OneApplyCal.h: DPPP step class to apply a calibration correction to the data
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite 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.
+//#
+//# The LOFAR software suite 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 the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id: OneApplyCal.h 21598 2012-07-16 08:07:34Z diepen $
+//#
+//# @author Tammo Jan Dijkema
+
+#ifndef DPPP_ONEAPPLYCAL_H
+#define DPPP_ONEAPPLYCAL_H
+
+// @file
+// @brief DPPP step class to apply a calibration correction to the data
+
+#include <DPPP/DPInput.h>
+#include <DPPP/DPBuffer.h>
+#include <DPPP/H5Parm.h>
+#include <ParmDB/ParmFacade.h>
+#include <ParmDB/ParmSet.h>
+#include <ParmDB/Parm.h>
+#include <casacore/casa/Arrays/Cube.h>
+#include <casacore/casa/Arrays/ArrayMath.h>
+#include <DPPP/FlagCounter.h>
+
+namespace LOFAR {
+  namespace DPPP {
+    // @ingroup NDPPP
+
+    // This class is a DPStep class applying calibration parameters to the data.
+    // It only applies one correction.
+
+    class OneApplyCal: public DPStep
+    {
+    public:
+      // Define the shared pointer for this type.
+      typedef shared_ptr<OneApplyCal> ShPtr;
+
+      enum CorrectType {GAIN, FULLJONES, TEC, CLOCK, ROTATIONANGLE, SCALARPHASE, PHASE,
+                        ROTATIONMEASURE, SCALARAMPLITUDE, AMPLITUDE};
+
+      // Construct the object.
+      // Parameters are obtained from the parset using the given prefix.
+      OneApplyCal (DPInput*, const ParameterSet&, const std::string& prefix,
+                const std::string& defaultPrefix,
+                bool substep=false, std::string predictDirection="");
+
+      virtual ~OneApplyCal();
+
+      // Process the data.
+      // It keeps the data.
+      // When processed, it invokes the process function of the next step.
+      virtual bool process (const DPBuffer&);
+
+      // Finish the processing of this step and subsequent steps.
+      virtual void finish();
+
+      // Update the general info.
+      virtual void updateInfo (const DPInfo&);
+
+      // Show the step parameters.
+      virtual void show (std::ostream&) const;
+
+      // Show the timings.
+      virtual void showTimings (std::ostream&, double duration) const;
+
+      bool invert() {
+        return itsInvert;
+      }
+
+    private:
+      // Read parameters from the associated parmdb and store them in itsParms
+      void updateParms (const double bufStartTime);
+
+      // If needed, show the flag counts.
+      virtual void showCounts (std::ostream&) const;
+
+      void initDataArrays();
+
+      // Check the number of polarizations in the parmdb or h5parm
+      uint nPol(const std::string& parmName);
+
+      static std::string correctTypeToString(CorrectType);
+      static CorrectType stringToCorrectType(const string&);
+
+      //# Data members.
+      DPInput*         itsInput;
+      DPBuffer         itsBuffer;
+      string           itsName;
+      string           itsParmDBName;
+      bool             itsUseH5Parm;
+      boost::shared_ptr<BBS::ParmFacade> itsParmDB;
+      H5Parm           itsH5Parm;
+      H5Parm::SolTab   itsSolTab;
+      CorrectType      itsCorrectType;
+      bool             itsInvert;
+      uint             itsTimeSlotsPerParmUpdate;
+      double           itsSigmaMMSE;
+      bool             itsUpdateWeights;
+
+      uint             itsCount; // number of steps
+
+      // Expressions to search for in itsParmDB
+      vector<casacore::String>   itsParmExprs;
+
+      // parameters, numparms, antennas, time x frequency
+      casacore::Cube<casacore::DComplex> itsParms;
+      uint            itsTimeStep; // time step within current chunk
+      uint            itsNCorr;
+      double          itsTimeInterval;
+      double          itsLastTime; // last time of current chunk
+      FlagCounter     itsFlagCounter;
+      bool            itsUseAP;      //# use ampl/phase or real/imag
+      hsize_t         itsDirection;
+      NSTimer         itsTimer;
+    };
+
+  } //# end namespace
+}
+
+#endif
diff --git a/CEP/DP3/DPPP/src/ApplyCal.cc b/CEP/DP3/DPPP/src/ApplyCal.cc
index 00b2f0fc5057463c07c82cd9b5a0975cae2e7d2e..2a41912f49a226abca37b94437f2864cbc717478 100644
--- a/CEP/DP3/DPPP/src/ApplyCal.cc
+++ b/CEP/DP3/DPPP/src/ApplyCal.cc
@@ -1,4 +1,4 @@
-//# ApplyCal.cc: DPPP step class to apply a calibration correction to the data
+//# GainCal.cc: DPPP step class to ApplyCal visibilities
 //# Copyright (C) 2013
 //# ASTRON (Netherlands Institute for Radio Astronomy)
 //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
@@ -17,671 +17,122 @@
 //# You should have received a copy of the GNU General Public License along
 //# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
 //#
-//# $Id: ApplyCal.cc 21598 2012-07-16 08:07:34Z diepen $
+//# $Id: GainCal.cc 21598 2012-07-16 08:07:34Z diepen $
 //#
 //# @author Tammo Jan Dijkema
 
 #include <lofar_config.h>
 #include <DPPP/ApplyCal.h>
-#include <DPPP/DPBuffer.h>
-#include <DPPP/DPInfo.h>
-#include <DPPP/MSReader.h>
-#include <Common/ParameterSet.h>
-#include <Common/StringUtil.h>
-#include <Common/LofarLogger.h>
-#include <casacore/casa/Arrays/ArrayMath.h>
-#include <casacore/casa/OS/File.h>
+
 #include <iostream>
-#include <iomanip>
+#include <Common/ParameterSet.h>
+#include <Common/ParameterValue.h>
+#include <Common/Timer.h>
 
-using namespace casacore;
-using namespace LOFAR::BBS;
+#include <stddef.h>
+#include <string>
+#include <sstream>
+#include <utility>
+#include <vector>
 
-/// Look at BBSKernel MeasurementExprLOFARUtil.cc and Apply.cc
+using namespace casacore;
 
 namespace LOFAR {
   namespace DPPP {
 
     ApplyCal::ApplyCal (DPInput* input,
-                        const ParameterSet& parset,
-                        const string& prefix,
-                        bool substep,
-                        string predictDirection
-                        )
-      : itsInput       (input),
-        itsName        (prefix),
-        itsParmDBName  (parset.getString (prefix + "parmdb")),
-        itsUseH5Parm   (itsParmDBName.find(".h5")!=string::npos),
-        itsTimeSlotsPerParmUpdate (parset.getInt (prefix +
-            "timeslotsperparmupdate", 500)),
-        itsSigmaMMSE   (parset.getDouble (prefix + "MMSE.Sigma", 0)),
-        itsUpdateWeights (parset.getBool (prefix + "updateweights", false)),
-        itsCount       (0),
-        itsTimeStep    (0),
-        itsNCorr       (0),
-        itsTimeInterval (-1),
-        itsLastTime    (-1),
-        itsUseAP       (false)
+                      const ParameterSet& parset,
+                      const string& prefix,
+                      bool substep,
+                      string predictDirection
+                      )
+      : itsIsSubstep(substep)
     {
-      ASSERT (!itsParmDBName.empty());
+      vector<string> subStepNames;
+      ParameterValue namesPar (parset.getString(prefix + "steps", ""));
 
-      if (substep) {
-        itsInvert=false;
+      if (namesPar.isVector()) {
+        subStepNames = namesPar.getStringVector();
       } else {
-        itsInvert = parset.getBool (prefix + "invert", true);
-      }
-
-      if (itsUseH5Parm) {
-        string directionStr;
-        if (substep) {
-          directionStr = predictDirection;
-        } else {
-          directionStr = parset.getString (prefix + "direction", "");
-        }
-        itsH5Parm = H5Parm(itsParmDBName);
-        string solTabName = toLower(parset.getString (prefix + "correction"));
-        itsSolTab = itsH5Parm.getSolTab(solTabName);
-        itsCorrectType = stringToCorrectType(itsSolTab.getType());
-        if (itsCorrectType==PHASE && nPol("")==1) {
-          itsCorrectType = SCALARPHASE;
-        }
-        if (itsCorrectType==AMPLITUDE && nPol("")==1) {
-          itsCorrectType = SCALARAMPLITUDE;
-        }
-        if (directionStr=="") {
-          ASSERT(!itsSolTab.hasAxis("dir") || itsSolTab.getAxis("dir").size==1);
+        subStepNames.push_back(namesPar.getString());
+      }
+
+      vector<string>::const_iterator subStepNameIter;
+      for (subStepNameIter = subStepNames.begin();
+           subStepNameIter != subStepNames.end();
+           ++subStepNameIter) {
+        string subStepName = (*subStepNameIter);
+        string subStepPrefix;
+        if (subStepName.empty()) {
+          // No substeps given, use parameters of this step
+          subStepPrefix = prefix;
         } else {
-          itsDirection = itsSolTab.getDirIndex(directionStr);
+          // Substeps given, use named parameters like applycal.applySol.parmdb
+          subStepPrefix = prefix + subStepName + ".";
         }
-      } else {
-        string correctTypeStr = toLower(parset.getString (prefix + "correction", "gain"));
-        itsCorrectType = stringToCorrectType(correctTypeStr);
+        itsApplyCals.push_back(OneApplyCal::ShPtr(new OneApplyCal(
+                input, parset, subStepPrefix, prefix, substep,
+                predictDirection)));
       }
 
-      if (itsCorrectType==FULLJONES && itsUpdateWeights) {
-        ASSERTSTR (itsInvert, "Updating weights has not been implemented for invert=false and fulljones");
+      uint numSteps = itsApplyCals.size();
+      for (uint step=0; step<numSteps-1; ++step) {
+        itsApplyCals[step]->setNextStep(itsApplyCals[step+1]);
       }
     }
 
     ApplyCal::ApplyCal()
     {}
 
-    string ApplyCal::correctTypeToString(CorrectType ct) {
-      if (ct==GAIN) return "gain";
-      if (ct==FULLJONES) return "fulljones";
-      if (ct==TEC) return "tec";
-      if (ct==CLOCK) return "clock";
-      if (ct==SCALARPHASE) return "scalarphase";
-      if (ct==SCALARAMPLITUDE) return "scalaramplitude";
-      if (ct==ROTATIONANGLE) return "rotationangle";
-      if (ct==ROTATIONMEASURE) return "rotationmeasure";
-      if (ct==PHASE) return "phase";
-      if (ct==AMPLITUDE) return "amplitude";
-      THROW(Exception, "Unknown correction type: "<<ct);
-      return "";
-    }
-
-    ApplyCal::CorrectType ApplyCal::stringToCorrectType(const string& ctStr) {
-      if (ctStr=="gain") return GAIN;
-      if (ctStr=="fulljones") return FULLJONES;
-      if (ctStr=="tec") return TEC;
-      if (ctStr=="clock") return CLOCK;
-      if (ctStr=="scalarphase" || ctStr=="commonscalarphase") return SCALARPHASE;
-      if (ctStr=="scalaramplitude" || ctStr=="commonscalaramplitude") return SCALARAMPLITUDE;
-      if (ctStr=="phase") return PHASE;
-      if (ctStr=="amplitude") return AMPLITUDE;
-      if (ctStr=="rotationangle" || ctStr=="commonrotationangle") return ROTATIONANGLE;
-      if (ctStr=="rotationmeasure") return ROTATIONMEASURE;
-      THROW(Exception, "Unknown correction type: "<<ctStr);
-      return GAIN;
-    }
-
     ApplyCal::~ApplyCal()
     {}
 
-    void ApplyCal::updateInfo (const DPInfo& infoIn)
+    void ApplyCal::setNextStep (DPStep::ShPtr nextStep)
     {
-      info() = infoIn;
-      info().setNeedVisData();
-      info().setWriteData();
-      info().setWriteFlags();
-      if (itsUpdateWeights) {
-        info().setWriteWeights();
-      }
-      itsTimeInterval = infoIn.timeInterval();
-      itsNCorr = infoIn.ncorr();
-
-      ASSERT(itsNCorr==4);
-
-      if (!itsUseH5Parm) { // Use ParmDB
-        itsParmDB.reset(new BBS::ParmFacade(itsParmDBName));
-      }
-
-      // Detect if full jones solutions are present
-      if (!itsUseH5Parm &&
-          (itsCorrectType == GAIN || itsCorrectType==FULLJONES) &&
-          (itsParmDB->getNames("Gain:0:1:*").size() +
-           itsParmDB->getDefNames("Gain:0:1:*").size() >0 )) {
-        itsCorrectType=FULLJONES;
-      }
-
-      // Detect if solutions are saved as Real/Imag or Ampl/Phase
-      if (itsCorrectType == GAIN || itsCorrectType == FULLJONES ){
-        if (itsUseH5Parm) {
-          // H5Parm uses amplitude / phase by definition
-          itsUseAP = true;
-        } else {
-          // Determine from values present in parmdb what to use
-          if (!itsParmDB->getNames("Gain:0:0:Real*").empty()) {
-            // Values with :Real present
-            itsUseAP = false;
-          } else if (!itsParmDB->getNames("Gain:0:0:Ampl*").empty() ||
-                     !itsParmDB->getNames("Phase:0:0:Ampl*").empty()) {
-            // Values with :Ampl present
-            itsUseAP = true;
-          } else if (!itsParmDB->getDefNames("Gain:0:0:Real*").empty()) {
-            // Defvalues with :Real present
-            itsUseAP = false;
-          } else if (!itsParmDB->getDefNames("Gain:0:0:Ampl*").empty() ||
-                     !itsParmDB->getDefNames("Gain:0:0:Phase*").empty()) {
-            // Defvalues with :Ampl present
-            itsUseAP = true;
-          } else {
-            THROW (Exception, "No gains found in parmdb "+itsParmDBName);
-          }
-        }
-      }
-
-      if (itsCorrectType == GAIN) {
-        if (itsUseAP) {
-          itsParmExprs.push_back("Gain:0:0:Ampl");
-          itsParmExprs.push_back("Gain:0:0:Phase");
-          itsParmExprs.push_back("Gain:1:1:Ampl");
-          itsParmExprs.push_back("Gain:1:1:Phase");
-        } else {
-          itsParmExprs.push_back("Gain:0:0:Real");
-          itsParmExprs.push_back("Gain:0:0:Imag");
-          itsParmExprs.push_back("Gain:1:1:Real");
-          itsParmExprs.push_back("Gain:1:1:Imag");
-        }
-      } else if (itsCorrectType == FULLJONES) {
-        if (itsUseAP) {
-          itsParmExprs.push_back("Gain:0:0:Ampl");
-          itsParmExprs.push_back("Gain:0:0:Phase");
-          itsParmExprs.push_back("Gain:0:1:Ampl");
-          itsParmExprs.push_back("Gain:0:1:Phase");
-          itsParmExprs.push_back("Gain:1:0:Ampl");
-          itsParmExprs.push_back("Gain:1:0:Phase");
-          itsParmExprs.push_back("Gain:1:1:Ampl");
-          itsParmExprs.push_back("Gain:1:1:Phase");
-        } else {
-          itsParmExprs.push_back("Gain:0:0:Real");
-          itsParmExprs.push_back("Gain:0:0:Imag");
-          itsParmExprs.push_back("Gain:0:1:Real");
-          itsParmExprs.push_back("Gain:0:1:Imag");
-          itsParmExprs.push_back("Gain:1:0:Real");
-          itsParmExprs.push_back("Gain:1:0:Imag");
-          itsParmExprs.push_back("Gain:1:1:Real");
-          itsParmExprs.push_back("Gain:1:1:Imag");
-        }
-      } else if (itsCorrectType == TEC) {
-        if (nPol("TEC")==1) {
-          itsParmExprs.push_back("TEC");
-        }
-        else {
-          itsParmExprs.push_back("TEC:0");
-          itsParmExprs.push_back("TEC:1");
-        }
-      } else if (itsCorrectType == CLOCK) {
-        if (nPol("Clock")==1) {
-          itsParmExprs.push_back("Clock");
-        }
-        else {
-          itsParmExprs.push_back("Clock:0");
-          itsParmExprs.push_back("Clock:1");
-        }
-      } else if (itsCorrectType == ROTATIONANGLE) {
-        itsParmExprs.push_back("{Common,}RotationAngle");
-      } else if (itsCorrectType == SCALARPHASE) {
-        itsParmExprs.push_back("{Common,}ScalarPhase");
-      } else if (itsCorrectType == ROTATIONMEASURE) {
-        itsParmExprs.push_back("RotationMeasure");
-      } else if (itsCorrectType == SCALARAMPLITUDE) {
-        itsParmExprs.push_back("{Common,}ScalarAmplitude");
-      } else if (itsCorrectType == PHASE) {
-        ASSERT(itsUseH5Parm);
-        itsParmExprs.push_back("Phase:0");
-        itsParmExprs.push_back("Phase:1");
-      } else if (itsCorrectType == AMPLITUDE) {
-        ASSERT(itsUseH5Parm);
-        itsParmExprs.push_back("Amplitude:0");
-        itsParmExprs.push_back("Amplitude:1");
-      } else {
-        THROW (Exception, "Correction type "<<
-                          correctTypeToString(itsCorrectType)<<" is unknown");
-      }
-
-      initDataArrays();
-      itsFlagCounter.init(getInfo());
-
-      // Check that channels are evenly spaced
-      if (info().nchan()>1) {
-        Vector<Double> upFreq = info().chanFreqs()(
-                                  Slicer(IPosition(1,1),
-                                         IPosition(1,info().nchan()-1)));
-        Vector<Double> lowFreq = info().chanFreqs()(
-                                  Slicer(IPosition(1,0),
-                                         IPosition(1,info().nchan()-1)));
-        Double freqstep0=upFreq(0)-lowFreq(0);
-        // Compare up to 1kHz accuracy
-        bool regularChannels=allNearAbs(upFreq-lowFreq, freqstep0, 1.e3) &&
-                             allNearAbs(info().chanWidths(),
-                                        info().chanWidths()(0), 1.e3);
-        ASSERTSTR(regularChannels, 
-                  "ApplyCal requires evenly spaced channels.");
-      }
+      DPStep::setNextStep(itsApplyCals[0]);
+      itsApplyCals[itsApplyCals.size()-1]->setNextStep(nextStep);
     }
 
-    void ApplyCal::show (std::ostream& os) const
+    void ApplyCal::show(std::ostream& os) const
     {
-      os << "ApplyCal " << itsName << std::endl;
-      if (itsUseH5Parm) {
-        os << "  H5Parm:         " << itsParmDBName <<endl;
-      } else {
-        os << "  parmdb:         " << itsParmDBName << endl;
-      }
-      os << "  correction:     " << correctTypeToString(itsCorrectType) << endl;
-      if (itsCorrectType==GAIN || itsCorrectType==FULLJONES) {
-        os << "    Ampl/Phase:   " << boolalpha << itsUseAP << endl;
+      // If not a substep, show will be called by DPRun,
+      // through the nextStep() mechanism
+      if (itsIsSubstep) {
+        vector<OneApplyCal::ShPtr>::const_iterator applycalIter;
+
+        for (applycalIter = itsApplyCals.begin();
+             applycalIter != itsApplyCals.end();
+             applycalIter++) {
+          (*applycalIter)->show(os);
+        }
       }
-      os << "  update weights: " << boolalpha << itsUpdateWeights << endl;
-      os << "  sigmaMMSE:      " << itsSigmaMMSE << endl;
-      os << "  invert:         " << boolalpha << itsInvert <<endl;
-      os << "  timeSlotsPerParmUpdate: " << itsTimeSlotsPerParmUpdate <<endl;
     }
 
     void ApplyCal::showTimings (std::ostream& os, double duration) const
     {
-      os << "  ";
-      FlagCounter::showPerc1 (os, itsTimer.getElapsed(), duration);
-      os << " ApplyCal " << itsName << endl;
+      if (itsIsSubstep) {
+        vector<OneApplyCal::ShPtr>::const_iterator iter;
+        for (iter = itsApplyCals.begin();
+             iter != itsApplyCals.end();
+             iter++) {
+          (*iter)->showTimings(os, duration);
+        }
+      }
     }
 
     bool ApplyCal::process (const DPBuffer& bufin)
     {
-      itsTimer.start();
-      itsBuffer.copy (bufin);
-
-      if (bufin.getTime() > itsLastTime) {
-        updateParms(bufin.getTime());
-        itsTimeStep=0;
-      }
-      else {
-        itsTimeStep++;
-      }
-
-      // Loop through all baselines in the buffer.
-      size_t nbl = itsBuffer.getData().shape()[2];
-
-      Complex* data = itsBuffer.getData().data();
-
-      itsInput->fetchWeights (bufin, itsBuffer, itsTimer);
-      float* weight = itsBuffer.getWeights().data();
-
-      bool* flag = itsBuffer.getFlags().data();
-
-      size_t nchan = itsBuffer.getData().shape()[1];
-
-#pragma omp parallel for
-      for (size_t bl=0; bl<nbl; ++bl) {
-        for (size_t chan=0;chan<nchan;chan++) {
-          uint timeFreqOffset=(itsTimeStep*info().nchan())+chan;
-          uint antA = info().getAnt1()[bl];
-          uint antB = info().getAnt2()[bl];
-          if (itsParms.shape()[0]>2) {
-            applyFull( &itsParms(0, antA, timeFreqOffset),
-                       &itsParms(0, antB, timeFreqOffset),
-                       &data[bl * itsNCorr * nchan + chan * itsNCorr ],
-                       &weight[bl * itsNCorr * nchan + chan * itsNCorr ],
-                       &flag[  bl * itsNCorr * nchan + chan * itsNCorr ],
-                       bl, chan, itsUpdateWeights, itsFlagCounter);
-          }
-          else {
-            applyDiag( &itsParms(0, antA, timeFreqOffset),
-                       &itsParms(0, antB, timeFreqOffset),
-                       &data[bl * itsNCorr * nchan + chan * itsNCorr ],
-                       &weight[bl * itsNCorr * nchan + chan * itsNCorr ],
-                       &flag[  bl * itsNCorr * nchan + chan * itsNCorr ],
-                       bl, chan, itsUpdateWeights, itsFlagCounter);
-          }
-        }
-      }
-
-      itsTimer.stop();
-      getNextStep()->process(itsBuffer);
-
-      itsCount++;
+      getNextStep()->process(bufin);
       return true;
     }
 
+
     void ApplyCal::finish()
     {
       // Let the next steps finish.
       getNextStep()->finish();
     }
 
-
-    void ApplyCal::updateParms (const double bufStartTime)
-    {
-      uint numAnts = info().antennaNames().size();
-
-      // itsParms contains the parameters to a grid, first for all parameters
-      // (e.g. Gain:0:0 and Gain:1:1), next all antennas, next over freq * time
-      // as returned by ParmDB
-      vector<vector<vector<double> > > parmvalues;
-      parmvalues.resize(itsParmExprs.size());
-      for (size_t i=0;i<parmvalues.size();++i) {
-        parmvalues[i].resize(numAnts);
-      }
-
-      uint numFreqs         (info().chanFreqs().size());
-      double freqInterval  (info().chanWidths()[0]);
-      if (numFreqs>1) { // Handle data with evenly spaced gaps between channels
-        freqInterval = info().chanFreqs()[1]-info().chanFreqs()[0];
-      }
-      double minFreq       (info().chanFreqs()[0]-0.5*freqInterval);
-      double maxFreq (info().chanFreqs()[numFreqs-1]+0.5*freqInterval);
-      itsLastTime = bufStartTime - 0.5*itsTimeInterval + 
-                    itsTimeSlotsPerParmUpdate * itsTimeInterval;
-      uint numTimes = itsTimeSlotsPerParmUpdate;
-
-      double lastMSTime = info().startTime() + info().ntime() * itsTimeInterval;
-      if (itsLastTime > lastMSTime && !nearAbs(itsLastTime, lastMSTime, 1.e-3)) {
-        itsLastTime = lastMSTime;
-        numTimes = info().ntime() % itsTimeSlotsPerParmUpdate;
-      }
-
-      map<string, vector<double> > parmMap;
-      map<string, vector<double> >::iterator parmIt;
-
-      uint tfDomainSize=numTimes*numFreqs;
-
-      // Fill parmvalues here, get raw data from H5Parm or ParmDB
-      if (itsUseH5Parm) {
-#pragma omp critical(updateH5ParmValues)
-{
-        // TODO: understand polarization etc.
-        ASSERT(itsParmExprs.size()==1 || itsParmExprs.size()==2);
-        hsize_t startTime = itsSolTab.getTimeIndex(bufStartTime);
-        hsize_t startFreq = itsSolTab.getFreqIndex(info().chanFreqs()[0]);
-        for (uint ant = 0; ant < numAnts; ++ant) {
-
-          uint freqUpsampleFactor = numFreqs;
-          if (itsSolTab.hasAxis("freq") && itsSolTab.getAxis("freq").size > 1) {
-            double h5freqinterval = itsSolTab.getFreqInterval();
-            freqUpsampleFactor = h5freqinterval/info().chanWidths()[0] + 0.5; // Round;
-            ASSERT(near(h5freqinterval, freqUpsampleFactor*info().chanWidths()[0],1.e-5));
-          }
-
-          uint timeUpsampleFactor = numTimes;
-          if (itsSolTab.getAxis("time").size > 1) {
-            double h5timeInterval = itsSolTab.getTimeInterval();
-            timeUpsampleFactor = h5timeInterval/itsTimeInterval+0.5; // Round
-            ASSERT(near(h5timeInterval,timeUpsampleFactor*itsTimeInterval,1.e-5));
-          }
-
-          // Figure out whether time or frequency is first axis
-          bool freqvariesfastest = true;
-          if (itsSolTab.hasAxis("freq") &&
-              itsSolTab.getAxisIndex("freq") < itsSolTab.getAxisIndex("time")) {
-            freqvariesfastest = false;
-          }
-          ASSERT(freqvariesfastest);
-
-          // Take the ceiling of numTimes/timeUpsampleFactor, same for freq
-          uint numTimesInH5Parm = (numTimes+timeUpsampleFactor-1)/timeUpsampleFactor;
-          uint numFreqsInH5Parm = (numFreqs+freqUpsampleFactor-1)/freqUpsampleFactor;
-
-          for (uint pol=0; pol<itsParmExprs.size(); ++pol) {
-            vector<double> rawsols;
-            rawsols = itsSolTab.getValues(info().antennaNames()[ant],
-                                        startTime, numTimesInH5Parm, 1,
-                                        startFreq, numFreqsInH5Parm, 1, pol, itsDirection);
-
-            parmvalues[pol][ant].resize(tfDomainSize);
-
-            size_t tf=0;
-            for (uint t=0; t<numTimesInH5Parm; ++t) {
-              for (uint ti=0; ti<timeUpsampleFactor; ++ti) {
-                for (uint f=0; f<numFreqsInH5Parm; ++f) {
-                  for (uint fi=0; fi<freqUpsampleFactor; ++fi) {
-                    if (tf<tfDomainSize) {
-                      parmvalues[pol][ant][tf++] = rawsols[t*numFreqsInH5Parm + f];
-                    }
-                  }
-                }
-              }
-            }
-            ASSERT(tf==tfDomainSize);
-          }
-        }
-} // End pragma omp critical
-      } else { // Use ParmDB
-        for (uint parmExprNum = 0; parmExprNum<itsParmExprs.size();++parmExprNum) {
-          // parmMap contains parameter values for all antennas
-          parmMap = itsParmDB->getValuesMap( itsParmExprs[parmExprNum] + "*",
-                                 minFreq, maxFreq, freqInterval,
-                                 bufStartTime-0.5*itsTimeInterval, itsLastTime,
-                                 itsTimeInterval, true);
-
-          // Resolve {Common,}Bla to CommonBla or Bla
-          if (!parmMap.empty() &&
-              itsParmExprs[parmExprNum].find("{") != std::string::npos) {
-            uint colonPos = (parmMap.begin()->first).find(":");
-            itsParmExprs[parmExprNum] = (parmMap.begin()->first).substr(0, colonPos);
-          }
-
-          for (uint ant = 0; ant < numAnts; ++ant) {
-            parmIt = parmMap.find(
-                      itsParmExprs[parmExprNum] + ":" + info().antennaNames()[ant]);
-
-            if (parmIt != parmMap.end()) {
-              parmvalues[parmExprNum][ant].swap(parmIt->second);
-              ASSERT(parmvalues[parmExprNum][ant].size()==tfDomainSize);
-            } else {// No value found, try default
-              Array<double> defValues;
-              double defValue;
-
-              if (itsParmDB->getDefValues(itsParmExprs[parmExprNum] + ":" +
-                  info().antennaNames()[ant]).size()==1) { // Default for antenna
-                itsParmDB->getDefValues(itsParmExprs[parmExprNum] + ":" +
-                    info().antennaNames()[ant]).get(0,defValues);
-                ASSERT(defValues.size()==1);
-                defValue=defValues.data()[0];
-              }
-              else if (itsParmDB->getDefValues(itsParmExprs[parmExprNum]).size()
-                  == 1) { //Default value
-                itsParmDB->getDefValues(itsParmExprs[parmExprNum]).get(0,defValues);
-                ASSERT(defValues.size()==1);
-                defValue=defValues.data()[0];
-              } else if (itsParmExprs[parmExprNum].substr(0,5)=="Gain:") {
-                defValue=0.;
-              }
-              else {
-                THROW (Exception, "No parameter value found for "+
-                   itsParmExprs[parmExprNum]+":"+info().antennaNames()[ant]);
-              }
-
-              parmvalues[parmExprNum][ant].resize(tfDomainSize);
-              for (uint tf=0; tf<tfDomainSize;++tf) {
-                parmvalues[parmExprNum][ant][tf]=defValue;
-              }
-            }
-          }
-        }
-      }
-
-      ASSERT(parmvalues[0][0].size() <= tfDomainSize); // Catches multiple matches
-
-      double freq;
-
-      // Make parameters complex
-      for (uint tf=0;tf<tfDomainSize;++tf) {
-        for (uint ant=0;ant<numAnts;++ant) {
-
-          freq=info().chanFreqs()[tf % numFreqs];
-
-          if (itsCorrectType==GAIN) {
-            if (itsUseAP) { // Data as Amplitude / Phase
-              itsParms(0, ant, tf) = polar(parmvalues[0][ant][tf],
-                                           parmvalues[1][ant][tf]);
-              itsParms(1, ant, tf) = polar(parmvalues[2][ant][tf],
-                                           parmvalues[3][ant][tf]);
-            } else { // Data as Real / Imaginary
-              itsParms(0, ant, tf) = DComplex(parmvalues[0][ant][tf],
-                                              parmvalues[1][ant][tf]);
-              itsParms(1, ant, tf) = DComplex(parmvalues[2][ant][tf],
-                                              parmvalues[3][ant][tf]);
-            }
-          }
-          else if (itsCorrectType==FULLJONES) {
-            if (itsUseAP) { // Data as Amplitude / Phase
-              itsParms(0, ant, tf) = polar(parmvalues[0][ant][tf],
-                                           parmvalues[1][ant][tf]);
-              itsParms(1, ant, tf) = polar(parmvalues[2][ant][tf],
-                                           parmvalues[3][ant][tf]);
-              itsParms(2, ant, tf) = polar(parmvalues[4][ant][tf],
-                                           parmvalues[5][ant][tf]);
-              itsParms(3, ant, tf) = polar(parmvalues[6][ant][tf],
-                                           parmvalues[7][ant][tf]);
-            } else { // Data as Real / Imaginary
-              itsParms(0, ant, tf) = DComplex(parmvalues[0][ant][tf],
-                                              parmvalues[1][ant][tf]);
-              itsParms(1, ant, tf) = DComplex(parmvalues[2][ant][tf],
-                                              parmvalues[3][ant][tf]);
-              itsParms(2, ant, tf) = DComplex(parmvalues[4][ant][tf],
-                                              parmvalues[5][ant][tf]);
-              itsParms(3, ant, tf) = DComplex(parmvalues[6][ant][tf],
-                                              parmvalues[7][ant][tf]);
-            }
-          }
-          else if (itsCorrectType==TEC) {
-            itsParms(0, ant, tf)=polar(1.,
-                parmvalues[0][ant][tf] * -8.44797245e9 / freq);
-            if (itsParmExprs.size() == 1) { // No TEC:0, only TEC:
-              itsParms(1, ant, tf)=polar(1.,
-                  parmvalues[0][ant][tf] * -8.44797245e9 / freq);
-            }
-            else { // TEC:0 and TEC:1
-              itsParms(1, ant, tf)=polar(1.,
-                  parmvalues[1][ant][tf] * -8.44797245e9 / freq);
-            }
-          }
-          else if (itsCorrectType==CLOCK) {
-            itsParms(0, ant, tf)=polar(1.,
-                parmvalues[0][ant][tf] * freq * casacore::C::_2pi);
-            if (itsParmExprs.size() == 1) { // No Clock:0, only Clock:
-              itsParms(1, ant, tf)=polar(1.,
-                  parmvalues[0][ant][tf] * freq * casacore::C::_2pi);
-            }
-            else { // Clock:0 and Clock:1
-              itsParms(1, ant, tf)=polar(1.,
-                  parmvalues[1][ant][tf] * freq * casacore::C::_2pi);
-            }
-          }
-          else if (itsCorrectType==ROTATIONANGLE) {
-            double phi=parmvalues[0][ant][tf];
-            if (itsInvert) {
-              phi = -phi;
-            }
-            double sinv=sin(phi);
-            double cosv=cos(phi);
-            itsParms(0, ant, tf) =  cosv;
-            itsParms(1, ant, tf) = -sinv;
-            itsParms(2, ant, tf) =  sinv;
-            itsParms(3, ant, tf) =  cosv;
-          }
-          else if (itsCorrectType==ROTATIONMEASURE) {
-            double lambda2 = casacore::C::c / freq;
-            lambda2 *= lambda2;
-            double chi = parmvalues[0][ant][tf] * lambda2;
-            if (itsInvert) {
-              chi = -chi;
-            }
-            double sinv = sin(chi);
-            double cosv = cos(chi);
-            itsParms(0, ant, tf) =  cosv;
-            itsParms(1, ant, tf) = -sinv;
-            itsParms(2, ant, tf) =  sinv;
-            itsParms(3, ant, tf) =  cosv;
-          }
-          else if (itsCorrectType==PHASE || itsCorrectType==SCALARPHASE) {
-            itsParms(0, ant, tf) = polar(1., parmvalues[0][ant][tf]);
-            if (itsCorrectType==SCALARPHASE) { // Same value for x and y
-              itsParms(1, ant, tf) = polar(1., parmvalues[0][ant][tf]);
-            } else { // Different value for x and y
-              itsParms(1, ant, tf) = polar(1., parmvalues[1][ant][tf]);
-            }
-          }
-          else if (itsCorrectType==AMPLITUDE || itsCorrectType==SCALARAMPLITUDE) {
-            itsParms(0, ant, tf) = parmvalues[0][ant][tf];
-            if (itsCorrectType==SCALARAMPLITUDE) { // Same value for x and y
-              itsParms(1, ant, tf) = parmvalues[0][ant][tf];
-            } else { // Different value for x and y
-              itsParms(1, ant, tf) = parmvalues[1][ant][tf];
-            }
-          }
-
-          // Invert
-          if (itsInvert) {
-            if (itsParms.shape()[0]==2) {
-            itsParms(0, ant, tf) = 1./itsParms(0, ant, tf);
-            itsParms(1, ant, tf) = 1./itsParms(1, ant, tf);
-            } else if (itsCorrectType==FULLJONES) {
-              invert(&itsParms(0, ant, tf),itsSigmaMMSE);
-            } else {
-              ASSERT (itsCorrectType==ROTATIONMEASURE || itsCorrectType==ROTATIONANGLE);
-              // rotationmeasure and commonrotationangle are already inverted above
-            }
-          }
-        }
-      }
-    }
-
-    uint ApplyCal::nPol(const string& parmName) {
-      if (itsUseH5Parm) {
-        if (!itsSolTab.hasAxis("pol")) {
-          return 1;
-        } else {
-          return itsSolTab.getAxis("pol").size;
-        }
-      } else { // Use ParmDB
-        if (itsParmDB->getNames(parmName+":0:*").empty() &&
-                    itsParmDB->getDefNames(parmName+":0:*").empty() ) {
-          return 1;
-        } else {
-          return 2;
-        }
-      }
-    }
-
-    void ApplyCal::initDataArrays() {
-      uint numAnts=info().antennaNames().size();
-      uint tfDomainSize=itsTimeSlotsPerParmUpdate*info().chanFreqs().size();
-
-      uint numParms;
-      if (itsCorrectType==FULLJONES ||
-          itsCorrectType==ROTATIONANGLE ||
-          itsCorrectType==ROTATIONMEASURE) {
-        numParms = 4;
-      }
-      else {
-        numParms = 2;
-      }
-
-      itsParms.resize(numParms, numAnts, tfDomainSize);
-    }
-
     void ApplyCal::applyDiag (const DComplex* gainA, const DComplex* gainB,
                               Complex* vis, float* weight, bool* flag,
                               uint bl, uint chan, bool updateWeights,
@@ -844,13 +295,5 @@ namespace LOFAR {
       weight[3]=1./weight[3];
     }
 
-    void ApplyCal::showCounts (std::ostream& os) const
-    {
-      os << endl << "Flags set by ApplyCal " << itsName;
-      os << endl << "=======================" << endl;
-      itsFlagCounter.showBaseline (os, itsCount);
-      itsFlagCounter.showChannel  (os, itsCount);
-    }
-
   } //# end namespace
 }
diff --git a/CEP/DP3/DPPP/src/CMakeLists.txt b/CEP/DP3/DPPP/src/CMakeLists.txt
index b2d33f4dcf6c7781c849c34e23265db818acf5e7..0d6115d9b0dc0340ca3a0c5ffced51d7f279e153 100644
--- a/CEP/DP3/DPPP/src/CMakeLists.txt
+++ b/CEP/DP3/DPPP/src/CMakeLists.txt
@@ -18,7 +18,7 @@ lofar_add_library(dppp
   ModelComponent.cc PointSource.cc GaussianSource.cc Patch.cc
   ModelComponentVisitor.cc GainCal.cc StefCal.cc
   DemixerNew.cc DemixInfo.cc DemixWorker.cc
-  Predict.cc
+  Predict.cc OneApplyCal.cc
   ApplyBeam.cc
   PhaseFitter.cc H5Parm.cc SolTab.cc DummyStep.cc H5ParmPredict.cc
 )
diff --git a/CEP/DP3/DPPP/src/DummyStep.cc b/CEP/DP3/DPPP/src/DummyStep.cc
index 68bab17a8685e76a142d4f8756ee4edf50017c3b..4fb62624db78903e2161ae0d27f0a0446404d6c9 100644
--- a/CEP/DP3/DPPP/src/DummyStep.cc
+++ b/CEP/DP3/DPPP/src/DummyStep.cc
@@ -35,7 +35,6 @@
 #include <vector>
 
 using namespace casacore;
-using namespace LOFAR::BBS;
 
 namespace LOFAR {
   namespace DPPP {
@@ -43,6 +42,7 @@ namespace LOFAR {
     DummyStep::DummyStep (DPInput* input,
                       const ParameterSet& parset,
                       const string& prefix)
+    : itsInput(input)
     {
 
     }
diff --git a/CEP/DP3/DPPP/src/H5ParmPredict.cc b/CEP/DP3/DPPP/src/H5ParmPredict.cc
index 8714455202a60bdbd2938acfe262fa0d0978d867..33b0a2dc1b2666d3f0afeb4bc112f2975276781e 100644
--- a/CEP/DP3/DPPP/src/H5ParmPredict.cc
+++ b/CEP/DP3/DPPP/src/H5ParmPredict.cc
@@ -137,6 +137,7 @@ namespace LOFAR {
     void H5ParmPredict::finish()
     {
       // Let the next steps finish.
+      itsPredictSteps[0]->finish();
       getNextStep()->finish();
     }
   } //# end namespace
diff --git a/CEP/DP3/DPPP/src/OneApplyCal.cc b/CEP/DP3/DPPP/src/OneApplyCal.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c602eece041bfaf50d848bf64c48982f0db6dc86
--- /dev/null
+++ b/CEP/DP3/DPPP/src/OneApplyCal.cc
@@ -0,0 +1,713 @@
+//# OneApplyCal.cc: DPPP step class to apply a calibration correction to the data
+//# Copyright (C) 2013
+//# ASTRON (Netherlands Institute for Radio Astronomy)
+//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+//#
+//# This file is part of the LOFAR software suite.
+//# The LOFAR software suite 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.
+//#
+//# The LOFAR software suite 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 the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+//#
+//# $Id: OneApplyCal.cc 21598 2012-07-16 08:07:34Z diepen $
+//#
+//# @author Tammo Jan Dijkema
+
+#include <lofar_config.h>
+#include <DPPP/OneApplyCal.h>
+#include <DPPP/ApplyCal.h>
+#include <DPPP/DPBuffer.h>
+#include <DPPP/DPInfo.h>
+#include <DPPP/MSReader.h>
+#include <Common/ParameterSet.h>
+#include <Common/StringUtil.h>
+#include <Common/LofarLogger.h>
+#include <casacore/casa/Arrays/ArrayMath.h>
+#include <casacore/casa/OS/File.h>
+#include <iostream>
+#include <iomanip>
+
+using namespace casacore;
+using namespace LOFAR::BBS;
+
+/// Look at BBSKernel MeasurementExprLOFARUtil.cc and Apply.cc
+
+namespace LOFAR {
+  namespace DPPP {
+
+    OneApplyCal::OneApplyCal (DPInput* input,
+                        const ParameterSet& parset,
+                        const string& prefix,
+                        const string& defaultPrefix,
+                        bool substep,
+                        string predictDirection
+                        )
+      : itsInput       (input),
+        itsName        (prefix),
+        itsParmDBName  (
+            parset.isDefined(prefix+"parmdb") ?
+            parset.getString(prefix + "parmdb") :
+            parset.getString(defaultPrefix + "parmdb")),
+        itsUseH5Parm   (itsParmDBName.find(".h5") != string::npos),
+        itsTimeSlotsPerParmUpdate (
+            parset.isDefined(prefix + "timeslotsperparmupdate") ?
+            parset.getInt (prefix + "timeslotsperparmupdate") :
+            parset.getInt (defaultPrefix + "timeslotsperparmupdate", 500)),
+        itsSigmaMMSE   (
+            parset.isDefined(prefix + "MMSE.Sigma") ?
+            parset.getDouble(prefix + "MMSE.Sigma") :
+            parset.getDouble(defaultPrefix + "MMSE.Sigma", 0.)),
+        itsUpdateWeights (
+            parset.isDefined(prefix + "updateweights") ?
+            parset.getBool (prefix + "updateweights") :
+            parset.getBool (defaultPrefix + "updateweights", false)),
+        itsCount       (0),
+        itsTimeStep    (0),
+        itsNCorr       (0),
+        itsTimeInterval (-1),
+        itsLastTime    (-1),
+        itsUseAP       (false)
+    {
+
+      ASSERT (!itsParmDBName.empty());
+
+      if (substep) {
+        itsInvert=false;
+      } else {
+        itsInvert = (parset.isDefined(prefix + "invert") ?
+                     parset.getBool (prefix + "invert") :
+                     parset.getBool (defaultPrefix + "invert", true));
+      }
+
+      if (itsUseH5Parm) {
+        string directionStr;
+        directionStr = (parset.isDefined(prefix + "direction") ?
+                        parset.getString(prefix + "direction") :
+                        parset.getString(defaultPrefix + "direction",
+                          predictDirection));
+        itsH5Parm = H5Parm(itsParmDBName);
+
+        string solTabName = (parset.isDefined(prefix + "correction") ?
+                             parset.getString(prefix + "correction") :
+                             parset.getString(defaultPrefix + "correction"));
+
+        itsSolTab = itsH5Parm.getSolTab(solTabName);
+        itsCorrectType = stringToCorrectType(itsSolTab.getType());
+        if (itsCorrectType==PHASE && nPol("")==1) {
+          itsCorrectType = SCALARPHASE;
+        }
+        if (itsCorrectType==AMPLITUDE && nPol("")==1) {
+          itsCorrectType = SCALARAMPLITUDE;
+        }
+        if (directionStr=="") {
+          ASSERT(!itsSolTab.hasAxis("dir") || itsSolTab.getAxis("dir").size==1);
+        } else {
+          itsDirection = itsSolTab.getDirIndex(directionStr);
+        }
+      } else {
+        string correctTypeStr = toLower(
+            parset.isDefined(prefix + "correction") ?
+            parset.getString(prefix + "correction") :
+            parset.getString (defaultPrefix + "correction", "gain"));
+        itsCorrectType = stringToCorrectType(correctTypeStr);
+      }
+
+      if (itsCorrectType==FULLJONES && itsUpdateWeights) {
+        ASSERTSTR (itsInvert, "Updating weights has not been implemented for invert=false and fulljones");
+      }
+    }
+
+    string OneApplyCal::correctTypeToString(CorrectType ct) {
+      if (ct==GAIN) return "gain";
+      if (ct==FULLJONES) return "fulljones";
+      if (ct==TEC) return "tec";
+      if (ct==CLOCK) return "clock";
+      if (ct==SCALARPHASE) return "scalarphase";
+      if (ct==SCALARAMPLITUDE) return "scalaramplitude";
+      if (ct==ROTATIONANGLE) return "rotationangle";
+      if (ct==ROTATIONMEASURE) return "rotationmeasure";
+      if (ct==PHASE) return "phase";
+      if (ct==AMPLITUDE) return "amplitude";
+      THROW(Exception, "Unknown correction type: "<<ct);
+      return "";
+    }
+
+    OneApplyCal::CorrectType OneApplyCal::stringToCorrectType(const string& ctStr) {
+      if (ctStr=="gain") return GAIN;
+      if (ctStr=="fulljones") return FULLJONES;
+      if (ctStr=="tec") return TEC;
+      if (ctStr=="clock") return CLOCK;
+      if (ctStr=="scalarphase" || ctStr=="commonscalarphase") return SCALARPHASE;
+      if (ctStr=="scalaramplitude" || ctStr=="commonscalaramplitude") return SCALARAMPLITUDE;
+      if (ctStr=="phase") return PHASE;
+      if (ctStr=="amplitude") return AMPLITUDE;
+      if (ctStr=="rotationangle" || ctStr=="commonrotationangle") return ROTATIONANGLE;
+      if (ctStr=="rotationmeasure") return ROTATIONMEASURE;
+      THROW(Exception, "Unknown correction type: "<<ctStr);
+      return GAIN;
+    }
+
+    OneApplyCal::~OneApplyCal()
+    {}
+
+    void OneApplyCal::updateInfo (const DPInfo& infoIn)
+    {
+      info() = infoIn;
+      info().setNeedVisData();
+      info().setWriteData();
+      info().setWriteFlags();
+      if (itsUpdateWeights) {
+        info().setWriteWeights();
+      }
+      itsTimeInterval = infoIn.timeInterval();
+      itsNCorr = infoIn.ncorr();
+
+      ASSERT(itsNCorr==4);
+
+      if (!itsUseH5Parm) { // Use ParmDB
+        itsParmDB.reset(new BBS::ParmFacade(itsParmDBName));
+      }
+
+      // Detect if full jones solutions are present
+      if (!itsUseH5Parm &&
+          (itsCorrectType == GAIN || itsCorrectType==FULLJONES) &&
+          (itsParmDB->getNames("Gain:0:1:*").size() +
+           itsParmDB->getDefNames("Gain:0:1:*").size() >0 )) {
+        itsCorrectType=FULLJONES;
+      }
+
+      // Detect if solutions are saved as Real/Imag or Ampl/Phase
+      if (itsCorrectType == GAIN || itsCorrectType == FULLJONES ){
+        if (itsUseH5Parm) {
+          // H5Parm uses amplitude / phase by definition
+          itsUseAP = true;
+        } else {
+          // Determine from values present in parmdb what to use
+          if (!itsParmDB->getNames("Gain:0:0:Real*").empty()) {
+            // Values with :Real present
+            itsUseAP = false;
+          } else if (!itsParmDB->getNames("Gain:0:0:Ampl*").empty() ||
+                     !itsParmDB->getNames("Phase:0:0:Ampl*").empty()) {
+            // Values with :Ampl present
+            itsUseAP = true;
+          } else if (!itsParmDB->getDefNames("Gain:0:0:Real*").empty()) {
+            // Defvalues with :Real present
+            itsUseAP = false;
+          } else if (!itsParmDB->getDefNames("Gain:0:0:Ampl*").empty() ||
+                     !itsParmDB->getDefNames("Gain:0:0:Phase*").empty()) {
+            // Defvalues with :Ampl present
+            itsUseAP = true;
+          } else {
+            THROW (Exception, "No gains found in parmdb "+itsParmDBName);
+          }
+        }
+      }
+
+      if (itsCorrectType == GAIN) {
+        if (itsUseAP) {
+          itsParmExprs.push_back("Gain:0:0:Ampl");
+          itsParmExprs.push_back("Gain:0:0:Phase");
+          itsParmExprs.push_back("Gain:1:1:Ampl");
+          itsParmExprs.push_back("Gain:1:1:Phase");
+        } else {
+          itsParmExprs.push_back("Gain:0:0:Real");
+          itsParmExprs.push_back("Gain:0:0:Imag");
+          itsParmExprs.push_back("Gain:1:1:Real");
+          itsParmExprs.push_back("Gain:1:1:Imag");
+        }
+      } else if (itsCorrectType == FULLJONES) {
+        if (itsUseAP) {
+          itsParmExprs.push_back("Gain:0:0:Ampl");
+          itsParmExprs.push_back("Gain:0:0:Phase");
+          itsParmExprs.push_back("Gain:0:1:Ampl");
+          itsParmExprs.push_back("Gain:0:1:Phase");
+          itsParmExprs.push_back("Gain:1:0:Ampl");
+          itsParmExprs.push_back("Gain:1:0:Phase");
+          itsParmExprs.push_back("Gain:1:1:Ampl");
+          itsParmExprs.push_back("Gain:1:1:Phase");
+        } else {
+          itsParmExprs.push_back("Gain:0:0:Real");
+          itsParmExprs.push_back("Gain:0:0:Imag");
+          itsParmExprs.push_back("Gain:0:1:Real");
+          itsParmExprs.push_back("Gain:0:1:Imag");
+          itsParmExprs.push_back("Gain:1:0:Real");
+          itsParmExprs.push_back("Gain:1:0:Imag");
+          itsParmExprs.push_back("Gain:1:1:Real");
+          itsParmExprs.push_back("Gain:1:1:Imag");
+        }
+      } else if (itsCorrectType == TEC) {
+        if (nPol("TEC")==1) {
+          itsParmExprs.push_back("TEC");
+        }
+        else {
+          itsParmExprs.push_back("TEC:0");
+          itsParmExprs.push_back("TEC:1");
+        }
+      } else if (itsCorrectType == CLOCK) {
+        if (nPol("Clock")==1) {
+          itsParmExprs.push_back("Clock");
+        }
+        else {
+          itsParmExprs.push_back("Clock:0");
+          itsParmExprs.push_back("Clock:1");
+        }
+      } else if (itsCorrectType == ROTATIONANGLE) {
+        itsParmExprs.push_back("{Common,}RotationAngle");
+      } else if (itsCorrectType == SCALARPHASE) {
+        itsParmExprs.push_back("{Common,}ScalarPhase");
+      } else if (itsCorrectType == ROTATIONMEASURE) {
+        itsParmExprs.push_back("RotationMeasure");
+      } else if (itsCorrectType == SCALARAMPLITUDE) {
+        itsParmExprs.push_back("{Common,}ScalarAmplitude");
+      } else if (itsCorrectType == PHASE) {
+        ASSERT(itsUseH5Parm);
+        itsParmExprs.push_back("Phase:0");
+        itsParmExprs.push_back("Phase:1");
+      } else if (itsCorrectType == AMPLITUDE) {
+        ASSERT(itsUseH5Parm);
+        itsParmExprs.push_back("Amplitude:0");
+        itsParmExprs.push_back("Amplitude:1");
+      } else {
+        THROW (Exception, "Correction type "<<
+                          correctTypeToString(itsCorrectType)<<" is unknown");
+      }
+
+      initDataArrays();
+      itsFlagCounter.init(getInfo());
+
+      // Check that channels are evenly spaced
+      if (info().nchan()>1) {
+        Vector<Double> upFreq = info().chanFreqs()(
+                                  Slicer(IPosition(1,1),
+                                         IPosition(1,info().nchan()-1)));
+        Vector<Double> lowFreq = info().chanFreqs()(
+                                  Slicer(IPosition(1,0),
+                                         IPosition(1,info().nchan()-1)));
+        Double freqstep0=upFreq(0)-lowFreq(0);
+        // Compare up to 1kHz accuracy
+        bool regularChannels=allNearAbs(upFreq-lowFreq, freqstep0, 1.e3) &&
+                             allNearAbs(info().chanWidths(),
+                                        info().chanWidths()(0), 1.e3);
+        ASSERTSTR(regularChannels, 
+                  "ApplyCal requires evenly spaced channels.");
+      }
+    }
+
+    void OneApplyCal::show (std::ostream& os) const
+    {
+      os << "ApplyCal " << itsName << std::endl;
+      if (itsUseH5Parm) {
+        os << "  H5Parm:         " << itsParmDBName <<endl;
+      } else {
+        os << "  parmdb:         " << itsParmDBName << endl;
+      }
+      os << "  correction:     " << correctTypeToString(itsCorrectType) << endl;
+      if (itsCorrectType==GAIN || itsCorrectType==FULLJONES) {
+        os << "    Ampl/Phase:   " << boolalpha << itsUseAP << endl;
+      }
+      os << "  update weights: " << boolalpha << itsUpdateWeights << endl;
+      os << "  sigmaMMSE:      " << itsSigmaMMSE << endl;
+      os << "  invert:         " << boolalpha << itsInvert <<endl;
+      os << "  timeSlotsPerParmUpdate: " << itsTimeSlotsPerParmUpdate <<endl;
+    }
+
+    void OneApplyCal::showTimings (std::ostream& os, double duration) const
+    {
+      os << "  ";
+      FlagCounter::showPerc1 (os, itsTimer.getElapsed(), duration);
+      os << " OneApplyCal " << itsName << endl;
+    }
+
+    bool OneApplyCal::process (const DPBuffer& bufin)
+    {
+      itsTimer.start();
+      itsBuffer.copy (bufin);
+
+      if (bufin.getTime() > itsLastTime) {
+        updateParms(bufin.getTime());
+        itsTimeStep=0;
+      }
+      else {
+        itsTimeStep++;
+      }
+
+      // Loop through all baselines in the buffer.
+      size_t nbl = itsBuffer.getData().shape()[2];
+
+      Complex* data = itsBuffer.getData().data();
+
+      itsInput->fetchWeights (bufin, itsBuffer, itsTimer);
+      float* weight = itsBuffer.getWeights().data();
+
+      bool* flag = itsBuffer.getFlags().data();
+
+      size_t nchan = itsBuffer.getData().shape()[1];
+
+#pragma omp parallel for
+      for (size_t bl=0; bl<nbl; ++bl) {
+        for (size_t chan=0;chan<nchan;chan++) {
+          uint timeFreqOffset=(itsTimeStep*info().nchan())+chan;
+          uint antA = info().getAnt1()[bl];
+          uint antB = info().getAnt2()[bl];
+          if (itsParms.shape()[0]>2) {
+            ApplyCal::applyFull( &itsParms(0, antA, timeFreqOffset),
+                       &itsParms(0, antB, timeFreqOffset),
+                       &data[bl * itsNCorr * nchan + chan * itsNCorr ],
+                       &weight[bl * itsNCorr * nchan + chan * itsNCorr ],
+                       &flag[  bl * itsNCorr * nchan + chan * itsNCorr ],
+                       bl, chan, itsUpdateWeights, itsFlagCounter);
+          }
+          else {
+            ApplyCal::applyDiag( &itsParms(0, antA, timeFreqOffset),
+                       &itsParms(0, antB, timeFreqOffset),
+                       &data[bl * itsNCorr * nchan + chan * itsNCorr ],
+                       &weight[bl * itsNCorr * nchan + chan * itsNCorr ],
+                       &flag[  bl * itsNCorr * nchan + chan * itsNCorr ],
+                       bl, chan, itsUpdateWeights, itsFlagCounter);
+          }
+        }
+      }
+
+      itsTimer.stop();
+      getNextStep()->process(itsBuffer);
+
+      itsCount++;
+      return true;
+    }
+
+    void OneApplyCal::finish()
+    {
+      // Let the next steps finish.
+      getNextStep()->finish();
+    }
+
+
+    void OneApplyCal::updateParms (const double bufStartTime)
+    {
+      uint numAnts = info().antennaNames().size();
+
+      // itsParms contains the parameters to a grid, first for all parameters
+      // (e.g. Gain:0:0 and Gain:1:1), next all antennas, next over freq * time
+      // as returned by ParmDB
+      vector<vector<vector<double> > > parmvalues;
+      parmvalues.resize(itsParmExprs.size());
+      for (size_t i=0;i<parmvalues.size();++i) {
+        parmvalues[i].resize(numAnts);
+      }
+
+      uint numFreqs         (info().chanFreqs().size());
+      double freqInterval  (info().chanWidths()[0]);
+      if (numFreqs>1) { // Handle data with evenly spaced gaps between channels
+        freqInterval = info().chanFreqs()[1]-info().chanFreqs()[0];
+      }
+      double minFreq       (info().chanFreqs()[0]-0.5*freqInterval);
+      double maxFreq (info().chanFreqs()[numFreqs-1]+0.5*freqInterval);
+      itsLastTime = bufStartTime - 0.5*itsTimeInterval + 
+                    itsTimeSlotsPerParmUpdate * itsTimeInterval;
+      uint numTimes = itsTimeSlotsPerParmUpdate;
+
+      double lastMSTime = info().startTime() + info().ntime() * itsTimeInterval;
+      if (itsLastTime > lastMSTime && !nearAbs(itsLastTime, lastMSTime, 1.e-3)) {
+        itsLastTime = lastMSTime;
+        numTimes = info().ntime() % itsTimeSlotsPerParmUpdate;
+      }
+
+      map<string, vector<double> > parmMap;
+      map<string, vector<double> >::iterator parmIt;
+
+      uint tfDomainSize=numTimes*numFreqs;
+
+      // Fill parmvalues here, get raw data from H5Parm or ParmDB
+      if (itsUseH5Parm) {
+#pragma omp critical(updateH5ParmValues)
+{
+        // TODO: understand polarization etc.
+        ASSERT(itsParmExprs.size()==1 || itsParmExprs.size()==2);
+        hsize_t startTime = itsSolTab.getTimeIndex(bufStartTime);
+        hsize_t startFreq = itsSolTab.getFreqIndex(info().chanFreqs()[0]);
+        for (uint ant = 0; ant < numAnts; ++ant) {
+
+          uint freqUpsampleFactor = numFreqs;
+          if (itsSolTab.hasAxis("freq") && itsSolTab.getAxis("freq").size > 1) {
+            double h5freqinterval = itsSolTab.getFreqInterval();
+            freqUpsampleFactor = h5freqinterval/info().chanWidths()[0] + 0.5; // Round;
+            ASSERT(near(h5freqinterval, freqUpsampleFactor*info().chanWidths()[0],1.e-5));
+          }
+
+          uint timeUpsampleFactor = numTimes;
+          if (itsSolTab.getAxis("time").size > 1) {
+            double h5timeInterval = itsSolTab.getTimeInterval();
+            timeUpsampleFactor = h5timeInterval/itsTimeInterval+0.5; // Round
+            ASSERT(near(h5timeInterval,timeUpsampleFactor*itsTimeInterval,1.e-5));
+          }
+
+          // Figure out whether time or frequency is first axis
+          bool freqvariesfastest = true;
+          if (itsSolTab.hasAxis("freq") &&
+              itsSolTab.getAxisIndex("freq") < itsSolTab.getAxisIndex("time")) {
+            freqvariesfastest = false;
+          }
+          ASSERT(freqvariesfastest);
+
+          // Take the ceiling of numTimes/timeUpsampleFactor, same for freq
+          uint numTimesInH5Parm = (numTimes+timeUpsampleFactor-1)/timeUpsampleFactor;
+          uint numFreqsInH5Parm = (numFreqs+freqUpsampleFactor-1)/freqUpsampleFactor;
+
+          for (uint pol=0; pol<itsParmExprs.size(); ++pol) {
+            vector<double> rawsols;
+            rawsols = itsSolTab.getValues(info().antennaNames()[ant],
+                                        startTime, numTimesInH5Parm, 1,
+                                        startFreq, numFreqsInH5Parm, 1, pol, itsDirection);
+
+            parmvalues[pol][ant].resize(tfDomainSize);
+
+            size_t tf=0;
+            for (uint t=0; t<numTimesInH5Parm; ++t) {
+              for (uint ti=0; ti<timeUpsampleFactor; ++ti) {
+                for (uint f=0; f<numFreqsInH5Parm; ++f) {
+                  for (uint fi=0; fi<freqUpsampleFactor; ++fi) {
+                    if (tf<tfDomainSize) {
+                      parmvalues[pol][ant][tf++] = rawsols[t*numFreqsInH5Parm + f];
+                    }
+                  }
+                }
+              }
+            }
+            ASSERT(tf==tfDomainSize);
+          }
+        }
+} // End pragma omp critical
+      } else { // Use ParmDB
+        for (uint parmExprNum = 0; parmExprNum<itsParmExprs.size();++parmExprNum) {
+          // parmMap contains parameter values for all antennas
+          parmMap = itsParmDB->getValuesMap( itsParmExprs[parmExprNum] + "*",
+                                 minFreq, maxFreq, freqInterval,
+                                 bufStartTime-0.5*itsTimeInterval, itsLastTime,
+                                 itsTimeInterval, true);
+
+          // Resolve {Common,}Bla to CommonBla or Bla
+          if (!parmMap.empty() &&
+              itsParmExprs[parmExprNum].find("{") != std::string::npos) {
+            uint colonPos = (parmMap.begin()->first).find(":");
+            itsParmExprs[parmExprNum] = (parmMap.begin()->first).substr(0, colonPos);
+          }
+
+          for (uint ant = 0; ant < numAnts; ++ant) {
+            parmIt = parmMap.find(
+                      itsParmExprs[parmExprNum] + ":" + info().antennaNames()[ant]);
+
+            if (parmIt != parmMap.end()) {
+              parmvalues[parmExprNum][ant].swap(parmIt->second);
+              ASSERT(parmvalues[parmExprNum][ant].size()==tfDomainSize);
+            } else {// No value found, try default
+              Array<double> defValues;
+              double defValue;
+
+              if (itsParmDB->getDefValues(itsParmExprs[parmExprNum] + ":" +
+                  info().antennaNames()[ant]).size()==1) { // Default for antenna
+                itsParmDB->getDefValues(itsParmExprs[parmExprNum] + ":" +
+                    info().antennaNames()[ant]).get(0,defValues);
+                ASSERT(defValues.size()==1);
+                defValue=defValues.data()[0];
+              }
+              else if (itsParmDB->getDefValues(itsParmExprs[parmExprNum]).size()
+                  == 1) { //Default value
+                itsParmDB->getDefValues(itsParmExprs[parmExprNum]).get(0,defValues);
+                ASSERT(defValues.size()==1);
+                defValue=defValues.data()[0];
+              } else if (itsParmExprs[parmExprNum].substr(0,5)=="Gain:") {
+                defValue=0.;
+              }
+              else {
+                THROW (Exception, "No parameter value found for "+
+                   itsParmExprs[parmExprNum]+":"+info().antennaNames()[ant]);
+              }
+
+              parmvalues[parmExprNum][ant].resize(tfDomainSize);
+              for (uint tf=0; tf<tfDomainSize;++tf) {
+                parmvalues[parmExprNum][ant][tf]=defValue;
+              }
+            }
+          }
+        }
+      }
+
+      ASSERT(parmvalues[0][0].size() <= tfDomainSize); // Catches multiple matches
+
+      double freq;
+
+      // Make parameters complex
+      for (uint tf=0;tf<tfDomainSize;++tf) {
+        for (uint ant=0;ant<numAnts;++ant) {
+
+          freq=info().chanFreqs()[tf % numFreqs];
+
+          if (itsCorrectType==GAIN) {
+            if (itsUseAP) { // Data as Amplitude / Phase
+              itsParms(0, ant, tf) = polar(parmvalues[0][ant][tf],
+                                           parmvalues[1][ant][tf]);
+              itsParms(1, ant, tf) = polar(parmvalues[2][ant][tf],
+                                           parmvalues[3][ant][tf]);
+            } else { // Data as Real / Imaginary
+              itsParms(0, ant, tf) = DComplex(parmvalues[0][ant][tf],
+                                              parmvalues[1][ant][tf]);
+              itsParms(1, ant, tf) = DComplex(parmvalues[2][ant][tf],
+                                              parmvalues[3][ant][tf]);
+            }
+          }
+          else if (itsCorrectType==FULLJONES) {
+            if (itsUseAP) { // Data as Amplitude / Phase
+              itsParms(0, ant, tf) = polar(parmvalues[0][ant][tf],
+                                           parmvalues[1][ant][tf]);
+              itsParms(1, ant, tf) = polar(parmvalues[2][ant][tf],
+                                           parmvalues[3][ant][tf]);
+              itsParms(2, ant, tf) = polar(parmvalues[4][ant][tf],
+                                           parmvalues[5][ant][tf]);
+              itsParms(3, ant, tf) = polar(parmvalues[6][ant][tf],
+                                           parmvalues[7][ant][tf]);
+            } else { // Data as Real / Imaginary
+              itsParms(0, ant, tf) = DComplex(parmvalues[0][ant][tf],
+                                              parmvalues[1][ant][tf]);
+              itsParms(1, ant, tf) = DComplex(parmvalues[2][ant][tf],
+                                              parmvalues[3][ant][tf]);
+              itsParms(2, ant, tf) = DComplex(parmvalues[4][ant][tf],
+                                              parmvalues[5][ant][tf]);
+              itsParms(3, ant, tf) = DComplex(parmvalues[6][ant][tf],
+                                              parmvalues[7][ant][tf]);
+            }
+          }
+          else if (itsCorrectType==TEC) {
+            itsParms(0, ant, tf)=polar(1.,
+                parmvalues[0][ant][tf] * -8.44797245e9 / freq);
+            if (itsParmExprs.size() == 1) { // No TEC:0, only TEC:
+              itsParms(1, ant, tf)=polar(1.,
+                  parmvalues[0][ant][tf] * -8.44797245e9 / freq);
+            }
+            else { // TEC:0 and TEC:1
+              itsParms(1, ant, tf)=polar(1.,
+                  parmvalues[1][ant][tf] * -8.44797245e9 / freq);
+            }
+          }
+          else if (itsCorrectType==CLOCK) {
+            itsParms(0, ant, tf)=polar(1.,
+                parmvalues[0][ant][tf] * freq * casacore::C::_2pi);
+            if (itsParmExprs.size() == 1) { // No Clock:0, only Clock:
+              itsParms(1, ant, tf)=polar(1.,
+                  parmvalues[0][ant][tf] * freq * casacore::C::_2pi);
+            }
+            else { // Clock:0 and Clock:1
+              itsParms(1, ant, tf)=polar(1.,
+                  parmvalues[1][ant][tf] * freq * casacore::C::_2pi);
+            }
+          }
+          else if (itsCorrectType==ROTATIONANGLE) {
+            double phi=parmvalues[0][ant][tf];
+            if (itsInvert) {
+              phi = -phi;
+            }
+            double sinv=sin(phi);
+            double cosv=cos(phi);
+            itsParms(0, ant, tf) =  cosv;
+            itsParms(1, ant, tf) = -sinv;
+            itsParms(2, ant, tf) =  sinv;
+            itsParms(3, ant, tf) =  cosv;
+          }
+          else if (itsCorrectType==ROTATIONMEASURE) {
+            double lambda2 = casacore::C::c / freq;
+            lambda2 *= lambda2;
+            double chi = parmvalues[0][ant][tf] * lambda2;
+            if (itsInvert) {
+              chi = -chi;
+            }
+            double sinv = sin(chi);
+            double cosv = cos(chi);
+            itsParms(0, ant, tf) =  cosv;
+            itsParms(1, ant, tf) = -sinv;
+            itsParms(2, ant, tf) =  sinv;
+            itsParms(3, ant, tf) =  cosv;
+          }
+          else if (itsCorrectType==PHASE || itsCorrectType==SCALARPHASE) {
+            itsParms(0, ant, tf) = polar(1., parmvalues[0][ant][tf]);
+            if (itsCorrectType==SCALARPHASE) { // Same value for x and y
+              itsParms(1, ant, tf) = polar(1., parmvalues[0][ant][tf]);
+            } else { // Different value for x and y
+              itsParms(1, ant, tf) = polar(1., parmvalues[1][ant][tf]);
+            }
+          }
+          else if (itsCorrectType==AMPLITUDE || itsCorrectType==SCALARAMPLITUDE) {
+            itsParms(0, ant, tf) = parmvalues[0][ant][tf];
+            if (itsCorrectType==SCALARAMPLITUDE) { // Same value for x and y
+              itsParms(1, ant, tf) = parmvalues[0][ant][tf];
+            } else { // Different value for x and y
+              itsParms(1, ant, tf) = parmvalues[1][ant][tf];
+            }
+          }
+
+          // Invert
+          if (itsInvert) {
+            if (itsParms.shape()[0]==2) {
+            itsParms(0, ant, tf) = 1./itsParms(0, ant, tf);
+            itsParms(1, ant, tf) = 1./itsParms(1, ant, tf);
+            } else if (itsCorrectType==FULLJONES) {
+              ApplyCal::invert(&itsParms(0, ant, tf),itsSigmaMMSE);
+            } else {
+              ASSERT (itsCorrectType==ROTATIONMEASURE || itsCorrectType==ROTATIONANGLE);
+              // rotationmeasure and commonrotationangle are already inverted above
+            }
+          }
+        }
+      }
+    }
+
+    uint OneApplyCal::nPol(const string& parmName) {
+      if (itsUseH5Parm) {
+        if (!itsSolTab.hasAxis("pol")) {
+          return 1;
+        } else {
+          return itsSolTab.getAxis("pol").size;
+        }
+      } else { // Use ParmDB
+        if (itsParmDB->getNames(parmName+":0:*").empty() &&
+                    itsParmDB->getDefNames(parmName+":0:*").empty() ) {
+          return 1;
+        } else {
+          return 2;
+        }
+      }
+    }
+
+    void OneApplyCal::initDataArrays() {
+      uint numAnts=info().antennaNames().size();
+      uint tfDomainSize=itsTimeSlotsPerParmUpdate*info().chanFreqs().size();
+
+      uint numParms;
+      if (itsCorrectType==FULLJONES ||
+          itsCorrectType==ROTATIONANGLE ||
+          itsCorrectType==ROTATIONMEASURE) {
+        numParms = 4;
+      }
+      else {
+        numParms = 2;
+      }
+
+      itsParms.resize(numParms, numAnts, tfDomainSize);
+    }
+
+    void OneApplyCal::showCounts (std::ostream& os) const
+    {
+      os << endl << "Flags set by OneApplyCal " << itsName;
+      os << endl << "=======================" << endl;
+      itsFlagCounter.showBaseline (os, itsCount);
+      itsFlagCounter.showChannel  (os, itsCount);
+    }
+
+  } //# end namespace
+}
diff --git a/CEP/DP3/DPPP/test/CMakeLists.txt b/CEP/DP3/DPPP/test/CMakeLists.txt
index 5a8912b708dc2651e500f7e8bd3ebd5409529d60..4ec9ebecd794b3d76977d3f3ed69aaec27b4ab96 100644
--- a/CEP/DP3/DPPP/test/CMakeLists.txt
+++ b/CEP/DP3/DPPP/test/CMakeLists.txt
@@ -18,6 +18,7 @@ lofar_add_test(tStationAdder tStationAdder.cc)
 lofar_add_test(tScaleData tScaleData.cc)
 lofar_add_test(tApplyCal tApplyCal.cc)
 lofar_add_test(tApplyCal2)
+lofar_add_test(tMultiApplyCal)
 lofar_add_test(tFilter tFilter.cc)
 #lofar_add_test(tDemixer tDemixer.cc)
 lofar_add_test(tNDPPP tNDPPP.cc)
diff --git a/CEP/DP3/DPPP/test/tApplyCal2.run b/CEP/DP3/DPPP/test/tApplyCal2.run
index f54aeb086f3288bff9b1de8aa04fb9bdd48cef4a..72094bc40ed1cadd54b432214d8dea29cf34a7e2 100755
--- a/CEP/DP3/DPPP/test/tApplyCal2.run
+++ b/CEP/DP3/DPPP/test/tApplyCal2.run
@@ -32,14 +32,18 @@ adddef Gain:1:1:Real values=3.
 EOL
 
 echo; echo "Testing without updateweights"
-NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 msout.weightcolumn=WEIGHTS_NEW steps=[applycal] applycal.parmdb=tApplyCal.parmdb showcounts=false
+cmd='NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 msout.weightcolumn=WEIGHTS_NEW steps=[applycal] applycal.parmdb=tApplyCal.parmdb showcounts=false'
+echo $cmd
+eval $cmd
 $taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=9*DATA3))' > taql.out
 diff taql.out taql.ref  ||  exit 1
 $taqlexe 'select from tNDPPP-generic.MS where not(all(WEIGHTS_NEW~=WEIGHT_SPECTRUM))' > taql.out
 diff taql.out taql.ref  ||  exit 1
 
 echo; echo "Testing with updateweights"
-NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 msout.weightcolumn=WEIGHTS_NEW steps=[applycal] applycal.parmdb=tApplyCal.parmdb showcounts=false applycal.updateweights=true
+cmd='NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 msout.weightcolumn=WEIGHTS_NEW steps=[applycal] applycal.parmdb=tApplyCal.parmdb showcounts=false applycal.updateweights=true'
+echo $cmd
+eval $cmd
 $taqlexe 'select from tNDPPP-generic.MS where not(all(WEIGHTS_NEW~=81*WEIGHT_SPECTRUM))' > taql.out
 diff taql.out taql.ref  ||  exit 1
 
@@ -49,7 +53,9 @@ parmdbm <<EOL
 open table="tApplyCal.parmdb"
 adddef CommonScalarPhase values=0
 EOL
-NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=commonscalarphase showcounts=false
+cmd='NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=commonscalarphase showcounts=false'
+echo $cmd
+eval $cmd
 $taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=DATA3))' > taql.out
 diff taql.out taql.ref  ||  exit 1
 
@@ -59,7 +65,9 @@ parmdbm <<EOL
 open table="tApplyCal.parmdb"
 adddef ScalarPhase values=0
 EOL
-NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=commonscalarphase showcounts=false
+cmd='NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=commonscalarphase showcounts=false'
+echo $cmd
+eval $cmd
 $taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=DATA3))' > taql.out
 diff taql.out taql.ref  ||  exit 1
 
@@ -76,7 +84,9 @@ add ScalarPhase:RS208HBA values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735,
 add ScalarPhase:RS305HBA values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2]
 add ScalarPhase:RS307HBA values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2]
 EOL
-NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=commonscalarphase showcounts=false
+cmd='NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=commonscalarphase showcounts=false'
+echo $cmd
+eval $cmd
 $taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=DATA3))' > taql.out
 diff taql.out taql.ref  ||  exit 1
 
@@ -93,7 +103,9 @@ add ScalarAmplitude:RS208HBA values=[3.,3.,3.,3.], domain=[10e6, 200e6, 44720257
 add ScalarAmplitude:RS305HBA values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2]
 add ScalarAmplitude:RS307HBA values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2]
 EOL
-NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=scalaramplitude showcounts=false
+cmd='NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=scalaramplitude showcounts=false'
+echo $cmd
+eval $cmd
 $taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=9*DATA3))' > taql.out
 diff taql.out taql.ref  ||  exit 1
 
diff --git a/CEP/DP3/DPPP/test/tMultiApplyCal.run b/CEP/DP3/DPPP/test/tMultiApplyCal.run
new file mode 100755
index 0000000000000000000000000000000000000000..31762901986ee3d5555093f4f2774538b5cd84e9
--- /dev/null
+++ b/CEP/DP3/DPPP/test/tMultiApplyCal.run
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+# Get the taql executable and srcdir (script created by cmake's CONFIGURE_FILE).
+source findenv.run_script
+echo "srcdirx=$rt_srcdir"
+
+# Set srcdir if not defined (in case run by hand).
+if test "$srcdir" = ""; then
+  srcdir="$rt_srcdir"
+fi
+
+if test ! -f ${srcdir}/tNDPPP-generic.in_MS.tgz; then
+  exit 3   # untested
+fi
+
+set -e # Stop on any error
+
+rm -rf tMultiApplyCal_tmp
+mkdir -p tMultiApplyCal_tmp
+# Unpack the MS and other files and do the DPPP run.
+cd tMultiApplyCal_tmp
+tar zxf ${srcdir}/tNDPPP-generic.in_MS.tgz
+
+# Create expected taql output.
+echo "    select result of 0 rows" > taql.ref
+
+echo; echo "Creating parmdb with defvalues 3"
+parmdbm <<EOL
+open table="tApplyCal.parmdb"
+adddef Gain:0:0:Real values=3.
+adddef Gain:1:1:Real values=3.
+adddef CommonScalarPhase values=0
+EOL
+
+cmd='NDPPP msin=tNDPPP-generic.MS checkparset=1 msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.steps="[gain,csp]" applycal.gain.parmdb=tApplyCal.parmdb applycal.gain.correction=gain applycal.csp.parmdb=tApplyCal.parmdb applycal.csp.correction=commonscalarphase showcounts=false'
+echo $cmd
+eval $cmd
+$taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=9*DATA3))' > taql.out
+diff taql.out taql.ref  ||  exit 1
+
+cmd='NDPPP msin=tNDPPP-generic.MS checkparset=1 msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.steps="[gain,csp]" applycal.parmdb=tApplyCal.parmdb applycal.gain.correction=gain applycal.csp.correction=commonscalarphase showcounts=false'
+echo $cmd
+eval $cmd
+$taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=9*DATA3))' > taql.out
+diff taql.out taql.ref  ||  exit 1
+
diff --git a/CEP/DP3/DPPP/test/tMultiApplyCal.sh b/CEP/DP3/DPPP/test/tMultiApplyCal.sh
new file mode 100755
index 0000000000000000000000000000000000000000..12b39f281e6f9ca66c4a064ebd0f89b5422d5b63
--- /dev/null
+++ b/CEP/DP3/DPPP/test/tMultiApplyCal.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+./runctest.sh tMultiApplyCal
diff --git a/CEP/DP3/DPPP_DDECal/test/tDDECal.run b/CEP/DP3/DPPP_DDECal/test/tDDECal.run
index 62b0dc972cabf60394666cce7ba7d4f4417a7e48..ae6ac580fb043ba43d1a8bf022ba098a3f296875 100755
--- a/CEP/DP3/DPPP_DDECal/test/tDDECal.run
+++ b/CEP/DP3/DPPP_DDECal/test/tDDECal.run
@@ -40,7 +40,7 @@ for caltype in complexgain scalarcomplexgain amplitudeonly scalaramplitude
 do
   for solint in 0 1 2 4
   do
-    for nchan in 0 1 2 5
+    for nchan in 1
     do
           rm -rf instrument.h5 # Remove h5parm if it exists
           echo "Calibrate on the original sources, caltype=$caltype"
@@ -106,7 +106,8 @@ cmd="NDPPP checkparset=1 msin=tDDECal.MS msout=. numthreads=4\
   steps=[ddecal]\
     ddecal.sourcedb=tDDECal.MS/sky\
     ddecal.directions=[[center,dec_off],[ra_off],[radec_off]]\
-    ddecal.applycal.parmdb=instrument.h5 ddecal.applycal.correction=amplitude000\
+    ddecal.applycal.parmdb=instrument.h5 ddecal.applycal.steps=applyampl\
+    ddecal.applycal.applyampl.correction=amplitude000\
     ddecal.h5parm=instrument2.h5 ddecal.mode=scalarcomplexgain"
 echo $cmd
 $cmd