diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/ITileRawChannelTool.h b/TileCalorimeter/TileRecUtils/TileRecUtils/ITileRawChannelTool.h
new file mode 100755
index 0000000000000000000000000000000000000000..8389b927aeed9a55552670694c0e7e031d4841e3
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/ITileRawChannelTool.h
@@ -0,0 +1,27 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_ITILERAWCHANNELTOOL_H
+#define TILERECUTILS_ITILERAWCHANNELTOOL_H
+
+
+class TileRawChannelContainer ;
+
+// Includes for Gaudi
+#include "GaudiKernel/StatusCode.h"
+#include "GaudiKernel/IAlgTool.h"
+
+
+static const InterfaceID IID_ITileRawChannelTool("ITileRawChannelTool", 1 , 0);
+
+class ITileRawChannelTool: virtual public IAlgTool {
+
+public:
+  // update TileRawChannelContainer, subtract common mode noise for example
+  virtual StatusCode process( const TileRawChannelContainer * rchCnt)=0 ;
+
+  static const InterfaceID& interfaceID() { return IID_ITileRawChannelTool;}
+};
+
+#endif 
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileBeamInfoProvider.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileBeamInfoProvider.h
new file mode 100755
index 0000000000000000000000000000000000000000..13f1cd98d3cc7aaa8dad8019e5c3ef9ccf1346c2
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileBeamInfoProvider.h
@@ -0,0 +1,350 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILEBEAMINFOPROVIDER_H
+#define TILERECUTILS_TILEBEAMINFOPROVIDER_H
+
+/********************************************************************
+ *
+ * NAME:     TileBeamInfoProvider
+ * PACKAGE:  offline/TileCalorimeter/TileRecUtils
+ *
+ * AUTHOR :  A. Solodkov
+ * CREATED:  June 06, 2004
+ *
+ * PURPOSE:  Re
+ *
+ *  Input: TileBeamElemContainer
+ *  Output: contents of cispar fragment, header, status and trigType
+ *  Parameters:
+ *    TileBeamElemContainer - Name of input container
+ *   
+ ********************************************************************/
+
+// Atlas includes
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+#include "AthenaBaseComps/AthAlgTool.h"
+
+#include "GaudiKernel/IIncidentListener.h"
+class TileHWID;
+class TileRawChannelCollection;
+class StoreGateSvc;
+class TileBeamInfoProvider;
+class TileDigitsContainer;
+class TileRawChannelContainer;
+class TileBeamElemContainer;
+class TileDCSSvc;
+class ITileBadChanTool;
+class IAtRndmGenSvc;
+namespace CLHEP {
+  class HepRandomEngine;
+}
+
+
+#include <string>
+#include <vector>
+
+#include <inttypes.h>
+
+/**
+@class coincBoard
+@brief Small class holding the information for a cosmic trigger coincidence board 
+*/
+class coincBoard {
+public:
+  bool trig[96];  //!< each component of this array correspond to a single tower of a module (12 modules x 8 towers = 96 components)
+  uint32_t amp;
+};
+
+/**
+* @class TileDQstatus
+* @brief Class that holds Data Quality fragment information and provides functions to extract the data quality status for channels
+* 		 Checks for the following types of errors stored in the Data Quality fragment:
+*		 	- Global CRC
+*		 	- ROD CRC
+*		 	- Front End CRC
+*			- BCID Mismatch
+*		 	- Header Format
+*		 	- Header Parity
+*		 	- Sample Format 
+*		 	- Sample Parity
+*		 	- Single Strobe
+*		 	- Double Strobe
+*/
+class TileDQstatus {
+
+ friend class TileBeamInfoProvider;
+ public:
+
+  /** @brief Default constructor */
+  TileDQstatus();
+  /** @brief Destructor */
+  ~TileDQstatus();
+
+  /** @brief returns whether TileDQStatus has been filled with data from TileDQ fragment */
+  inline bool   isFilled        ()              const           { return m_isFilled; }
+  /** @brief returns gain mode of run */
+  inline bool   isBiGain        ()              const           { return m_isBiGain; }
+  /** @brief returns True if there are any errors in event */
+  inline bool   nonZeroCounter  ()              const           { return m_counter;  }
+  /** @brief returns status of single ADC 
+  			 returns False if there are any errors*/
+  bool          isAdcDQgood     (int partition, int drawer, int ch, int gain) const;
+  /** @brief returns status of single channel (if bigain, returns AND of ADCs' status*/
+  bool          isChanDQgood    (int partition, int drawer, int ch) const;
+  /** @brief True if channel is not fully implemented */
+  static int    isChEmpty       (int partition, int drawer, int ch);
+  /** @brief returns 0 if event is ok, 1 if empty LG event, 2 if empty HG event, 3 if empty event in both gains */
+  inline int checkEmptyEvent(int partition, int drawer, int dmu) const {
+	return (m_EmptyEventArray[partition][drawer][dmu][0]+((m_EmptyEventArray[partition][drawer][dmu][1]) << 1));
+  }
+  
+  /** @brief returns 1 if adc channel has global CRC error */
+  inline int checkGlobalCRCErr(int partition, int drawer, int gain) const {
+    return m_GlobalCRCErrArray[partition][drawer][gain] & 1;
+  }
+  /** @brief returns 1 if DMU has CRC error originating in ROD */
+  inline int checkROD_CRCErr(int partition, int drawer, int dmu, int gain) const {
+    return (1 - ((m_ROD_DMUmaskArray[partition][drawer][gain] >> dmu) & 1));
+  }
+/** @brief returns 1 if DMU has CRC error originating in FE electronics */
+  inline int checkFE_CRCErr(int partition, int drawer, int dmu, int gain) const {
+      return (1 - ((m_FE_DMUmaskArray[partition][drawer][gain] >> dmu) & 1)); 
+  }
+  /** @brief returns 1 if DMU has BCID mismatch between DMU and ROD  
+  * Since BCID errors in the DQ fragment are determined by comparison of each DMU to DMU1, 
+  * if DMU1 is bad and DMUX is also bad, we must compare the individual DMU's BCID to the 
+  * actual (ROD) BCID. This is due to the fact that DMU1 is checked against the ROD, while 
+  * the others DMUs are checked against DMU1.  This requires access to the digits in the 
+  * function checkBCIDErrDetail.  If this behavior is undesired set m_checkDigi to false in 
+  * TileBeamInfoProvider.  Setting m_checkDigi=false would cause any BCID errors 
+  * simultaneous with DMU1 to be flagged as BAD (conservative approach)
+  */
+  inline int checkBCIDErr(int partition, int drawer, int dmu, int gain) const {
+      return ((m_BCIDErrArray[partition][drawer][gain] & 0x2) && m_checkDigi) 
+          ? this->checkBCIDErrDetail(partition,drawer,dmu,gain) :
+          ((m_BCIDErrArray[partition][drawer][gain] >> dmu) & 1);
+  }
+  /** @brief returns 1 if DMU has BCID mismatch between DMU and ROD
+  *   This method accesses the TileDigitsContainer in order to explicitly
+  *   check the DMU BCID with the ROD BCID.
+  **/
+  inline int checkBCIDErrDetail(int partition, int drawer, int dmu, int gain) const {
+      return (m_BCIDErrArrayDetail[partition][drawer][gain] >> dmu) & 1;
+  }
+  /** @brief returns 1 if DMU has header word format error */
+  inline int checkHeaderFormatErr(int partition, int drawer, int dmu, int gain) const {
+    return (m_HeaderFormatErrArray[partition][drawer][gain] >> dmu) & 1;
+  }
+  /** @brief returns 1 if DMU has header word parity error */
+  inline int checkHeaderParityErr(int partition, int drawer, int dmu, int gain) const {
+    return (m_HeaderParityErrArray[partition][drawer][gain] >> dmu) & 1;
+  }
+  /** @brief returns 1 if DMU has data word format error */
+  inline int checkSampleFormatErr(int partition, int drawer, int dmu, int gain) const {
+    return (m_SampleFormatErrArray[partition][drawer][gain] >> dmu) & 1;
+  }
+  /** @brief returns 1 if DMU has data word parity error */
+  inline int checkSampleParityErr(int partition, int drawer, int dmu, int gain) const {
+    return (m_SampleParityErrArray[partition][drawer][gain] >> dmu) & 1;
+  }
+  /** @brief returns 1 if DMU has memory parity error */
+  inline int checkMemoryParityErr(int partition, int drawer, int dmu, int gain) const {
+    return (m_MemoryParityErrArray[partition][drawer][gain] >> dmu) & 1;
+  }
+  /** @brief returns 1 if DMU has single strobe error */
+  inline int checkSingleStrobeErr(int partition, int drawer, int dmu, int gain) const {
+    return (m_SingleStrobeErrArray[partition][drawer][gain] >> dmu) & 1;
+  }
+  /** @brief returns 1 if DMU has double strobe error */
+  inline int checkDoubleStrobeErr(int partition, int drawer, int dmu, int gain) const {
+    return (m_DoubleStrobeErrArray[partition][drawer][gain] >> dmu) & 1;
+  }
+ 
+ protected:
+
+  /** @brief sets flag that DQ status instance has been filled for this event */
+  void setFilled(bool filled) {m_isFilled=filled;}
+  /** @brief sets flag of gain mode of run */
+  void setBiGain(bool biGain) {m_isBiGain=biGain;}
+  /** @brief mark all channels/ADC's as DQ good */
+  void setAllGood();
+  /** @brief sets the ROD BCID stored and used in DQStatus  */
+  void setRODBCID(uint32_t BCID)   {m_BCID=BCID;}
+  /** @brief sets flag to control the access of TileDigitsContainer  */
+  void setCheckDigi(bool checkDigi) {m_checkDigi=checkDigi;}
+  /** @brief sets flag that DMU sent an empty event (0xFFFFFFFF) */
+  inline void setEmptyEvent(int partition, int drawer, int dmu, int gain, int isEmpty) {
+    m_EmptyEventArray[partition][drawer][dmu][gain]=isEmpty;
+    m_counter+=isEmpty;
+  }
+  /** @brief sets TileBeamInfoProvider instance to argument 
+   *  @param[in] TBIP Pointer to a TileBeamInfoProvider instance 
+   */
+  void setTBIP(TileBeamInfoProvider* TBIP) {m_TBIP=TBIP;}
+  /** @brief parses DQ fragments and fill error arrays for event*/
+  void fillArrays(const TileRawChannelCollection * coll, int gain);
+  void fillBCIDErrDetail(int frag, int gain);
+  void fillTrips(unsigned int partition, const std::vector<float>& trips, double* rndmVec);
+
+ private:
+
+  /** Pointer to TileBeamInfoProvider that contains TileDQStatus instance */
+  TileBeamInfoProvider* m_TBIP;
+  /** Boolean storing if DQ fragment has been parsed already */
+  bool  m_isFilled;
+  /** Boolean storing gain mode of run */
+  bool  m_isBiGain;
+  /** Boolean flag to control TileDigitsContainer access  */
+  bool  m_checkDigi;
+  /** Event bunch crossing identification */
+  uint32_t   m_BCID;
+  /** Counter of non-zero elements in all error arrays */
+  int   m_counter;
+  /** Array storing whether event is empty */
+  short m_EmptyEventArray     [5][64][16][2];
+  /** Array of bit masks storing CRC errors for all DMUs*/
+  short m_GlobalCRCErrArray   [5][64][2];
+  /** Array of bit masks storing CRC errors for all DMUs */
+  short m_FE_DMUmaskArray     [5][64][2];
+  /** Array of bit masks storing CRC errors for all DMUs */
+  short m_ROD_DMUmaskArray    [5][64][2];
+  /** Array of bit masks storing BCID errors for all DMUs (from comparison with DMU1) */
+  short m_BCIDErrArray        [5][64][2];
+  /** Array of bit masks storing BCID errors for all DMUs (from comparison with Digits) */
+  short m_BCIDErrArrayDetail  [5][64][2];
+  /** Array of bit masks storing Header Format errors for all DMUs */
+  short m_HeaderFormatErrArray[5][64][2];
+  /** Array of bit masks storing Header Parity errors for all DMUs */
+  short m_HeaderParityErrArray[5][64][2];
+  /** Array of bit masks storing Sample Format errors for all DMUs */
+  short m_SampleFormatErrArray[5][64][2];
+  /** Array of bit masks storing Sample Parity errors for all DMUs */
+  short m_SampleParityErrArray[5][64][2];
+  /** Array of bit masks storing Memory Parity errors for all DMUs */
+  short m_MemoryParityErrArray[5][64][2];
+  /** Array of bit masks storing Single Strobe errors for all DMUs */
+  short m_SingleStrobeErrArray[5][64][2];
+  /** Array of bit masks storing Double Strobe errors for all DMUs */
+  short m_DoubleStrobeErrArray[5][64][2];
+
+  static const int ch2dmuLB[48];
+  static const int ch2dmuEB[48];
+  static const int ch2dmuEBspecial[48];
+};
+
+/**
+@class TileBeamInfoProvider
+@brief This class provides the contents of cispar fragment, header, status and trigType from the objects in a TileBeamElemContainer
+*/
+class TileBeamInfoProvider: public AthAlgTool
+                          , virtual public IIncidentListener {
+ friend class TileDQstatus;
+ public:
+  TileBeamInfoProvider(const std::string& type, const std::string& name, const IInterface* parent); //!< Constructor
+
+  virtual ~TileBeamInfoProvider(); //!< Destructor
+
+  virtual StatusCode initialize();  //!< intialize method
+  virtual StatusCode finalize();    //!< finalize method
+  
+  virtual void handle(const Incident&) ;   //!< Callback for Incident service 
+
+  static const InterfaceID& interfaceID( ) ; //!< AlgTool InterfaceID
+  
+  const TileDQstatus* getDQstatus(); //<! Creates TileDQstatus object and fills arrays from DQ fragment
+
+  bool          isChanDCSgood   (int partition, int drawer, int channel) const; 
+
+  inline uint32_t   eventCounter(void)  const { return m_evt; }  
+  inline uint32_t      calibMode(void)  const { return m_calibMode; }
+  inline int            trigType(void)  const { return m_trigType; }
+  inline uint32_t      laserFlag(void)  const { return m_laserFlag; }
+  inline uint32_t       digiSize(void)  const { return m_digiSize; }
+  inline uint32_t           BCID(void)  const { return m_BCID; }
+  inline const uint32_t * cispar(void)  const { return m_cispar; }
+  inline const uint32_t * laspar(void)  const { return m_laspar; }
+  uint32_t        checkCalibMode(void);
+  inline const std::vector<coincBoard> & getCoincBoard(void) const { return m_coincTrig; }
+
+  void setContainers(const TileDigitsContainer * digitsCnt = NULL,
+                     const TileRawChannelContainer * rcCnt = NULL,
+                     const TileBeamElemContainer * beamElemCnt = NULL);
+
+  const TileRawChannelContainer * dspContainer() const { return m_rcCnt;}
+  bool noiseFilterApplied() const { return m_noiseFilterApplied;}
+  void setNoiseFilterApplied(bool val=true) {m_noiseFilterApplied=val;}
+  bool incompleteDigits() const { return m_incompleteDigits;}
+
+ protected:
+
+  bool m_checkDQ;   //!< if false, skip DQ checks (set to false if container is not found in first event)
+  bool m_checkDigi; //!< if false, skip reading of TileDigitsContainer ( /-/-/-/ )
+  bool m_checkBeam; //!< if false, skip reading of TileBeamContainer   ( /-/-/-/ )
+  bool m_checkDCS;  //!< if false, do not use TileDCSSvc at all
+  bool m_simulateTrips;  //! if true simulate drawer trips
+
+  std::string m_TileBeamContainerID;    //!< Name of the TileBeamElemContainer
+  std::string m_TileDigitsContainerID;  //!< Name of the TileDigitsContainer
+  std::string m_TileRawChContainerID;   //!< Name of the TileRawChannelContainer
+  std::string m_TileTriggerContainerID; //!< Name of the TileTriggerContainer
+  std::string m_TileLaserObjectID;      //!< Name of the TileLaserObject
+
+  ServiceHandle<TileDCSSvc>   m_tileDCSSvc; //!< Pointer to TileDCSSvc
+  ServiceHandle<IAtRndmGenSvc> m_rndmSvc;  //!< Random number service to use
+  ToolHandle<ITileBadChanTool> m_tileBadChanTool; //!< Tool which provides trips probabilities also
+
+  const TileHWID* m_tileHWID; //!< Pointer to TileHWID
+
+  int      m_trigType;     //!< Trigger type
+  uint32_t m_laserFlag;    //!< Laser Flag
+  uint32_t m_cispar[110];  //!< Parameter for CIS
+  uint32_t m_laspar[32];   //!< Parameter for LASER
+
+  uint32_t m_calibMode;    //!< Calibration mode
+
+  uint32_t m_digiSize;     //!< number of samples in digits
+
+  uint32_t m_BCID;         //!< BCID in LASTROD
+
+  uint32_t m_evt;          //!< number of event
+
+  std::vector<coincBoard> m_coincTrig; // vector with instances of the coincBoard class
+
+  TileDQstatus m_DQstatus; //<! DQStatus
+
+  const TileDigitsContainer *     m_digitsCnt;
+  const TileRawChannelContainer * m_rcCnt;
+  const TileBeamElemContainer *   m_beamElemCnt;
+  
+  CLHEP::HepRandomEngine* m_pHRengine;    //!< Random number generator engine to use
+  double* m_rndmVec;
+
+  bool m_noiseFilterApplied;    //!< True if noise filter is applied to DSP container
+
+  bool m_incompleteDigits;    //!< True if not all digits are available (taken from frag 1)
+
+};
+
+// if PMT does not exist, returns 1 
+// if DMU does not exist, returns 2
+__attribute__((always_inline)) inline
+int TileDQstatus::isChEmpty(int partition, int drawer, int ch) {
+  
+  switch (partition) {
+    case 1:
+      return ch2dmuLB[ch];
+    case 2:
+      return ch2dmuLB[ch];    
+    case 3:
+      return (drawer != 14) ? ch2dmuEB[ch] : ch2dmuEBspecial[ch];
+    case 4:
+      return (drawer != 17) ? ch2dmuEB[ch] : ch2dmuEBspecial[ch];
+    default:
+      return 0;
+  }  
+}
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellBuilder.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellBuilder.h
new file mode 100755
index 0000000000000000000000000000000000000000..641e3168686d56bee0dc75967b3136ee5e228ac5
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellBuilder.h
@@ -0,0 +1,320 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILECELLBUILDER_H
+#define TILERECUTILS_TILECELLBUILDER_H
+
+/********************************************************************
+ *
+ * NAME:     TileCellBuilder
+ * PACKAGE:  offline/TileCalorimeter/TileRecUtils
+ *
+ * AUTHOR :  A. Solodkov
+ * CREATED:  20-Jun-2004
+ *
+ * PURPOSE:  Create Cells from RawChannels and store them in container
+ *
+ *  Input: TileRawChannel (one by one or from TileRawChannelContainer)
+ *  Output: Container or collection with TileCells
+ *  Parameters:
+ *    TileRawChannelContainer - Name of input container
+ *   
+ ********************************************************************/
+
+// Gaudi includes
+#include "GaudiKernel/ToolHandle.h"
+#include "GaudiKernel/ServiceHandle.h"
+
+// Atlas includes
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "Identifier/HWIdentifier.h"
+#include "AthenaKernel/IOVSvcDefs.h"
+
+// Calo includes
+#include "CaloInterface/ICaloCellMakerTool.h"
+#include "CaloConditions/CaloAffectedRegionInfo.h"
+
+// Tile includes
+#include "TileEvent/TileCellContainer.h"
+#include "TileIdentifier/TileFragHash.h"
+#include "TileIdentifier/TileRawChannelUnit.h"
+#include "TileRecUtils/TileBeamInfoProvider.h"
+
+// forward declarations
+class TileID;
+class TileTBID;
+class TileHWID;
+class TileCell;
+class TileRawChannel;
+class MbtsDetDescrManager;
+class TileDetDescrManager;
+class TileCellCollection;
+class CaloCellContainer;
+class ITileBadChanTool;
+class TileCondToolEmscale;
+class TileCondToolTiming;
+class ITileRawChannelTool;
+
+
+// C++ STL includes
+#include <string>
+#include <vector>
+
+/**
+ @class TileDrawerEvtStatus
+ @brief This class keep detailed status info about one drawer in a given event
+ 
+ */
+class TileDrawerEvtStatus {
+  public:
+    int nChannels;
+    int nMaskedChannels;
+    int nBadQuality;
+    int nOverflow;
+    int nUnderflow;
+    int nSomeSignal;
+};
+
+/**
+ @class TileDrawerRunStatus
+ @brief This class keep detailed status info about one drawer in whole run
+ 
+ */
+class TileDrawerRunStatus {
+  public:
+    int drawerAbsent;
+    int drawerMasked;
+    int channelsMasked;
+};
+
+/**
+ @class TileCellBuilder
+ @brief This class creates Cells from RawChannels and stores them in a container
+ 
+ */
+class TileCellBuilder: public AthAlgTool, virtual public ICaloCellMakerTool {
+    friend class DoubleVectorIterator;
+  public:
+    TileCellBuilder(const std::string& type, const std::string& name, const IInterface* parent); //!< Contructor
+
+    virtual ~TileCellBuilder(); //!< Destructor
+
+    virtual StatusCode initialize();                     //!< initialize mehtod
+
+    //!< Callback added to handle Data-driven GeoModel initialisation
+    virtual StatusCode geoInit(IOVSVC_CALLBACK_ARGS);
+
+    void reset(bool fullSizeCont, bool printReset = true); //!< Method to reset the options of the TileCellContainer
+
+    /**
+     This method sets the type and unit for the TileRAwChannels. It
+     might be called from TileROD_Decoder
+     otherwise it isn't needed - type and unit are available from 
+     TileRawChannelContainer itself (see TileCellBuilder::process() below)
+     */
+    void set_type_and_unit(TileFragHash::TYPE type = TileFragHash::Default
+        , TileRawChannelUnit::UNIT unit = TileRawChannelUnit::ADCcounts);
+
+    virtual StatusCode finalize(); //!< finalize method
+
+    virtual StatusCode process(CaloCellContainer* theCellContainer); // method to process all raw channels and store them in container
+
+    template<class ITERATOR, class COLLECTION>
+    void build(const ITERATOR & begin, const ITERATOR & end, COLLECTION * coll); //!< method to process raw channels from a given vector and store them in collection
+
+    /** method to check if channels are good or bad. Puts zero if both channels are bad
+     or recovers from single-channel failure. It returns true if cell was changed, false otherwise
+     */
+    bool maskBadChannel(TileCell* pCell, HWIdentifier hwid);
+    bool maskBadChannels(TileCell* pCell);
+
+    //AlgTool InterfaceID
+    static const InterfaceID& interfaceID();
+    //static const InterfaceID& interfaceID() { return ICaloCellMakerTool; };
+
+  protected:
+    // properties
+    std::string m_rawChannelContainer;
+    std::string m_infoName;
+    std::string m_MBTSContainer;
+    std::string m_dspRawChannelContainer;
+
+    float m_eneForTimeCut;        //!< keep time for channels with energy above cut
+    float m_eneForTimeCutMBTS;    //!< similar cut for MBTS in pC
+    float m_zeroEnergy;           //!< energy to store in every PMT if both PMT are bad
+    int m_qualityCut;           //!< cut on channel quality (set energy to m_zeroEnergy for them)
+    bool m_correctTime;          //!< should time be corrected (deltat added from CondDB)
+    bool m_correctAmplitude; //!< If true, amplitude is corrected by parabolic function (needed for OF without iterations)
+    bool m_mergeChannels;        //!< If true, missing raw channels are taken from DSP container
+
+    // thresholds for parabolic amplitude correction
+    float m_ampMinThresh;   //!< correct amplitude if it's above amplitude threshold (in ADC counts)
+    float m_timeMinThresh;                //!< correct amplitude is time is above time min threshold
+    float m_timeMaxThresh;                //!< correct amplitude is time is below time max threshold
+
+    float m_minEneChan[3];        //!< channel energy thresholds for masking (normal,gap,mbts)
+    float m_eThreshold;           //!< cell energy threshold to consider the cell
+    float m_maxTimeDiff;          //!< maximum time difference between the PMTs in the cell
+    float m_maxTime;              //!< maximum time for the PMTs in the cels
+    float m_minTime;              //!< minimum time for the PMTs in the cels
+    float m_maxChi2;              //!< maximum chi2 for the PMTs in the cels
+    float m_minChi2;              //!< minimum chi2 for the PMTs in the cels
+    bool m_thresholdNotSet;      //!< bool variable to check wether some threshold have been set
+    bool m_fullSizeCont;
+    bool m_maskBadChannels;      //!< if true=> bad channels are masked
+    bool m_fakeCrackCells;       //!< if true=> fake E3/E4 cells added
+    int  m_skipGain;             //!< for two-gain calib runs skip one of two gains
+
+    const TileID* m_tileID;   //!< Pointer to TileID
+    const TileTBID* m_tileTBID; //!< Pointer to TileTBID
+    const TileHWID* m_tileHWID; //!< Pointer to TileHWID
+    const TileDQstatus* theDQstatus;
+
+    ToolHandle<ITileBadChanTool> m_tileBadChanTool; //!< Tile Bad Channel tool
+    ToolHandle<TileCondToolEmscale> m_tileToolEmscale; //!< main Tile Calibration tool
+    ToolHandle<TileCondToolTiming> m_tileToolTiming;  //!< Tile Timing Calibration tool
+    ToolHandle<TileBeamInfoProvider> m_beamInfo; //!< Beam Info tool to get the DQ Status object
+    ToolHandleArray<ITileRawChannelTool> m_noiseFilterTools; //!< noise filter tools to apply
+
+    const TileDetDescrManager* m_tileMgr; //!< Pointer to TileDetDescrManager
+    const MbtsDetDescrManager* m_mbtsMgr; //!< Pointer to MbtsDetDescrManager
+
+    std::vector<TileCell*> m_allCells;  //!< vector to of pointers to TielCells
+    TileCellContainer* m_MBTSCells;     //!< Pointer to MBTS cell container
+
+    TileFragHash::TYPE m_RChType;        //!< Type of TileRawChannels (Fit, OF2, etc.)
+    TileRawChannelUnit::UNIT m_RChUnit;  //!< Unit for TileRawChannels (ADC, pCb, etc.)
+    //unsigned int m_bsflags;              //!< other flags stored in TileRawChannelContainer
+    float m_maxTimeCorr;                 //!< max possible time when time correction is applied
+
+    TileDrawerEvtStatus m_drawerEvtStatus[5][64]; //!< status of every drawer in every event
+    TileDrawerRunStatus m_drawerRunStatus[5][64]; //!< overall status of drawer in whole run
+    int m_eventErrorCounter[4]; //!< number of events with no errors(0), warnings(1), error(2), total(3)
+
+    std::vector<CaloAffectedRegionInfo> m_affectedRegionInfo_global;
+    std::vector<CaloAffectedRegionInfo> m_affectedRegionInfo_current_run;
+
+    void correctCell(TileCell* pCell, int correction, int pmt, int gain, float ener, float time,
+        unsigned char iqual, unsigned char qbit, int ch_type); //!< Compute calibrated energy, time, etc. for TileCell and adjust it.
+
+    unsigned char iquality(float qual)  {//!< method to compute the cell quality
+         return std::min(255, abs((int) qual));
+    } // keep quality within 8 bits make it "unsigned char"
+
+    unsigned char qbits(int ros, int drawer, bool count_over, bool good_time, bool good_ener,
+        bool overflow, bool underflow, bool good_overflowfit); //!< method to compute the cell quality bits
+
+    template<typename T, typename V>
+    class DoubleVectorIterator {
+        T* first;
+        TileFragHash::TYPE typ1;
+        TileRawChannelUnit::UNIT uni1;
+        float cut1;
+        bool amp1;
+        bool tim1;
+        T* second;
+        TileFragHash::TYPE typ2;
+        TileRawChannelUnit::UNIT uni2;
+        float cut2;
+        bool amp2;
+        bool tim2;
+        TileCellBuilder* ptr;
+        int pos;
+        typedef typename T::iterator itr_type;
+        itr_type itr;
+
+      public:
+
+        DoubleVectorIterator(T* f, TileFragHash::TYPE y1, TileRawChannelUnit::UNIT u1, float c1, bool a1, bool t1
+                           , T* s, TileFragHash::TYPE y2, TileRawChannelUnit::UNIT u2, float c2, bool a2, bool t2
+                           , TileCellBuilder* b, int p)
+            : first(f), typ1(y1), uni1(u1), cut1(c1), amp1(a1), tim1(t1)
+            , second(s), typ2(y2), uni2(u2), cut2(c2), amp2(a2), tim2(t2)
+            , ptr(b), pos(p) {
+
+          if (first->begin() != first->end() && pos < 1) {
+            pos = 0;
+            itr = first->begin();
+          } else if (second->begin() != second->end() && pos < 2) {
+            pos = 1;
+            itr = second->begin();
+            // set parameters for second vector
+            ptr->m_RChType = typ2;
+            ptr->m_RChUnit = uni2;
+            ptr->m_maxTimeCorr = cut2;
+            ptr->m_correctAmplitude = amp2;
+            ptr->m_correctTime = tim2;
+          } else {
+            pos = 2;
+            itr = second->end();
+          }
+        }
+
+        DoubleVectorIterator(const DoubleVectorIterator& i)
+            : first(i.first), typ1(i.typ1), uni1(i.uni1), cut1(i.cut1), amp1(i.amp1), tim1(i.tim1)
+            , second(i.second), typ2(i.typ2), uni2(i.uni2), cut2(i.cut2), amp2(i.amp2), tim2(i.tim2)
+            , ptr( i.ptr), pos(i.pos), itr(i.itr) {
+        }
+
+        DoubleVectorIterator& operator=(const DoubleVectorIterator& i) {
+          first = i.first; typ1 = i.typ1; uni1 = i.uni1; cut1 = i.cut1; amp1 = i.amp1; tim1 = i.tim1;
+          second = i.second; typ2 = i.typ2; uni2 = i.uni2; cut2 = i.cut2; amp2 = i.amp2; tim2 = i.tim2;
+          ptr = i.ptr; pos = i.pos; itr = i.itr;
+        }
+
+        bool operator!=(const DoubleVectorIterator& i) {
+          if (pos != i.pos || itr != i.itr) return true;
+          else return false;
+        }
+
+        V& operator*() const { return (*itr); }
+        V* operator->() const { return (*itr); }
+
+        DoubleVectorIterator& operator++() {
+          switch (pos) {
+            case 0:
+              if (itr != first->end()) ++itr;
+              if (itr != first->end()) break;
+              itr = second->begin();
+              pos = 1;
+              // set parameters for second vector
+              ptr->m_RChType = typ2;
+              ptr->m_RChUnit = uni2;
+              ptr->m_maxTimeCorr = cut2;
+              ptr->m_correctAmplitude = amp2;
+              ptr->m_correctTime = tim2;
+              if (itr != second->end()) break;
+              pos = 2;
+              // recover parameters for first vector
+              ptr->m_RChType = typ1;
+              ptr->m_RChUnit = uni1;
+              ptr->m_maxTimeCorr = cut1;
+              ptr->m_correctAmplitude = amp1;
+              ptr->m_correctTime = tim1;
+              break;
+            case 1:
+              if (itr != second->end()) ++itr;
+              if (itr != second->end()) break;
+              pos = 2;
+              // recover parameters for first vector
+              ptr->m_RChType = typ1;
+              ptr->m_RChUnit = uni1;
+              ptr->m_maxTimeCorr = cut1;
+              ptr->m_correctAmplitude = amp1;
+              ptr->m_correctTime = tim1;
+              break;
+            default:
+              break;
+          }
+          return *this;
+        }
+    };
+
+    enum CELL_CHANNEL {E1_CHANNEL = 12};
+    enum CELL_TOWER {E1_TOWER = 10};
+};
+
+#include "TileRecUtils/TileCellBuilder.icc"
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellBuilder.icc b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellBuilder.icc
new file mode 100755
index 0000000000000000000000000000000000000000..ce8593ff933d5f0ab6d355b330618e32173c79e7
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellBuilder.icc
@@ -0,0 +1,390 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TileEvent/TileCell.h"
+#include "DataModel/DataPool.h"
+
+// Atlas includes
+
+// Calo includes
+#include "CaloIdentifier/CaloCell_ID.h"
+#include "CaloDetDescr/CaloDetDescrElement.h"
+#include "CaloDetDescr/MbtsDetDescrManager.h"
+#include "Identifier/IdentifierHash.h"
+
+// Tile includes
+#include "CaloIdentifier/TileID.h"
+#include "CaloIdentifier/TileTBID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileDetDescr/TileDetDescrManager.h"
+#include "TileCalibBlobObjs/TileCalibUtils.h"
+#include "TileConditions/TileCondToolEmscale.h"
+#include "TileConditions/TileCondToolTiming.h"
+#include "TileEvent/TileRawChannel.h"
+#include "TileRecUtils/TileRawChannelBuilder.h"
+
+
+template<class ITERATOR, class COLLECTION>
+void TileCellBuilder::build(const ITERATOR & begin, const ITERATOR & end, COLLECTION * coll) {
+
+  // disable checks for TileID and remember previous state
+  bool do_checks = m_tileID->do_checks();
+  m_tileID->set_do_checks(false);
+  bool do_checks_tb = m_tileID->do_checks();
+  m_tileTBID->set_do_checks(false);
+
+  // Now retrieve the TileDQStatus
+  theDQstatus = m_beamInfo->getDQstatus();
+
+  /* zero all counters and sums */
+  int nTwo = 0;
+  int nCell = 0;
+  int nFake = 0;
+  int nMBTS = 0;
+  int nChan = 0;
+  float eCh = 0.0;
+  float eCellTot = 0.0;
+  float eMBTSTot = 0.0;
+  bool EBdrawerPresent[128];
+  memset(EBdrawerPresent, 0, sizeof(EBdrawerPresent));
+
+  static DataPool<TileCell> tileCellsP(5217);
+  //**
+  //* Iterate over raw channels, creating new TileCells (or incrementing
+  //* existing ones). Add each new TileCell to the output collection
+  //**
+
+  for (ITERATOR rawItr = begin; rawItr != end; ++rawItr) {
+
+    const TileRawChannel* pChannel = (*rawItr);
+    HWIdentifier adc_id = pChannel->adc_HWID();
+    int ros = m_tileHWID->ros(adc_id);
+    int drawer = m_tileHWID->drawer(adc_id);
+    int channel = m_tileHWID->channel(adc_id);
+    int gain = m_tileHWID->adc(adc_id);
+    if (gain == m_skipGain) {
+      ATH_MSG_VERBOSE (" skipping adc_id=" << m_tileHWID->to_string(adc_id));
+      continue; // select only one of two gains in calib runs
+    }
+    int drawerIdx = TileCalibUtils::getDrawerIdx(ros, drawer);
+    if (channel == 0 && ros > 2) EBdrawerPresent[(ros - 3) * 64 + drawer] = true; // EB drawer appeared in the data
+
+    float time = pChannel->uncorrTime(); // take uncorrected time (if available)
+    float amp = pChannel->amplitude();
+
+    TileRawChannelUnit::UNIT oldUnit = m_RChUnit;
+    if (m_correctAmplitude && time > m_timeMinThresh && time < m_timeMaxThresh) { // parabolic correction
+      if (m_RChUnit > TileRawChannelUnit::OnlineADCcounts) { // convert from online units to ADC counts
+        oldUnit = TileRawChannelUnit::ADCcounts;
+        amp = m_tileToolEmscale->undoOnlCalib(drawerIdx, channel, gain, amp, m_RChUnit);
+        if (amp > m_ampMinThresh) // amp cut in ADC counts
+          amp *= TileRawChannelBuilder::correctAmp(time);
+      } else if (amp > m_ampMinThresh
+                && (m_RChUnit == TileRawChannelUnit::ADCcounts
+                    || m_RChUnit == TileRawChannelUnit::OnlineADCcounts)) {
+
+        amp *= TileRawChannelBuilder::correctAmp(time);
+      } else {
+        ATH_MSG_ERROR( "Units in raw channel container is " << m_RChUnit );
+        ATH_MSG_ERROR( "But amplitude correction works only with ADC counts " );
+        ATH_MSG_ERROR( "Please, disable CIS calibration in optimal filter " );
+      }
+    }
+
+    float qual = pChannel->quality();
+
+    // check that time was really reconstructed
+    bool good_time = (fabs(time) < m_maxTimeCorr);
+    bool non_zero_time = (m_RChType == TileFragHash::OptFilterDspCompressed)
+                          ? ((qual > 2.99 && qual < 4.01))
+                          : ((qual > 0.0 || m_RChType == TileFragHash::OptFilterDsp));
+
+    // new feature in rel 17.2.7 - pedestal keeps information about overflow and underflow
+    // if there is an underflow, 10000 is added to pedestal value
+    // if there is an overflow,  20000 is added to pedestal value
+    // if there is an underflow in all samples, 80000 is added to pedestal value
+    // if there is an overflow in all samples,  90000 is added to pedestal value
+    // if there is bad pattern nunber N 100000+N*10000 is added to pedestal value
+    bool overflow = false;
+    bool underflow = false;
+    bool overfit = false;
+    float ped = pChannel->pedestal();
+    if (ped > 55000.) { // one of bad patterns
+      qual = 9999; // mask all bad patterns
+    } else if (ped > 39500.) { // 40000 for constant value or 50000 for all zeros in disconnexted channel
+      // nothing to do
+    } else if (ped > 5000.) { // 10000 for underflow or 20000 for overflow or 10000+20000
+      underflow = ((ped < 15000.) || (ped > 29500.));
+      overflow = (ped > 15000.);
+      // special flag indicating that fit method was applied for overflow channels
+      overfit = ( (ped >= 22500 && ped < 29500) || (ped >= 32500 && ped < 39500) );
+
+      if (overflow
+          && gain == TileID::LOWGAIN
+          && amp > 0
+          && time > m_timeMinThresh
+          && time < m_timeMaxThresh) {
+
+        qual = fabs(qual);
+        if (qual > m_qualityCut && qual < 9999.) {
+          qual = m_qualityCut; // to avoid masking of overflow in low gain
+        }
+      }
+    }
+
+    // apply time correction if needed
+    if (m_correctTime && good_time && non_zero_time)
+      time -= m_tileToolTiming->getSignalPhase(drawerIdx, channel, gain);
+    else
+      time = pChannel->time();
+
+    ++nChan;
+    eCh += amp;
+
+    int index, pmt;
+    Identifier cell_id = (TileCablingService::getInstance())->h2s_cell_id_index (ros, drawer, channel, index, pmt);
+
+    if (index == -2) { // MBTS cells
+
+      if (m_MBTSCells) { // do something with them only if contaier existst
+        ++nMBTS;
+
+        // convert ADC counts to pCb and not to MeV
+        float ener = m_tileToolEmscale->channelCalib(drawerIdx, channel, gain, amp , oldUnit
+                                                     , TileRawChannelUnit::PicoCoulombs);
+
+        eMBTSTot += ener;
+        unsigned char iqual = iquality(qual);
+        // for MBTS qbit use AND of good_time and non_zero_time and check that energy is above MBTS energy threshold in pC
+        unsigned char qbit = qbits(ros, drawer, false, (good_time && non_zero_time)
+           , (fabs(ener) > m_eneForTimeCutMBTS), overflow, underflow, overfit);
+        CaloGain::CaloGain cgain = (gain == TileID::HIGHGAIN)
+                                   ? CaloGain::TILEONEHIGH
+                                   : CaloGain::TILEONELOW;
+
+        TileCell* pCell = tileCellsP.nextElementPtr();
+        // MBTS CaloDDE
+        // Cell ID is set explicitly
+        pCell->set((m_mbtsMgr) ? m_mbtsMgr->get_element(cell_id) : NULL, cell_id);
+        pCell->setEnergy_nonvirt(ener, 0, cgain, 3);
+        pCell->setTime_nonvirt(time);
+        pCell->setQual1(iqual);
+        pCell->setQual2(0);
+        pCell->setQbit1(qbit);
+        pCell->setQbit2(0);
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " MBTS cell_id=" << m_tileTBID->to_string(cell_id)
+                            << " adc_id=" << m_tileHWID->to_string(adc_id)
+                            << " ene= " << ener
+                            << " amp= " << amp
+                            << " time= " << time
+                            << " qual= " << pChannel->quality()
+                            << " iqual= " << (int) iqual
+                            << " qbit = 0x" << MSG::hex << (int) qbit << MSG::dec;
+
+          if (ped > 5000)
+            msg(MSG::VERBOSE) << " err = " << TileRawChannelBuilder::BadPatternName(ped) << endmsg;
+          else
+            msg(MSG::VERBOSE) << endmsg;
+        }
+
+        if (m_maskBadChannels && maskBadChannel(pCell, adc_id))
+          ATH_MSG_VERBOSE ( "cell with id=" << m_tileTBID->to_string(cell_id)
+                             << " bad channel masked, new energy=" << pCell->energy() );
+
+        m_MBTSCells->push_back(pCell); // store cell in container
+
+      }
+    } else if (index != -1) { // connected channel
+
+      float ener = m_tileToolEmscale->channelCalib(drawerIdx, channel, gain, amp
+           , oldUnit, TileRawChannelUnit::MegaElectronVolts);
+
+      eCellTot += ener;
+      
+      unsigned char iqual = iquality(qual);
+      // for normal cell qbit use only non_zero_time flag and check that energy is above standatd energy threshold in MeV
+      unsigned char qbit = qbits(ros, drawer, true, non_zero_time, (fabs(ener) > m_eneForTimeCut)
+          , overflow, underflow, overfit);
+
+
+      if ( (TileCablingService::getInstance())->getCablingType() == TileCablingService::RUN2Cabling
+      	   && channel == E1_CHANNEL
+	   && ros > 2) { // Raw channel -> E1 cell.
+
+       int drawer2 = (TileCablingService::getInstance())->E1_merged_with_run2(drawer);
+       if (drawer2 != 0) { // Raw channel splitted into two E1 cells for Run 2.
+         int side = (ros == 3) ? 1 : -1;
+         Identifier cell_id2 = m_tileID->cell_id(TileID::GAPDET, side, drawer2, E1_TOWER, TileID::SAMP_E);
+         int index2 = m_tileID->cell_hash(cell_id2);
+         TileCell* pCell2 = tileCellsP.nextElementPtr();
+         ++nCell;
+         m_allCells[index2] = pCell2;
+         const CaloDetDescrElement* dde2 = m_tileMgr->get_cell_element(index2);
+         pCell2->set(dde2, cell_id2);
+         pCell2->setEnergy_nonvirt(0, 0, CaloGain::INVALIDGAIN, CaloGain::INVALIDGAIN);
+         int pmt2(0);
+         ener /= 2.0F;
+         correctCell(pCell2, 1, pmt2, gain, ener, time, iqual, qbit, 1);
+   
+         ATH_MSG_DEBUG("E1 cell Id => " << m_tileID->to_string(cell_id)
+                      << " splitted into " << m_tileID->to_string(cell_id2));
+
+
+       }
+
+     }
+
+
+
+
+      TileCell* pCell = m_allCells[index];
+      if (pCell) {
+        ++nTwo;
+        correctCell(pCell, 2, pmt, gain, ener, time, iqual, qbit, 0); // correct & merge 2 PMTs in one cell
+      } else {
+        ++nCell;
+        m_allCells[index] = pCell = tileCellsP.nextElementPtr();
+        const CaloDetDescrElement* dde = m_tileMgr->get_cell_element(index);
+        pCell->set(dde, cell_id);
+        pCell->setEnergy_nonvirt(0, 0, CaloGain::INVALIDGAIN, CaloGain::INVALIDGAIN);
+        int ch_type = (dde->onl2() == TileHWID::NOT_VALID_HASH) ? 1 : 0;
+        correctCell(pCell, 1, pmt, gain, ener, time, iqual, qbit, ch_type); // correct & save e,t,q in new cell
+      }
+
+      if (msgLvl(MSG::VERBOSE)) {
+        float calib1 = (amp != 0) ? ener / amp : 0.0;
+        msg(MSG::VERBOSE) << " cell_id=" << m_tileID->to_string(cell_id, -2)
+                          << " adc_id=" << m_tileHWID->to_string(adc_id)
+                          << " calib=" << calib1
+                          << " nCell=" << nCell
+                          << " energy=" << ener << " (" << pCell->energy() << ", " << pCell->eneDiff() << ")" << endmsg;
+
+        msg(MSG::VERBOSE) << " amp= " << amp
+                          << " time= " << time
+                          << " qual= " << pChannel->quality()
+                          << " iqual= " << (int) iqual
+                          << " qbit = 0x" << MSG::hex << (int) qbit << MSG::dec;
+
+        if (ped > 5000)
+          msg(MSG::VERBOSE) << " err = " << TileRawChannelBuilder::BadPatternName(ped) << endmsg;
+        else
+          msg(MSG::VERBOSE) << endmsg;
+      }
+      
+    } else {
+
+      if (msgLvl(MSG::VERBOSE)) {
+
+        unsigned char iqual = iquality(qual);
+        unsigned char qbit = qbits(0, drawer, false, non_zero_time, false, overflow, underflow, overfit); //fake ros number here
+
+        msg(MSG::VERBOSE) << " channel with adc_id=" << m_tileHWID->to_string(adc_id)
+                          << " is not connected" << endmsg;
+
+        msg(MSG::VERBOSE) << " amp= " << amp
+                          << " time= " << time
+                          << " qual= " << pChannel->quality()
+                          << " iqual= " << (int) iqual
+                          << " qbit = 0x" << MSG::hex << (int) qbit << MSG::dec;
+
+        if (ped > 5000)
+          msg(MSG::VERBOSE) << " err = " << TileRawChannelBuilder::BadPatternName(ped) << endmsg;
+        else
+          msg(MSG::VERBOSE) << endmsg;
+      }
+    }
+    if (msgLvl(MSG::VERBOSE)) {
+      if ((m_correctTime && good_time && non_zero_time) || pChannel->sizeTime() > 1) {
+        msg(MSG::VERBOSE) << " OF_time = " << pChannel->uncorrTime()
+                          << " corr_time = " << time << endmsg;
+      }
+    }
+  }
+
+  //**
+  // Now store all TileCells  
+  //**
+  for (unsigned int index = 0; index < m_allCells.size(); ++index) {
+
+    TileCell * pCell = m_allCells[index];
+
+    if (pCell) {      // cell exists
+
+      if (m_maskBadChannels)
+        if (maskBadChannels(pCell))
+          ATH_MSG_VERBOSE ( "cell with id=" << m_tileID->to_string(pCell->ID(), -2)
+                           << " bad channels masked, new energy=" << pCell->energy() );
+
+      if (m_thresholdNotSet
+          || (pCell->energy() > m_eThreshold
+              && fabs(pCell->timeDiff()) < m_maxTimeDiff
+              && pCell->time1() < m_maxTime && pCell->time1() > m_minTime
+              && pCell->time2() < m_maxTime && pCell->time2() > m_minTime
+              && pCell->qual1() > m_minChi2 && pCell->qual1() < m_maxChi2
+              && pCell->qual2() > m_minChi2 && pCell->qual2() < m_maxChi2)) {
+
+        coll->push_back(pCell); // store cell in container
+
+      } else {
+
+        //delete pCell; it's dangerous to delete cell, if it's in DataPool 
+
+      }
+
+      m_allCells[index] = 0;             // clear pointer for next event
+    } else if (m_fakeCrackCells) { // can be true only for full-size container
+
+      pCell = tileCellsP.nextElementPtr();
+      const CaloDetDescrElement* dde = m_tileMgr->get_cell_element(index);
+      pCell->set(dde, dde->identify());
+
+      if (m_tileID->section(pCell->ID()) == TileID::GAPDET) { // missing D4/E3/E4 cell
+
+        int ind = m_tileID->module(pCell->ID()) + ((m_tileID->side(pCell->ID()) > 0) ? 0 : 64);
+        if (EBdrawerPresent[ind]) {
+          ++nFake;
+          if (m_tileID->sample(pCell->ID()) == TileID::SAMP_E) {
+            pCell->setEnergy(0.0, 0.0, TileID::LOWGAIN, CaloGain::INVALIDGAIN); // reset energy completely, indicate problem putting low gain
+            pCell->setQuality(0, TileCell::MASK_BADCH, 0); // reset quality flag for first pmt
+            pCell->setQuality(0, TileCell::MASK_BADCH, 1); // reset quality flag for second pmt
+          } else {
+            pCell->setEnergy(0.0, 0.0, TileID::LOWGAIN, TileID::LOWGAIN); // reset energy completely, indicate problem putting low gain
+            pCell->setQuality(0, 0, 0); // reset quality flag for first pmt
+            pCell->setQuality(0, 0, 1); // reset quality flag for second pmt
+          }
+          pCell->setTime(0.0); // reset time completely
+
+          ATH_MSG_VERBOSE ( "adding fake cell with id=" << m_tileID->to_string(pCell->ID(), -2)
+                           << " ene=" << pCell->energy()
+                           << " status=" << (pCell->badcell() ? "bad" : "good") );
+
+          coll->push_back(pCell); // store cell in container
+        }
+      }
+    }
+  }
+
+  if (msgLvl(MSG::DEBUG)) {
+    msg(MSG::DEBUG) << " nChan=" << nChan
+                    << " RawChSum=" << eCh
+                    << " nCell=" << nCell
+                    << " n2=" << nTwo
+                    << " nFake=" << nFake
+                    << " eneTot=" << eCellTot;
+
+    if (m_MBTSCells)
+      msg(MSG::DEBUG) << " nMBTS=" << nMBTS
+                      << " eMBTS=" << eMBTSTot << endmsg;
+    else
+      msg(MSG::DEBUG) << endmsg;
+  }
+  
+  m_tileID->set_do_checks(do_checks);
+  // set back this flag to TileID
+  m_tileTBID->set_do_checks(do_checks_tb);
+
+}
+
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellFakeProb.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellFakeProb.h
new file mode 100755
index 0000000000000000000000000000000000000000..60e8bef5d97cbc5b3620418230fdf098bf6d4833
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellFakeProb.h
@@ -0,0 +1,69 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILECELLFAKEPROB_H
+#define TILERECUTILS_TILECELLFAKEPROB_H
+
+/*! @class TileCellFakeProb
+ * 
+ *  @brief Scales down the energy of cells due to simulated 
+ *   failure of drawers
+ *
+ *  \author Kai Voss <kai.voss@cern.ch>
+ *
+ *  \date   August 25, 2005
+ */
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "CaloInterface/ICellWeightTool.h"
+#include "Identifier/Identifier.h"
+
+class TileID;
+class TileHWID;
+class CaloCell_ID;
+class CaloCell;
+class TileCablingService;
+
+#include <string>
+#include <vector>
+
+class TileCellFakeProb: public AthAlgTool, virtual public ICellWeightTool {
+
+  public:
+
+    /*! Standard constructor */
+    TileCellFakeProb(const std::string& type, const std::string& name,
+        const IInterface* parent);
+
+    /*! Standard destructor */
+    ~TileCellFakeProb();
+
+    /*! AlgTool Initialization */
+    virtual StatusCode initialize();
+
+    /*! implementation of ICellWeightTool weighting method */
+    virtual double wtCell(const CaloCell* theCell);
+
+  private:
+    /*! Reads in properties and creates list of miscalibrated cells */
+    StatusCode createMiscalibratedCellList();
+
+    /*! Pointer to CaloCellID */
+    const CaloCell_ID * m_caloID;
+    /*! Pointer to TileID */
+    const TileID * m_tileID;
+    /*! Pointer to TileHWID */
+    const TileHWID * m_tileHWID;
+    /*! Pointer to TileCablingService */
+    const TileCablingService* m_cabling;
+
+    /*! Property: List of "dead" drawers */
+    std::vector<std::string> m_deadDrawerInput;
+
+    /*! List of "dead" cells and their weights */
+    std::map<Identifier, double> m_celllist;
+
+};
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellMaskingTool.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellMaskingTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2c85b0603603b3d5e93c22766ba42267254f2e4
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellMaskingTool.h
@@ -0,0 +1,56 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILECELLMASKINGTOOL_H
+#define TILECELLMASKINGTOOL_H
+
+// Gaudi includes
+#include "GaudiKernel/ServiceHandle.h"
+
+// Atlas includes
+#include "AthenaBaseComps/AthAlgTool.h"
+
+// Calo includes
+#include "CaloInterface/ICaloCellMakerTool.h"
+#include "Identifier/HWIdentifier.h"
+
+#include <string>
+#include <vector>
+#include <bitset>
+
+class TileID;
+class TileHWID;
+
+class TileCellMaskingTool: public AthAlgTool, virtual public ICaloCellMakerTool {
+  public:
+    TileCellMaskingTool(const std::string& type
+                        , const std::string& name
+                        , const IInterface* parent);
+
+    virtual StatusCode initialize();
+    virtual StatusCode process(CaloCellContainer * theCellContainer);
+    virtual StatusCode finalize();
+
+    virtual bool channel_is_good(HWIdentifier & hwid);
+
+  private:
+
+    StatusCode fillIncludedCellsMap();
+    void killer(std::string component, int ros, int drw, int index);
+
+    std::vector<std::string> m_rejectedTileDrawer;
+    std::vector<std::string> m_rejectedTileMB;
+    std::vector<std::string> m_rejectedTileDigitizer;
+    std::vector<std::string> m_rejectedTileDMU;
+    std::vector<std::string> m_rejectedTileChannels;
+    float m_zeroEnergy;
+
+    const TileID* m_tileID;
+    const TileHWID* m_tileHWID;
+
+    std::bitset<65536> m_includedCellsMap;
+
+};
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellNoiseFilter.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellNoiseFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..d355a523dd7513a4902e2386e039010ffc7eb153
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCellNoiseFilter.h
@@ -0,0 +1,99 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TILECELLNOISEFILTER_H
+#define TILECELLNOISEFILTER_H
+
+// Atlas includes
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "GaudiKernel/ToolHandle.h"
+#include "GaudiKernel/ServiceHandle.h"
+
+// Calo includes
+#include "CaloIdentifier/CaloCell_ID.h"
+#include "CaloInterface/ICaloCellMakerTool.h"
+#include "CaloInterface/ICalorimeterNoiseTool.h"
+
+// Tile includes
+#include "TileConditions/TileCondToolEmscale.h"
+#include "TileConditions/TileCondToolNoiseSample.h"
+
+// forward declarations
+class TileID;
+class TileHWID;
+class TileCell;
+class CaloCellContainer;
+
+/**
+ @class TileCellNoiseFilter
+ @brief This tool subtracts common-mode noise from all TileCells
+ */
+class TileCellNoiseFilter: public AthAlgTool, virtual public ICaloCellMakerTool {
+  public:
+
+    /** AlgTool like constructor */
+    TileCellNoiseFilter(const std::string& type, const std::string& name,
+        const IInterface* parent);
+
+    /** Virtual destructor */
+    virtual ~TileCellNoiseFilter() {
+    }
+    ;
+
+    /** AlgTool InterfaceID */
+    static const InterfaceID& interfaceID();
+
+    /** AlgTool initialize method.*/
+    StatusCode initialize();
+    /** AlgTool finalize method */
+    StatusCode finalize();
+
+    /** proceed the coherent noise subtruction algorithm and correct Tile cell energies */
+    virtual StatusCode process(CaloCellContainer *cellcoll);
+
+    /** Callback to handle Data-driven GeoModel initialisation */
+    virtual StatusCode geoInit(IOVSVC_CALLBACK_ARGS);
+
+  private:
+
+    // set common-mode subtructed energy
+    void setCMSEnergy(TileCell *cell);
+
+    // calculate common-mode for all the motherboards
+    int calcCM(const CaloCellContainer *cellcoll);
+
+    // derive a value of common-mode shift
+    float getCMShift(int partition, int drawer, int channel) {
+      return m_commonmode[partition][drawer][channel / maxChannel];
+    }
+
+    const TileID* m_tileID;   //!< Pointer to TileID
+    const TileHWID* m_tileHWID; //!< Pointer to TileHWID
+
+    ToolHandle<TileCondToolEmscale> m_tileToolEmscale; //!< main Tile Calibration tool
+    ToolHandle<TileCondToolNoiseSample> m_tileToolNoiseSample; //!< tool which provided noise values
+    ToolHandle<ICalorimeterNoiseTool> m_noiseTool;       //!< Calo Noise tool
+
+    // properties
+    float m_truncationThresholdOnAbsEinSigma;
+    float m_minimumNumberOfTruncatedChannels;
+    bool m_useTwoGaussNoise;
+    bool m_useTileNoiseDB;
+
+    static const CaloCell_ID::SUBCALO m_caloIndex = CaloCell_ID::TILE;
+
+    static const int maxPartition = 4; // LBA,LBC,EBA,EBC
+    static const int maxDrawer = 64;   // # of drawers per partition
+    static const int maxMOB = 4;       // # of motherboards per drawer
+    static const int maxChannel = 12;  // # of channels per motherboard
+
+    float m_commonmode[maxPartition][maxDrawer][maxMOB];
+    int m_nemptychan[maxPartition][maxDrawer][maxMOB];
+    int m_ngoodchan[maxPartition][maxDrawer][maxMOB];
+};
+
+#endif // TILECELLNOISEFILTER_H
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileCorrelation.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCorrelation.h
new file mode 100755
index 0000000000000000000000000000000000000000..e5b02d2f96399d8d723a070c10ef695a56ac100a
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileCorrelation.h
@@ -0,0 +1,71 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//*************************************************************************************
+// Filename : TileCorrelation.h
+// Author   : C Cuenca
+// Created  : May 2004
+
+// DESCRIPTION
+// Implementation of a class for correlation among digits calculation.
+
+// ************************************************************************************
+
+#include "GaudiKernel/MsgStream.h"
+
+class TileHWID;
+
+#include <vector>
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+
+using namespace std;
+
+class TileCorrelation
+{
+ public:
+  TileCorrelation();
+  ~TileCorrelation(); 
+  void SetCorrelationZero(MsgStream & log, int dignum);
+  void SetCorrelationDelta(MsgStream & log, int dignum);
+  void Sum(vector<double> &digits, int ros, int drawer, int channel, int gain, MsgStream & log, bool m_debug, int &dignum);
+  void RunningCorrelation(vector<double> &digits, int ros, int drawer, int channel, int gain,
+			  MsgStream & log, bool m_debug, int &dignum, int chthres);
+  void CalcCorrelation(MsgStream & log, int dignum);
+  void CalcRunningCorrelation(MsgStream & log, int dignum, int chthres, bool m_7to9);
+  void PrintCorrelation(int dignum);
+  void SaveCorrelationSumm(bool m_deltaCorrelation, string m_OptFilterFile_CorrelationSumm, const TileHWID *m_tileHWID,
+		       MsgStream & log, int dignum);
+  void SaveCorrelationMatrix(bool m_deltaCorrelation, string m_OptFilterFile_CorrelationMatrix, const TileHWID *m_tileHWID,
+		       MsgStream & log, int dignum);
+  void CalcWeights(bool m_deltaCorrelation,
+		   vector<double> m_LshapeForm, vector<double> m_HshapeForm,
+		   vector<double> m_LdshapeForm, vector<double> m_HdshapeForm,
+		   string m_OptFilterFile_ai_lo, string m_OptFilterFile_bi_lo,
+		   string m_OptFilterFile_ai_hi, string m_OptFilterFile_bi_hi,
+		   const TileHWID *m_tileHWID, MsgStream & log, int dignum);
+  void BuildPulseShape(vector<double> &m_pulseShape, vector<double> &m_pulseShapeX, vector<double> &m_pulseShapeT,
+		       int dignum, MsgStream &log);
+  
+ private:
+  //double SS(9,9);
+  double SS[4][64][48][2][9][9];
+  double S[4][64][48][2][9];
+  double R[4][64][48][2][9][9];
+  //int N_events[4][64][48][2];  
+
+  double corr[9], corr_sum[4][64][48][2][9], corr_sum_sq[4][64][48][2][9], ncorr;
+  int N[4][64][48][2], jentry, lag, N_pairs[4][64][48][2][9];
+  double N_d;
+  double S1[4][64][48][2][9], S2[4][64][48][2][9], S11[4][64][48][2][9], S12[4][64][48][2][9], S22[4][64][48][2][9];
+  
+
+
+  //  const TileInfo *m_tileInfo;
+};
+
+//#ifndef TILERECALGS_TILELOF_H
+//#define TILERECALGS_TILELOF_H
+//#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileFilterManager.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileFilterManager.h
new file mode 100755
index 0000000000000000000000000000000000000000..e2e929c11ad6e54da463d4fde6744ebf05a0afad
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileFilterManager.h
@@ -0,0 +1,111 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECALGS_TILEFILTERMANAGER_H
+#define TILERECALGS_TILEFILTERMANAGER_H
+// *************************************************************************************
+// Filename : TileFilterManager.h
+// Author   : F. Merritt, A. Aurisano
+// Created  : March 2004
+
+// DESCRIPTION
+// Managers filtering of TileDigit objects to produce amplitudes of TileRawChannels.
+
+// ************************************************************************************
+
+#include "TileRecUtils/TileFitter.h"
+#include "TileRecUtils/TileFilterResult.h"
+
+#include <CLHEP/Matrix/Matrix.h>
+#include <CLHEP/Matrix/Vector.h>
+#include <string>
+#include <vector>
+
+using namespace CLHEP;
+/** Auxiliary class for TileRawChannelMakerManyAmps. 
+ */
+class TileFilterManager 
+{
+ public:
+  // =================================================================================
+  //Constructor
+  TileFilterManager(int mode, int level, int &nPar, int &nSam, int &inTsam, int &jbsam, int &jesam, 
+		    int &ncr, int &intcr, int &jBcr, int &jEcr, std::vector<double> &Shape, bool lDebug=false);
+  // =================================================================================
+  // Destructor
+  ~TileFilterManager();
+  // =================================================================================
+  // Find the crossing with largest amplitude.
+  int FindHighestResidual(HepVector &digits) const;
+  // =================================================================================
+  // Find the crossing with lowest residual amplitude.
+  int FindLowestCrossing(HepVector &digits) const;
+  // =================================================================================
+  // Fit the in-time amplitude for this digits vector.
+  int FitDigits(TileFilterResult &tRes, bool lDebug = false);
+  // =================================================================================
+  // Fit the in-time amplitude for this digits vector.
+  int FitDigits1(TileFilterResult &tRes, bool lDebug = false);
+  // =================================================================================
+  // Fit the in-time amplitude for this digits vector (alternate method).
+  int FitDigits2(TileFilterResult &tRes, bool lDebug = false);
+  // =================================================================================
+  // Make the table of offsets used to locate Fitter matrices.
+  void MakeFitterOffsetTables();
+  // =================================================================================
+  // Fill the SPD matrix for a new fit configuration.
+  int MakeSPD(bool debug, std::vector<int>& vcross, HepMatrix& SPD);
+  // =================================================================================
+  // Make the TileFitter arrays which hold the solution matrices for each fit
+  int MakeFitterArrays();
+  // =================================================================================
+  // Return the vector of crossing for a given fit index.
+  void getVcross(int Npile, int iconfig, std::vector<int>& vcross);
+  // =================================================================================
+  // Return the cuts used in filtering.
+  void getCuts(double& rchisq, double& chi);
+  // =================================================================================
+  std::vector<int>& get_NfitIndex();
+  // =================================================================================
+  // Return the FitIndex for a given crossing configuration.
+  int getFitIndex(int Npile, std::vector<int>& vcross);
+  // =================================================================================
+  // Return the FitErr vector for a given crossing configuration.
+  std::vector<double>& getFitterErr(int Npile, int iconfig);
+  // =================================================================================
+
+
+ private:
+
+  std::string   m_infoName;
+  //  StoreGateSvc* m_storeGate;
+  //  StoreGateSvc* m_detStore;
+
+  int Fmode;
+  int Flevel;
+  bool debug;
+  int NParamMax;
+  int Nshape;
+  int InTshape;
+  int Ndig;
+  int InTdig;
+  int jBsamp;
+  int jEsamp;
+  int Ncross;
+  int InTcross;
+  int jBcross;
+  int jEcross;
+  double rchisqCut;
+  double chiCut;
+
+  std::vector<int *> OffsetVector;
+  std::vector<int> NfitIndex;
+  std::vector<double *> CrossShape;
+  int map_ind2icr[9];
+  int map_icr2ind[9];
+  std::vector<TileFitter *> vNparam;
+  std::vector<std::vector<TileFitter> > vNpFitter;
+};
+
+#endif // TILERECALGS_TILEFILTERMANAGER_H
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileFilterResult.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileFilterResult.h
new file mode 100755
index 0000000000000000000000000000000000000000..6132acfc9482ada400de9d377cbbb314554f0087
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileFilterResult.h
@@ -0,0 +1,66 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECALGS_TILEFILTERRESULT_H
+#define TILERECALGS_TILEFILTERRESULT_H
+//*********************************************************************************
+// Filename : TileFilterResult.h
+// Authors  : F. Merritt, A. Aurisano
+// Created  : March 2004
+//*********************************************************************************
+
+
+#include <CLHEP/Matrix/Matrix.h>
+#include <CLHEP/Matrix/Vector.h>
+#include <vector>
+
+using namespace CLHEP;
+/** Auxiliary class for TileRawChannelMakerManyAmps. 
+ */
+class TileFilterResult
+{
+ public:
+
+  // Constructor
+  TileFilterResult(std::vector<float> &dig, double sigma);
+
+  // Destructor
+  ~TileFilterResult();
+
+  double       getSigDig() const;
+  HepVector&   getDigRef();
+  HepVector*   getDigPt();
+  std::vector<int>& getVcrossRef();
+  int&         getFitIndexRef();
+  int&         getNparRef();
+  HepVector&   getParamRef();
+  HepVector&   getErrRef();
+  HepVector&   getResidRef();
+  double&      getChisqRef();
+  int          addCross(int kcrIndex);
+  int          dropCross(int kcrIndex);
+  void         PrintFitParam();
+  double       getInTime(double &, double &, double &, double &, double &);
+  void         SnapShot(int imode);
+
+ private:
+
+  bool debug;
+
+  HepVector digits;
+  double sigDigit;
+
+  int Nparam;
+  int Npileup;
+  int iFitIndex;
+  std::vector<int> Vcross;
+
+  HepVector fitParam;
+  HepVector fitErr;
+
+  HepVector residuals;
+  double chisq;
+};
+// **********************************************************************************
+#endif // TILERECALGS_TILEFILTERRESULT_H
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileFilterTester.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileFilterTester.h
new file mode 100755
index 0000000000000000000000000000000000000000..5b7a5b02626dae21eb52eee055ef54d824e6be4c
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileFilterTester.h
@@ -0,0 +1,51 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECALGS_TILEFILTERTESTER_H
+#define TILERECALGS_TILEFILTERTESTER_H
+//**************************************************************************************************
+// Filename: TileFilterTester.h
+// Author:   F. Merritt
+// Created:  April 2004
+
+// DESCRIPTION
+// This class is to be called from the TileHitToRawChannel initialization section,
+// after TileFilterManager has been instantiated.  A reference to TileFilterManager 
+// is passed in the call, so it is possible to thoroughly test the program outside 
+// of the event loop.
+// *************************************************************************************************
+
+class TileInfo;
+class TileFilterManager;
+class StoreGateSvc;
+
+#include <vector>
+/** Auxiliary class for TileRawChannelMakerManyAmps. 
+ */
+class TileFilterTester
+{
+ public:
+
+  // Constructor
+  TileFilterTester(TileFilterManager * tFilterManager, int Fmode, int Ftest, bool lDebug = false);
+  //Destructor
+  ~TileFilterTester();
+  // Event Generator
+  void GenEvents(int N);
+
+ private:
+  bool debug;
+  int Fmode;
+  int Ftest;
+  int Cmode;
+  int Ncross;
+  int Npileup;
+  int Namp;
+  std::vector<int> Nconfig;
+  std::vector<int> iAmpVec;
+  std::vector<double> AmpVec;
+
+  TileFilterManager * m_tileFilterManager;
+};
+#endif // TILERECALGS_TILEFILTERTESTER_H
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileFitter.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileFitter.h
new file mode 100755
index 0000000000000000000000000000000000000000..9e4e78c1a5d148ba22a4feead85620e4110c6005
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileFitter.h
@@ -0,0 +1,69 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//***************************************************************************************************
+// Filename :  TileFitter.h
+// Author   :  F. Merritt, A. Aurisano
+// Created  :  March 2004
+//
+// Description
+//   Stores methods and matrices needed for a fitting a TileDigits object to a specific
+//   configuration of energy deposits.
+//
+//
+//***************************************************************************************************
+
+#ifndef TILESIMALGS_TILEFITTER_H
+#define TILESIMALGS_TILEFITTER_H
+
+#include "TileRecUtils/TileFilterResult.h"
+
+#include <CLHEP/Matrix/Matrix.h>
+#include <CLHEP/Matrix/Vector.h>
+#include <CLHEP/Matrix/SymMatrix.h>
+#include <vector>
+
+using namespace CLHEP;
+
+class StoreGateSvc;
+
+class TileFitter
+ {
+ public:
+
+   // Constructor
+   TileFitter();
+   TileFitter(bool debug, int nrow, int ncol, int index, HepMatrix& S, int Icon);
+
+   // Destructor
+   ~TileFitter();
+
+   int FitAmp(TileFilterResult &tResult, bool lDebug=false);
+   int getIndex();
+   std::vector<double>& getErrRef();
+   void PrintMat(HepMatrix &mat);
+   void PrintVec(HepVector &vec);
+
+ private:
+
+   int Iconstraint;     // >0 => include constraint on Ped; >1 => also pileups.
+   HepVector Camp;       // used for maxP fit with constraints only.
+   double Ped_const;
+   double F2_sigPed;
+   double F2_sigPile;
+
+   int ND;               // number of samples.
+   int NP;               // number of parameters in fit
+   int iconfig;          // number of this configuration.
+   std::vector<int> vconfig;  // vector of crossings for this configuration
+   HepMatrix SPD;      // NPxND matrix
+   //   HepMatrix M;        // = SPD*SPDT
+   //   HepMatrix MI;       // = M.inverse
+   HepMatrix MISPD;    // = MI*SPD
+   std::vector<double> ErrDiag;
+ };
+
+#endif // TILERECALGS_TILEFITTER_H
+
+
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilder.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilder.h
new file mode 100755
index 0000000000000000000000000000000000000000..6e6faa1735f644ee4fb294fff9c815052d686a5d
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilder.h
@@ -0,0 +1,191 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#ifndef TILERECUTILS_ITILERAWCHANNELBUILDER_H
+#define TILERECUTILS_ITILERAWCHANNELBUILDER_H
+
+/********************************************************************
+ *
+ * NAME:     TileRawChanelBuilder
+ * PACKAGE:  offline/TileCalorimeter/TileRecUtils
+ *
+ * AUTHOR :  K. Gellerstedt
+ * CREATED:  Nov 17 2003
+ *
+ * PURPOSE:  Interface for builder algorithms
+ *
+ *  Input: TileDigitsContainer
+ *  Output: Container with TileRawChannels
+ *  Parameters:
+ *    TileRawChannelContainer - Name of output container
+ *   
+ ********************************************************************/
+
+//Gaudi includes
+#include "GaudiKernel/ToolHandle.h"
+#include "GaudiKernel/ServiceHandle.h"
+
+// Atlas includes
+#include "AthenaBaseComps/AthAlgTool.h"
+
+#include <utility>
+#include <vector>
+
+class TileDigits;
+class TileRawChannel;
+class TileID;
+class TileHWID;
+class TileInfo;
+class TileBeamInfoProvider;
+class ITileRawChannelTool;
+class StoreGateSvc;
+class MsgStream;
+
+//class vector;
+
+#include "TileIdentifier/TileFragHash.h"
+#include "TileIdentifier/TileRawChannelUnit.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileDigitsCollection.h"
+
+
+typedef std::vector<std::pair<TileRawChannel*, TileDigits*> > Overflows_t;
+
+class TileRawChannelBuilder: public AthAlgTool {
+  public:
+    TileRawChannelBuilder(const std::string& type, const std::string& name,
+        const IInterface* parent);
+
+    virtual ~TileRawChannelBuilder();
+
+    // Algorithm tool virtual methods
+    virtual StatusCode initialize();
+    //virtual StatusCode execute();
+    virtual StatusCode finalize();
+
+    /**
+     * Create container in SG with name given by
+     * parameter (m_TileRawChannelContainerID)
+     */
+    virtual StatusCode createContainer();
+
+    /**
+     * Builder virtual method to be implemented by subclasses
+     * @param digits Pointer to TileDigitsContainer
+     *
+     */
+    virtual TileRawChannel* rawChannel(const TileDigits* digits);
+
+    /**
+     * Commit RawChannelContiner in SG and make const
+     */
+    virtual StatusCode commitContainer();
+
+    void initLog();
+    void endLog();
+
+    // process one digit and store result in internal container
+    void build(const TileDigits* digits) {
+      m_rawChannelCnt->push_back(rawChannel(digits));
+    }
+
+    // find all bad patterns in a drawer and fill internal static arrays
+    void fill_drawer_errors(const TileDigitsCollection* collection);
+
+    // process all digits from one collection and store results in internal container
+    void build(const TileDigitsCollection* collection);
+
+    // process digits from a given vector and store results in internal container
+    template<class ITERATOR>
+    void build(const ITERATOR & begin, const ITERATOR & end) {
+      for (ITERATOR rawItr = begin; rawItr != end; ++rawItr)
+        m_rawChannelCnt->push_back(rawChannel((*rawItr)));
+    }
+
+    // process digits from a given vector and store results in collection
+    template<class ITERATOR, class COLLECTION>
+    void build(const ITERATOR & begin, const ITERATOR & end, COLLECTION * coll) {
+      initLog();
+      for (ITERATOR rawItr = begin; rawItr != end; ++rawItr)
+        coll->push_back(rawChannel((*rawItr)));
+      endLog();
+    }
+
+    /**
+     * AlgTool InterfaceID
+     */
+    static const InterfaceID& interfaceID();
+
+    static double correctAmp(double phase); //!< Amplitude correction factor according to the time when using weights for tau=0 without iterations
+
+    static int CorruptedData(int ros, int drawer, int channel, int gain,
+        const std::vector<float> & digits, float &dmin, float &dmax);
+
+    static const char* BadPatternName(float ped);
+
+    static void resetDrawer();
+    void resetOverflows(void);
+    Overflows_t& getOverflowedChannels(void);
+    std::string getTileRawChannelContainerID(void);
+
+  protected:
+    // properties
+    std::string m_TileRawChannelContainerID;
+
+    // RawChannelContainer
+    TileRawChannelContainer* m_rawChannelCnt;
+
+    // parameters for RawChannelContainer
+    TileFragHash::TYPE m_rChType;
+    TileRawChannelUnit::UNIT m_rChUnit;
+    unsigned int m_bsflags;
+
+    // Should energy be calibrated
+    bool m_calibrateEnergy;
+
+    // Should time be calibrated (delta added from laser run)
+    bool m_correctTime;
+
+    // Use DSP noise correction for incomplete containers
+    bool m_useDSP;
+
+    // thresholds for parabolic amplitude correction (used in optimal filter without iterations)
+    float m_ampMinThresh;   //!< correct amplitude if it's above amplitude threshold (in ADC counts)
+    float m_timeMinThresh;   //!< correct amplitude is time is above time min threshold
+    float m_timeMaxThresh;   //!< correct amplitude is time is below time max threshold
+
+    // runType used to override value of trigType in event
+    int m_runType;
+
+    // Tile objects
+    const TileID* m_tileID;
+    const TileHWID* m_tileHWID;
+    const TileInfo* m_tileInfo;
+
+    ToolHandle<TileBeamInfoProvider> m_beamInfo;
+    ToolHandleArray<ITileRawChannelTool> m_noiseFilterTools;
+
+    int m_trigType;
+    bool m_idophys;   // Phys fitting
+    bool m_idolas; // Laser fitting
+    bool m_idoped; // Ped run, phys fitting
+    bool m_idocis; // CIS fitting
+    int m_cischan; // which channel is fired by CIS
+    double m_capdaq; // Capacitor
+
+    unsigned int m_evtCounter;
+    unsigned int m_chCounter;
+
+    int m_nChL;
+    int m_nChH;
+    double m_RChSumL;
+    double m_RChSumH;
+
+    Overflows_t m_overflows;
+    int m_dataPoollSize;
+
+};
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderFitFilter.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderFitFilter.h
new file mode 100755
index 0000000000000000000000000000000000000000..35264bf52332c93b5c8a4f78ac046688b00fd8dd
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderFitFilter.h
@@ -0,0 +1,106 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELBUILDERFITFILTER_H
+#define TILERECUTILS_TILERAWCHANNELBUILDERFITFILTER_H
+
+/********************************************************************
+ *
+ * NAME:     TileRawChannelBuilderFitFilter.h 
+ * PACKAGE:  offline/TileCalorimeter/TileRecUtils
+ *
+ * AUTHOR :  K. Gellerstedt, Algorithm by Richard John Teuscher
+ * CREATED:  Dec 03 2003
+ *
+ * PURPOSE:  Build TileRawChannels from digits using fitting
+ *
+ *  Input: TileDigitsContainer
+ *  Output: TileRawChannelContainer
+ *  Parameters: TileRawChannelCont - Name of output container in SG
+ *    FrameLength
+ *    MaxIterate
+ *    RMSChannelNoise
+ ********************************************************************/
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilder.h"
+#include "TileConditions/TilePulseShapes.h"
+
+class TileBeamInfoProvider;
+
+class TileRawChannelBuilderFitFilter: public TileRawChannelBuilder {
+  public:
+
+    // constructor
+    TileRawChannelBuilderFitFilter(const std::string& type, const std::string& name,
+        const IInterface *parent);
+    // destructor
+    ~TileRawChannelBuilderFitFilter();
+
+    // virtual methods
+    virtual StatusCode initialize();
+    virtual StatusCode finalize();
+
+    // Inherited from TileRawChannelBuilder
+    virtual TileRawChannel * rawChannel(const TileDigits* digits);
+
+    /**
+     * AlgTool InterfaceID
+     */
+    static const InterfaceID& interfaceID();
+
+  private:
+
+    void pulseFit(const TileDigits *digit, double &amplitude, double &time, double &pedestal, double &chi2);
+
+    // generic functions for pulse interpolation
+    double pulse(double x, const std::vector<double> * xvec, const std::vector<double>* yvec, bool zeroOutside = false) const;
+
+    double scaledpulse(double x, const std::vector<double> * xvec, const std::vector<double> * yvec) const {
+      return m_fnpar[1] + m_fnpar[2] * pulse(x, xvec, yvec, false);
+    }
+
+    double deriv(double x, const std::vector<double> * xvec, const std::vector<double> * yvec) const {
+      return pulse(x, xvec, yvec, true);
+    }
+
+    std::vector<double> m_dummy; // dummy vector which can be passed to the functions above
+
+    // Pulse shape function variables
+    double m_t0fit;
+    double m_fnpar[3];
+    int m_ipeak0;
+    //double m_phasefrac;
+    double m_min_time;
+    double m_max_time;
+    double m_min_tau;
+    double m_max_tau;
+
+    // Precalculated values for 1 iter
+    std::vector<double> m_gphyslo;
+    std::vector<double> m_dgphyslo;
+    std::vector<double> m_gphyshi;
+    std::vector<double> m_dgphyshi;
+
+    // Parameters
+    int m_frameLength;
+    int m_RMSChannelNoise;
+    int m_maxIterate;
+    int m_extraSamplesLeft;
+    int m_extraSamplesRight;
+
+    double m_SaturatedSampleError; // which error in terms of RMS is assigned to the saturated sample
+    double m_ZeroSampleError;  // which error in terms of RMS is assigned to the zero sample (== 0)
+    double m_NoiseThresholdRMS;     // for pedestal-like events only 2-parametric fit is applied
+    double m_MaxTimeFromPeak; // max.time for fitting the pulse shapes. Samples with t>t0+m_MaxTimeFromPeak are not fitted.
+
+    double m_noiseLow;  // default low gain noise from TileInfo used in simulation
+    double m_noiseHigh; // default high gain noise from TileInfo used in simulation
+
+    // Pulse shapes
+    TilePulseShapesStruct* m_pulsevar;
+};
+
+#define DTIME 25.0  // 25 ns distance between subsequent samples
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderFitFilterCool.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderFitFilterCool.h
new file mode 100755
index 0000000000000000000000000000000000000000..a9aa2bbe27db46ce28fe313cd70405c1a4581c9c
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderFitFilterCool.h
@@ -0,0 +1,138 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELBUILDERFITFILTERCOOL_H
+#define TILERECUTILS_TILERAWCHANNELBUILDERFITFILTERCOOL_H
+
+/********************************************************************
+ *
+ * NAME:     TileRawChannelBuilderFitFilterCool.h 
+ * PACKAGE:  offline/TileCalorimeter/TileRecUtils
+ *
+ * AUTHOR :  K. Gellerstedt, Algorithm by Richard John Teuscher
+ * CREATED:  Dec 03 2003
+ *
+ * PURPOSE:  Build TileRawChannels from digits using fitting
+ *
+ *  Input: TileDigitsContainer
+ *  Output: TileRawChannelContainer
+ *  Parameters: TileRawChannelCont - Name of output container in SG
+ *    FrameLength
+ *    MaxIterate
+ *    RMSChannelNoise
+ ********************************************************************/
+#include "GaudiKernel/IIncidentListener.h"
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilder.h"
+#include "TileConditions/TilePulseShapes.h"
+#include "TileConditions/TileCondToolPulseShape.h"
+#include "TileConditions/TileCondToolNoiseSample.h"
+
+#define PHYS_START_T_HI -75.5
+#define PHYS_START_T_LO -75.5
+#define PHYS_DT_HI 0.5
+#define PHYS_DT_LO 0.5
+#define CIS_START_T_HI -100.
+#define CIS_START_T_LO -100.
+#define SCIS_START_T_HI -72.
+#define SCIS_START_T_LO -72.
+#define CIS_DT_HI 2
+#define LEAK_START_T_HI -100.
+#define LEAK_START_T_LO -100.
+#define SLEAK_START_T_HI -72.
+#define SLEAK_START_T_LO -72.
+#define CIS_DT_LO 2
+#define LAS_START_T_HI -68.
+#define LAS_START_T_LO -68.
+#define LAS_DT_HI 1.8
+#define LAS_DT_LO 1.8
+
+class TileBeamInfoProvider;
+
+class TileRawChannelBuilderFitFilterCool: public TileRawChannelBuilder
+                                        , public IIncidentListener {
+  public:
+
+    // constructor
+    TileRawChannelBuilderFitFilterCool(const std::string& type, const std::string& name,
+        const IInterface *parent);
+    // destructor
+    ~TileRawChannelBuilderFitFilterCool();
+
+    // virtual methods
+    virtual StatusCode initialize();
+    virtual StatusCode finalize();
+
+    // Inherited from TileRawChannelBuilder
+    virtual TileRawChannel * rawChannel(const TileDigits* digits);
+
+    /**
+     * AlgTool InterfaceID
+     */
+    static const InterfaceID& interfaceID();
+
+    virtual void handle(const Incident&);
+
+  private:
+
+    void pulseFit(const TileDigits* digit, double &amplitude, double &time, double &pedestal, double &chi2);
+
+    // generic functions for pulse interpolation
+    double pulse(double x, const std::vector<double> * xvec, const std::vector<double> * yvec, bool zeroOutside = false) const;
+
+    double scaledpulse(double x, const std::vector<double> * xvec, const std::vector<double> * yvec) const {
+      return m_fnpar[1] + m_fnpar[2] * pulse(x, xvec, yvec, false);
+    }
+
+    double deriv(double x, const std::vector<double> * xvec, const std::vector<double> * yvec) const {
+      return pulse(x, xvec, yvec, true);
+    }
+
+    std::vector<double> m_dummy; // dummy vector which can be passed to the functions above
+
+    // Pulse shape function variables
+    double m_t0fit;
+    double m_fnpar[3];
+    int m_ipeak0;
+    double m_min_time;
+    double m_max_time;
+    double m_min_tau;
+    double m_max_tau;
+
+    // Precalculated values for 1 iter
+    std::vector<double> m_gphyslo;
+    std::vector<double> m_dgphyslo;
+    std::vector<double> m_gphyshi;
+    std::vector<double> m_dgphyshi;
+
+    // Parameters
+    int m_frameLength;
+    int m_RMSChannelNoise;
+    int m_maxIterate;
+    int m_extraSamplesLeft;
+    int m_extraSamplesRight;
+
+    double m_SaturatedSampleError; // which error in terms of RMS is assigned to the saturated sample
+    double m_ZeroSampleError;  // which error in terms of RMS is assigned to the zero sample (== 0)
+    double m_NoiseThresholdRMS;     // for pedestal-like events only 2-parametric fit is applied
+    double m_MaxTimeFromPeak; // max.time for fitting the pulse shapes. Samples with t>t0+m_MaxTimeFromPeak are not fitted.
+
+    double m_noiseLow;  // default low gain noise from TileInfo used in simulation
+    double m_noiseHigh; // default high gain noise from TileInfo used in simulation
+
+    // Pulse shapes
+    TilePulseShapesStruct* m_pulsevar;
+
+    ToolHandle<TileCondToolPulseShape> m_tileToolPulseShape;
+    ToolHandle<TileCondToolPulseShape> m_tileToolLeak100Shape;
+    ToolHandle<TileCondToolPulseShape> m_tileToolLeak5p2Shape;
+    ToolHandle<TileCondToolPulseShape> m_tileToolPulse5p2Shape;
+
+    ToolHandle<TileCondToolNoiseSample> m_tileToolNoiseSample;
+    TilePulseShapesStruct* m_shapes;
+
+};
+
+#define DTIME 25.0  // 25 ns distance between subsequent samples
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderFlatFilter.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderFlatFilter.h
new file mode 100755
index 0000000000000000000000000000000000000000..970432f9cddba07ef33504ca80d7e52800e1e78e
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderFlatFilter.h
@@ -0,0 +1,166 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELBUILDERFLATFILTER_H
+#define TILERECUTILS_TILERAWCHANNELBUILDERFLATFILTER_H
+
+/********************************************************************
+ *
+ * NAME:     TileRawChannelBuilderFlatFilter.h 
+ * PACKAGE:  offline/TileCalorimeter/TileRecUtils
+ *
+ * AUTHOR :  K. Gellerstedt
+ * CREATED:  Oct 17 2003
+ *
+ * PURPOSE:  Build TileRawChannels from digits using flat filter
+ *
+ *  Input: TileDigitsContainer
+ *  Output: TileRawChannelContainer
+ *  Parameters: TileRawChannelCont - Name of output container in SG
+ *   PedStart
+ *   PedLength
+ *   PedOffset
+ *   SignalStart
+ *   SignalLength
+ *   FilterLength
+ *   FrameLength
+ *   DeltaCutLo
+ *   DeltaCutHi
+ *   RMSCutLo
+ *   RMSCutHi
+ ********************************************************************/
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilder.h"
+
+#include <inttypes.h>
+
+class TileRawChannelBuilderFlatFilter: public TileRawChannelBuilder {
+  public:
+
+    // constructor
+    TileRawChannelBuilderFlatFilter(const std::string& type, const std::string& name,
+        const IInterface *parent);
+    // destructor
+    ~TileRawChannelBuilderFlatFilter();
+
+    // virtual methods
+    virtual StatusCode initialize();
+    virtual StatusCode finalize();
+
+    // Inherited from TileRawChannelBuilder
+    virtual TileRawChannel * rawChannel(const TileDigits* digits);
+
+    /**
+     * Filter given digits using FlatFilter method
+     * @param digits as vector of unsigned 32 bit integers
+     * @param amplitude
+     * @param time
+     */
+    void flatFilter(const std::vector<uint32_t> &digits, const int gain
+                    , double& amplitude, double& time) const;
+
+    /**
+     * Filter given digits using FlatFilter method
+     * @param digits as vector of double
+     * @param amplitude
+     * @param time
+     */
+    void flatFilter(const std::vector<float> &digits, const int gain
+                    , double& amplitude, double& time) const;
+
+    /**
+     * AlgTool InterfaceID
+     */
+    static const InterfaceID& interfaceID();
+
+  private:
+
+    /**
+     * Find min and max sample
+     * Calculate mean and RMS of all samples.
+     * Returns true i signal is found
+     * @param digits Vector of digits(double)
+     * @param delatCut Delta cut value
+     * @param rmsCut RMS cut value
+     * @param frameMax Storage for max sample
+     * @param frameMin Storage for frame min
+     * @param frameMean Storage for frame mean
+     * @param frameRMS Storage for frame RMS
+     * @return If signal was found
+     */
+    bool isSignalInFrame(const std::vector<float> &digits, double deltaCut, double rmsCut,
+                          int &frameMax, int &frameMin, double &frameMean, double &frameRMS) const;
+    /**
+     * Calculate pedestal as mean of <pedLgt> samples
+     * starting from <pedStart>
+     * @param digits Vector of digits(double)
+     * @param pedStart Start of pedestal
+     * @param pedLgt Length of pedestal
+     * @return Pedestal value
+     */
+    double getPedestal(const std::vector<float> &digits, int pedStart, int pedLgt) const;
+
+    /**
+     * Get position of maximal adder (sum of <filterLength> samples)
+     * in whole frame and in signal window (<signalStart>-><signalStart>+<signalLength>
+     * Return position and value of maximal adder in frame
+     * @param digits Vector if digits(double)
+     * @param filterLength
+     * @param signalStart Start of signal window
+     * @param signalLength Length of signal window
+     * @param
+     * return Position of max adder
+     */
+    int getMaxAdder(const std::vector<float> &digits, int filterLength, int signalStart,
+                    int signalLength, int &tMax, int &tMaxFrame, int &adderFrame) const;
+
+    int getMaxSample(const std::vector<float> &digits, int signalStart, int signalLength
+                    , int &tMax, int &tMaxFrame, int &sampleFrame) const;
+
+    double calculatePeak(const std::vector<float> &digits, int peakPos, double ped,
+                         double &position) const;
+
+    /**
+     * Calculate energy using flat filter
+     * Sum of <filterLength> samples starting at <filterStart>
+     * Requires pedestal value and returns position of peak
+     * @param digits Vector of didits(double>
+     * @param filterStart Start position for filter
+     * @param filterLength Number of samples
+     * @param ped Pedestal value
+     * @param position Storage place for position of peak
+     * @return Energy
+     */
+    double calculateFlatFilter(const std::vector<float> &digits, int filterStart
+                              , int filterLength, double ped, double &position) const;
+
+    /**
+     * Calculate the time of signal in window of <signalLength> starting at <signalStart>
+     * Result is sum of time for every sample weighted with amplitude
+     * and divided by sum of times
+     * @param digits Vector of digits(double)
+     * @param signalStart Start of signal
+     * @param signalLength Length of signal in samples
+     * @param ped Pedestal value
+     * @return Time of signal
+     */
+    double getTime(const std::vector<float> &digits, int signalStart
+                  , int signalLength, double ped) const;
+
+    // parameters
+    int m_pedStart; // start of pedestal window
+    int m_pedLength; // length of pedestal window
+    int m_pedOffset; // if > 0 ped can be taken after signal as well
+    int m_signalStart; // start of signal window
+    int m_signalLength; // length of signal window
+    int m_filterLength;
+    int m_frameLength;
+
+    double m_deltaCut[2]; // Cut for signalInFrame, lo/hi gain
+    double m_rmsCut[2]; // RMS cut for signalInFram, lo/hi gain
+
+};
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderMF.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderMF.h
new file mode 100755
index 0000000000000000000000000000000000000000..78e94b22a5f867797d7d74bdec4c678c60397e76
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderMF.h
@@ -0,0 +1,71 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELBUILDERMF_H
+#define TILERECUTILS_TILERAWCHANNELBUILDERMF_H
+
+/********************************************************************
+ *
+ * NAME:     TileRawChannelBuilderMF.h 
+ * PACKAGE:  offline/TileCalorimeter/TileRecUtils
+ *
+ * AUTHOR :  B. Peralva
+ * CREATED:  September 2012
+ *
+ * PURPOSE:  Build TileRawChannels from digits using Matched filter
+ *
+ *  Input: TileDigitsContainer
+ *  Output: TileRawChannelContainer
+ *  Parameters: TileRawChannelCont - Name of output container in SG
+ ********************************************************************/
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilder.h"
+#include "TileConditions/TileCondToolOfcCool.h"
+#include "TileConditions/TileCondToolTiming.h"
+#include "TileConditions/TileCondToolNoiseSample.h"
+
+class TileHWID;
+class TileInfo;
+class TileFilterManager;
+
+class TileRawChannelBuilderMF: public TileRawChannelBuilder {
+  public:
+
+    // constructor
+    TileRawChannelBuilderMF(const std::string& type, const std::string& name,
+        const IInterface *parent);
+    // destructor
+    ~TileRawChannelBuilderMF();
+
+    // virtual methods
+    virtual StatusCode initialize();
+    virtual StatusCode finalize();
+
+    // Inherited from TileRawChannelBuilder
+    virtual TileRawChannel * rawChannel(const TileDigits* digits);
+
+  private:
+    ToolHandle<TileCondToolTiming> m_tileToolTiming;
+    ToolHandle<ITileCondToolOfc> m_tileCondToolOfcCool;
+    ToolHandle<TileCondToolNoiseSample> m_tileToolNoiseSample;
+
+    bool Are3FF(float &dmin, float &dmax); //!< Checks that all the samples are 0x3FF (as sent by the DSP when no data arrives)
+
+    bool m_correctAmplitude; //!< If true, resulting amplitude is corrected when using weights for tau=0 without iteration
+    int m_pedestalMode;	  // -1 pedestal from conditions, 0 - fixed pedestal, 1 (default) pedestal from data
+    double m_defPedestal;  // use a fixed pedestal value
+    int m_MF; // 0 - COF, 1 - MF (created for muon receiver board simulation)
+    int m_NSamp; //!< number of samples in the data
+    int m_t0Samp;  //!< position of peak sample = (m_NSamp-1)/2
+    double m_maxTime; //!< max allowed time = 25*(m_NSamp-1)/2
+    double m_minTime; //!< min allowed time = -25*(m_NSamp-1)/2
+
+    std::vector<float> digits;
+    int m_chPedCounter[5][64][48][2];
+    float m_chPed[5][64][48][2];
+
+};
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderManyAmps.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderManyAmps.h
new file mode 100755
index 0000000000000000000000000000000000000000..a208f30cc6c155c11bcf40ae01380b37ddf7b96e
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderManyAmps.h
@@ -0,0 +1,59 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELBUILDERMANYAMPS_H
+#define TILERECUTILS_TILERAWCHANNELBUILDERMANYAMPS_H
+
+/********************************************************************
+ *
+ * NAME:     TileRawChannelBuilderManyamps.h 
+ * PACKAGE:  offline/TileCalorimeter/TileRecUtils
+ *
+ * AUTHOR :  X. Poveda
+ * CREATED:  March 2006
+ *
+ * PURPOSE:  Build TileRawChannels from digits using flat filter
+ *
+ *  Input: TileDigitsContainer
+ *  Output: TileRawChannelContainer
+ *  Parameters: TileRawChannelCont - Name of output container in SG
+ ********************************************************************/
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilder.h"
+
+class TileHWID;
+class TileInfo;
+class TileFilterManager;
+
+class TileRawChannelBuilderManyAmps: public TileRawChannelBuilder {
+  public:
+
+    // constructor
+    TileRawChannelBuilderManyAmps(const std::string& type, const std::string& name,
+        const IInterface *parent);
+    // destructor
+    ~TileRawChannelBuilderManyAmps();
+
+    // virtual methods
+    virtual StatusCode initialize();
+    virtual StatusCode finalize();
+
+    // Inherited from TileRawChannelBuilder
+    virtual TileRawChannel* rawChannel(const TileDigits* digits);
+
+  private:
+
+    int m_digitFilterMode; //<! 2=> start with 1 amplitude and add needed; 3=> start with 8 amplitudes and drop spurious
+    int m_digitFilterLevel; //<! number of parameters for fit (3-9 for mode 2)
+    int m_digitFilterTest;  //<! non-zero means call Tester (during initialization phase)
+
+    //IChronoStatSvc* m_timeKeeper;
+
+    TileFilterManager* m_tileFilterManagerHi; //<! TileFilterManager for high gain
+    TileFilterManager* m_tileFilterManagerLo; //<! TileFilterManager for low gain
+
+};
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderOpt2Filter.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderOpt2Filter.h
new file mode 100755
index 0000000000000000000000000000000000000000..cebb39940cd7f861a2580e34a94a65040acc5768
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderOpt2Filter.h
@@ -0,0 +1,122 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELBUILDEROPT2FILTER_H
+#define TILERECUTILS_TILERAWCHANNELBUILDEROPT2FILTER_H
+
+//////////////////////////////////////////////////////////////////////
+//
+//     Based on the code of Ximo Poveda@cern.ch. June 2007
+//     Andrei.Artamonov@cern.ch, July 2008
+//
+//     TileRawChannelBuilderOpt2Filter.h
+//
+//     implementation of the Optimal Filtering based on Lagrange multipliers
+//       for energy/time reconstruction in TileCal 
+//
+//////////////////////////////////////////////////////////////////////
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilder.h"
+#include "TileConditions/ITileCondToolOfc.h"
+#include "TileConditions/TileCondToolOfcCool.h"
+#include "TileConditions/TileCondToolTiming.h"
+#include "TileConditions/TileCondToolNoiseSample.h"
+
+#include <vector>
+#include <string>
+
+/**
+ *
+ * @class TileRawChannelBuilderOpt2Filter
+ * @brief Reconstructs Tile digitized pulses (ie, computes amplitude, time and pedestal) as a linear combination of the samples
+ *
+ * This class implements an energy recontruction method known as Optimal
+ * Filtering. It takes as input the digital samples from the digitizer boards
+ * in the front-end electronics and outputs the amplitude, time and pedestal
+ * of the pulse. Full details and fundaments of the method can be found in
+ * the ATL-TILECAL-2005-001 note. Two different versions of the algorithms
+ * are currently used: OF1 (with 2 parameters: amplitude and time) and OF2
+ * (with 3 parameters (amplitude, time and pedestal).
+ *
+ * OFCs are calculated on-the-fly (using TileCondToolOfc) or are extracted
+ * from COOL database (TileCondToolOfcCool). In case of non-iterative
+ * procedure, optionally, the initial, "best phase", can be extracted
+ * from COOL DB by means of TileCondToolTiming.
+ */
+class TileRawChannelBuilderOpt2Filter: public TileRawChannelBuilder {
+  public:
+
+    TileRawChannelBuilderOpt2Filter(const std::string& type, const std::string& name,
+        const IInterface *parent); //!< Constructor
+    ~TileRawChannelBuilderOpt2Filter(); //!< Destructor
+
+    // virtual methods
+    virtual StatusCode initialize(); //!< Initialize method
+    //virtual StatusCode execute();
+    virtual StatusCode finalize(); //!< Finalize method
+
+    // Inherited from TileRawChannelBuilder
+    virtual TileRawChannel * rawChannel(const TileDigits* digits);
+
+    /**
+     * AlgTool InterfaceID
+     */
+    static const InterfaceID& interfaceID();
+
+  private:
+
+    //!< Callback added to handle Data-driven GeoModel initialization
+    virtual StatusCode geoInit(IOVSVC_CALLBACK_ARGS);
+
+    ToolHandle<TileCondToolTiming> m_tileToolTiming;
+    ToolHandle<ITileCondToolOfc> m_tileCondToolOfc;
+    ToolHandle<TileCondToolOfcCool> m_tileCondToolOfcCool;
+    ToolHandle<TileCondToolNoiseSample> m_tileToolNoiseSample; //!< tool which provides noise values
+
+    double Filter(int, int, int, int &, double &, double &, double &); //!< Applies OF algorithm
+    float FindMaxDigit();  //!< Finds maximum digit position in the pulse
+    float SetPedestal(int, int, int, int); //!< Sets pedestal estimation for OF1
+    int Iterator(int, int, int, int, double &, double &, double &, double &); //!< Apply the number of iterations needed for reconstruction by calling the Filter method
+    double Compute(int, int, int, int, double &, double &, double &, double); //!< Computes A,time,ped using OF. If iterations are required, the Iterator method is used
+    void BuildPulseShape(std::vector<double> &m_pulseShape, std::vector<double> &m_pulseShapeX
+        , std::vector<double> &m_pulseShapeT, int dignum, MsgStream &log); //!< Builds pulse shapes
+
+    void ofc2int(int ndigits, double* w_off, short* w_int, short& scale); // convert weights to dsp short int format
+
+    int m_maxIterations; //!< maximum number of iteration to perform
+    int m_pedestalMode;  //!< pedestal mode to use
+    bool m_ConfTB;       //!< use testbeam configuration
+    double m_timeForConvergence; //!< minimum time difference to quit iteration procedure
+    bool m_of2;    //!< bool variable for OF method: true=> OF2;  false=> OF1
+    bool m_minus1Iter;   //!< bool variable for whether to apply -1 iteration (initial phase guess)
+    bool m_correctAmplitude; //!< If true, resulting amplitude is corrected when using weights for tau=0 without iteration
+
+    bool m_bestphase; // if true, use best phase from COOL DB in "fixed phase" mode (i.e., no iterations)
+    bool m_ofcfromcool; // if true, take OFCs from DB (no on-fly calculations)
+    bool m_emulatedsp; // if true, emulate DSP reconstruction algorithm
+    int c_signal; //!< internal counters
+    int c_negat;  //!< internal counters
+    int c_center; //!< internal counters
+    int c_const;  //!< internal counters
+
+    int m_NSamp; //!< number of samples in the data
+    int m_t0Samp;  //!< position of peak sample = (m_NSamp-1)/2
+    double m_maxTime; //!< max allowed time = 25*(m_NSamp-1)/2
+    double m_minTime; //!< min allowed time = -25*(m_NSamp-1)/2
+
+    std::vector<double> m_LpulseShape_cis;  //!< vector for low gain/CIS pulse shape
+    std::vector<double> m_HpulseShape_cis;  //!< vector for high gain/CIS pulse shape
+    std::vector<double> m_LpulseShape_phys; //!< vector for low gain/Physics pulse shape
+    std::vector<double> m_HpulseShape_phys; //!< vector for high gain/Physics pulse shape
+
+    std::vector<double> m_LdpulseShape_cis;  //!< vector for low gain/CIS pulse derivative
+    std::vector<double> m_HdpulseShape_cis;  //!< vector for high gain/CIS pulse derivative
+    std::vector<double> m_LdpulseShape_phys; //!< vector for low gain/Physics pulse derivative
+    std::vector<double> m_HdpulseShape_phys; //!< vector for high gain/Physics pulse derivative
+
+    std::vector<float> OptFilterDigits;
+};
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderOpt2FilterLookup.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderOpt2FilterLookup.h
new file mode 100644
index 0000000000000000000000000000000000000000..a13806556266f04be2300b864b0d644deabfcb73
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderOpt2FilterLookup.h
@@ -0,0 +1,261 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELBUILDEROPT2FILTERLOOKUP_H
+#define TILERECUTILS_TILERAWCHANNELBUILDEROPT2FILTERLOOKUP_H
+
+//////////////////////////////////////////////////////////////////////
+//
+// copied from online to for DSP OF reconstruction emulation
+// by      Andrei.Artamonov@cern.ch
+// ------------ original comments:
+// LUT calculated in
+// afs_ific@dev/LUT
+// 25-jul-2007
+//////////////////////////////////////////////////////////////////////
+
+unsigned short lookup[2401]={
+0x000e,
+0x8000, 0x4000, 0x2aab, 0x2000, 0x199a, 0x1555, 0x1249, 0x1000, 0x0e39, 0x0ccd, 
+0x0ba3, 0x0aab, 0x09d9, 0x0925, 0x0889, 0x0800, 0x0788, 0x071c, 0x06bd, 0x0666, 
+0x0618, 0x05d1, 0x0591, 0x0555, 0x051f, 0x04ec, 0x04be, 0x0492, 0x046a, 0x0444, 
+0x0421, 0x0400, 0x03e1, 0x03c4, 0x03a8, 0x038e, 0x0376, 0x035e, 0x0348, 0x0333, 
+0x031f, 0x030c, 0x02fa, 0x02e9, 0x02d8, 0x02c8, 0x02b9, 0x02ab, 0x029d, 0x028f, 
+0x0283, 0x0276, 0x026a, 0x025f, 0x0254, 0x0249, 0x023f, 0x0235, 0x022b, 0x0222, 
+0x0219, 0x0211, 0x0208, 0x0200, 0x01f8, 0x01f0, 0x01e9, 0x01e2, 0x01db, 0x01d4, 
+0x01ce, 0x01c7, 0x01c1, 0x01bb, 0x01b5, 0x01af, 0x01aa, 0x01a4, 0x019f, 0x019a, 
+0x0195, 0x0190, 0x018b, 0x0186, 0x0182, 0x017d, 0x0179, 0x0174, 0x0170, 0x016c, 
+0x0168, 0x0164, 0x0160, 0x015d, 0x0159, 0x0155, 0x0152, 0x014e, 0x014b, 0x0148, 
+0x0144, 0x0141, 0x013e, 0x013b, 0x0138, 0x0135, 0x0132, 0x012f, 0x012d, 0x012a, 
+0x0127, 0x0125, 0x0122, 0x011f, 0x011d, 0x011a, 0x0118, 0x0116, 0x0113, 0x0111, 
+0x010f, 0x010d, 0x010a, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00fe, 0x00fc, 
+0x00fa, 0x00f8, 0x00f6, 0x00f5, 0x00f3, 0x00f1, 0x00ef, 0x00ed, 0x00ec, 0x00ea, 
+0x00e8, 0x00e7, 0x00e5, 0x00e4, 0x00e2, 0x00e0, 0x00df, 0x00dd, 0x00dc, 0x00da, 
+0x00d9, 0x00d8, 0x00d6, 0x00d5, 0x00d3, 0x00d2, 0x00d1, 0x00cf, 0x00ce, 0x00cd, 
+0x00cc, 0x00ca, 0x00c9, 0x00c8, 0x00c7, 0x00c5, 0x00c4, 0x00c3, 0x00c2, 0x00c1, 
+0x00c0, 0x00bf, 0x00bd, 0x00bc, 0x00bb, 0x00ba, 0x00b9, 0x00b8, 0x00b7, 0x00b6, 
+0x00b5, 0x00b4, 0x00b3, 0x00b2, 0x00b1, 0x00b0, 0x00af, 0x00ae, 0x00ad, 0x00ac, 
+0x00ac, 0x00ab, 0x00aa, 0x00a9, 0x00a8, 0x00a7, 0x00a6, 0x00a5, 0x00a5, 0x00a4, 
+0x00a3, 0x00a2, 0x00a1, 0x00a1, 0x00a0, 0x009f, 0x009e, 0x009e, 0x009d, 0x009c, 
+0x009b, 0x009b, 0x009a, 0x0099, 0x0098, 0x0098, 0x0097, 0x0096, 0x0096, 0x0095, 
+0x0094, 0x0094, 0x0093, 0x0092, 0x0092, 0x0091, 0x0090, 0x0090, 0x008f, 0x008e, 
+0x008e, 0x008d, 0x008d, 0x008c, 0x008b, 0x008b, 0x008a, 0x008a, 0x0089, 0x0089, 
+0x0088, 0x0087, 0x0087, 0x0086, 0x0086, 0x0085, 0x0085, 0x0084, 0x0084, 0x0083, 
+0x0083, 0x0082, 0x0082, 0x0081, 0x0081, 0x0080, 0x0080, 0x007f, 0x007f, 0x007e, 
+0x007e, 0x007d, 0x007d, 0x007c, 0x007c, 0x007b, 0x007b, 0x007a, 0x007a, 0x0079, 
+0x0079, 0x0078, 0x0078, 0x0078, 0x0077, 0x0077, 0x0076, 0x0076, 0x0075, 0x0075, 
+0x0075, 0x0074, 0x0074, 0x0073, 0x0073, 0x0073, 0x0072, 0x0072, 0x0071, 0x0071, 
+0x0071, 0x0070, 0x0070, 0x006f, 0x006f, 0x006f, 0x006e, 0x006e, 0x006e, 0x006d, 
+0x006d, 0x006d, 0x006c, 0x006c, 0x006b, 0x006b, 0x006b, 0x006a, 0x006a, 0x006a, 
+0x0069, 0x0069, 0x0069, 0x0068, 0x0068, 0x0068, 0x0067, 0x0067, 0x0067, 0x0066, 
+0x0066, 0x0066, 0x0065, 0x0065, 0x0065, 0x0065, 0x0064, 0x0064, 0x0064, 0x0063, 
+0x0063, 0x0063, 0x0062, 0x0062, 0x0062, 0x0062, 0x0061, 0x0061, 0x0061, 0x0060, 
+0x0060, 0x0060, 0x0060, 0x005f, 0x005f, 0x005f, 0x005e, 0x005e, 0x005e, 0x005e, 
+0x005d, 0x005d, 0x005d, 0x005d, 0x005c, 0x005c, 0x005c, 0x005c, 0x005b, 0x005b, 
+0x005b, 0x005b, 0x005a, 0x005a, 0x005a, 0x005a, 0x0059, 0x0059, 0x0059, 0x0059, 
+0x0058, 0x0058, 0x0058, 0x0058, 0x0057, 0x0057, 0x0057, 0x0057, 0x0056, 0x0056, 
+0x0056, 0x0056, 0x0056, 0x0055, 0x0055, 0x0055, 0x0055, 0x0054, 0x0054, 0x0054, 
+0x0054, 0x0054, 0x0053, 0x0053, 0x0053, 0x0053, 0x0053, 0x0052, 0x0052, 0x0052, 
+0x0052, 0x0052, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0050, 0x0050, 0x0050, 
+0x0050, 0x0050, 0x004f, 0x004f, 0x004f, 0x004f, 0x004f, 0x004e, 0x004e, 0x004e, 
+0x004e, 0x004e, 0x004d, 0x004d, 0x004d, 0x004d, 0x004d, 0x004d, 0x004c, 0x004c, 
+0x004c, 0x004c, 0x004c, 0x004c, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x004a, 
+0x004a, 0x004a, 0x004a, 0x004a, 0x004a, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 
+0x0049, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0047, 0x0047, 
+0x0047, 0x0047, 0x0047, 0x0047, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 
+0x0046, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0044, 0x0044, 
+0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 
+0x0043, 0x0043, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 
+0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0040, 0x0040, 
+0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x003f, 0x003f, 0x003f, 0x003f, 
+0x003f, 0x003f, 0x003f, 0x003f, 0x003e, 0x003e, 0x003e, 0x003e, 0x003e, 0x003e, 
+0x003e, 0x003e, 0x003d, 0x003d, 0x003d, 0x003d, 0x003d, 0x003d, 0x003d, 0x003d, 
+0x003d, 0x003c, 0x003c, 0x003c, 0x003c, 0x003c, 0x003c, 0x003c, 0x003c, 0x003c, 
+0x003b, 0x003b, 0x003b, 0x003b, 0x003b, 0x003b, 0x003b, 0x003b, 0x003b, 0x003b, 
+0x003a, 0x003a, 0x003a, 0x003a, 0x003a, 0x003a, 0x003a, 0x003a, 0x003a, 0x0039, 
+0x0039, 0x0039, 0x0039, 0x0039, 0x0039, 0x0039, 0x0039, 0x0039, 0x0039, 0x0038, 
+0x0038, 0x0038, 0x0038, 0x0038, 0x0038, 0x0038, 0x0038, 0x0038, 0x0038, 0x0038, 
+0x0037, 0x0037, 0x0037, 0x0037, 0x0037, 0x0037, 0x0037, 0x0037, 0x0037, 0x0037, 
+0x0037, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 
+0x0036, 0x0036, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 
+0x0035, 0x0035, 0x0035, 0x0035, 0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 
+0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 0x0034, 0x0033, 0x0033, 0x0033, 0x0033, 
+0x0033, 0x0033, 0x0033, 0x0033, 0x0033, 0x0033, 0x0033, 0x0033, 0x0032, 0x0032, 
+0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 0x0032, 
+0x0032, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 
+0x0031, 0x0031, 0x0031, 0x0031, 0x0031, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 
+0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x002f, 
+0x002f, 0x002f, 0x002f, 0x002f, 0x002f, 0x002f, 0x002f, 0x002f, 0x002f, 0x002f, 
+0x002f, 0x002f, 0x002f, 0x002f, 0x002e, 0x002e, 0x002e, 0x002e, 0x002e, 0x002e, 
+0x002e, 0x002e, 0x002e, 0x002e, 0x002e, 0x002e, 0x002e, 0x002e, 0x002e, 0x002e, 
+0x002d, 0x002d, 0x002d, 0x002d, 0x002d, 0x002d, 0x002d, 0x002d, 0x002d, 0x002d, 
+0x002d, 0x002d, 0x002d, 0x002d, 0x002d, 0x002d, 0x002c, 0x002c, 0x002c, 0x002c, 
+0x002c, 0x002c, 0x002c, 0x002c, 0x002c, 0x002c, 0x002c, 0x002c, 0x002c, 0x002c, 
+0x002c, 0x002c, 0x002c, 0x002b, 0x002b, 0x002b, 0x002b, 0x002b, 0x002b, 0x002b, 
+0x002b, 0x002b, 0x002b, 0x002b, 0x002b, 0x002b, 0x002b, 0x002b, 0x002b, 0x002b, 
+0x002b, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 
+0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x0029, 
+0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 
+0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0028, 
+0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 
+0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0027, 
+0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 
+0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 0x0027, 
+0x0027, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 
+0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 0x0026, 
+0x0026, 0x0026, 0x0026, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 
+0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 
+0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0025, 0x0024, 0x0024, 0x0024, 
+0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 
+0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 
+0x0024, 0x0024, 0x0024, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 
+0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 
+0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0023, 0x0022, 
+0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 
+0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 
+0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0021, 0x0021, 
+0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 
+0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 
+0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0020, 0x0020, 
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 
+0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 
+0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 
+0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 0x001f, 
+0x001f, 0x001f, 0x001f, 0x001f, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 
+0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 
+0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 
+0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 
+0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 
+0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 
+0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 
+0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001d, 0x001c, 
+0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 
+0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 
+0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 
+0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 0x001c, 
+0x001c, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 
+0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 
+0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 
+0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 
+0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, 0x001a, 0x001a, 0x001a, 0x001a, 
+0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 
+0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 
+0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 
+0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 
+0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 
+0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 
+0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 
+0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 
+0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 
+0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0019, 0x0018, 0x0018, 0x0018, 
+0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 
+0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 
+0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 
+0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 
+0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 
+0x0018, 0x0018, 0x0018, 0x0018, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 
+0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 
+0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 
+0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 
+0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 
+0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 
+0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0017, 0x0016, 0x0016, 0x0016, 0x0016, 
+0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 
+0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 
+0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 
+0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 
+0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 
+0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 
+0x0016, 0x0016, 0x0016, 0x0016, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 
+0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 
+0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 
+0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 
+0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 
+0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 
+0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 
+0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0015, 0x0014, 0x0014, 
+0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 
+0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 
+0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 
+0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 
+0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 
+0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 
+0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 
+0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 
+0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 
+0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 
+0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 
+0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 
+0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 
+0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 
+0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 
+0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 
+0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 
+0x0013, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 0x0012, 
+0x0012, 0x0012, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 
+0x0011, 0x0011, 0x0011, 0x0011, 0x0011, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 
+0x0010, 0x0010, 0x0010, 0x0010, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 
+0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000f, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 
+0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e  }; 
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderOptFilter.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderOptFilter.h
new file mode 100755
index 0000000000000000000000000000000000000000..a2b6081f667672c197978dd4287db474bf518ff7
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderOptFilter.h
@@ -0,0 +1,114 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELBUILDEROPTFILTER_H
+#define TILERECUTILS_TILERAWCHANNELBUILDEROPTFILTER_H
+
+//////////////////////////////////////////////////////////////////////
+//
+//     Tilecal Valencia, Ximo.Poveda@cern.ch. June 2007
+//
+//     TileRawChannelBuilderOptFilter.h
+//
+//     implementation of the Optimal Filtering based on Lagrange multipliers
+//       for energy/time reconstruction in TileCal 
+//
+//////////////////////////////////////////////////////////////////////
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilder.h"
+#include "TileConditions/TileOptFilterWeights.h"
+#include "TileConditions/TilePulseShapes.h"
+
+#include <vector>
+#include <string>
+
+/**
+ *
+ * @class TileRawChannelBuilderOptFilter
+ * @brief Reconstructs Tile digitized pulses (ie, computes amplitude, time and pedestal) as a linear combination of the samples
+ *
+ * This class implements a energy reconstruction method known as Optimal
+ * Filtering. It takes as input the digital samples from the digitizer boards
+ * in the front-end electronics and outputs the amplitude, time and pedestal
+ * of the pulse. Full details and fundaments of the method can be found in
+ * the ATL-TILECAL-2005-001 note. Two different versions of the algorithms
+ * are currently used: OF1 (with 2 parameters: amplitude and time) and OF2
+ * (with 3 parameters (amplitude, time and pedestal).
+ */
+class TileRawChannelBuilderOptFilter: public TileRawChannelBuilder {
+  public:
+
+    TileRawChannelBuilderOptFilter(const std::string& type, const std::string& name,
+        const IInterface *parent); //!< Constructor
+    ~TileRawChannelBuilderOptFilter(); //!< Destructor
+
+    // virtual methods
+    virtual StatusCode initialize(); //!< Initialize method
+    //virtual StatusCode execute();
+    virtual StatusCode finalize(); //!< Finalize method
+
+    // Inherited from TileRawChannelBuilder
+    virtual TileRawChannel* rawChannel(const TileDigits* digits);
+
+    /**
+     * AlgTool InterfaceID
+     */
+    static const InterfaceID& interfaceID();
+
+  private:
+
+    double Filter(int, int, int, int &, double &, double &, double &); //!< Applies OF algorithm
+    float MaxDigDiff(); //!< Computes maximum difference between digits
+    float MaxDigit(); //!< Finds maximum digit value in the pulse
+    float FindMaxDigit();  //!< Finds maximum digit position in the pulse
+    bool Are3FF(); //!< Checks that all the samples are 0x3FF (as sent by the DSP when no data arrives)
+    float SetPedestal(); //!< Sets pedestal estimation for OF1
+    int Iterator(int, int, int, int, double &, double &, double &, double &); //!< Apply the number of iterations needed for reconstruction by calling the Filter method
+    double Compute(int, int, int, int, double &, double &, double &, int); //!< Computes A,time,ped using OF. If iterations are required, the Iterator method is used
+    void BuildPulseShape(std::vector<double> &m_pulseShape, std::vector<double> &m_pulseShapeX,
+        std::vector<double> &m_pulseShapeT, int dignum); //!< Builds pulse shapes
+
+    int m_maxIterations; //!< maximum number of iteration to perform
+    int m_pedestalMode;  //!< pedestal mode to use
+    bool m_ConfTB;       //!< use testbeam configuration
+    double m_timeForConvergence; //!< minimum time difference to quit iteration procedure
+    bool m_of2;    //!< bool variable for OF method: true=> OF2;  false=> OF1
+    bool m_minus1Iter;   //!< bool variable for whether to apply -1 iteration (initial phase guess)
+    bool m_correctAmplitude; //!< If true, resulting amplitude is corrected when using weights for tau=0 without iteration
+
+    int c_signal; //!< internal counters
+    int c_negat;  //!< internal counters
+    int c_center; //!< internal counters
+
+    int m_NSamp;   //!< number of samples in the data
+    int m_t0Samp;  //!< position of peak sample = (m_NSamp-1)/2
+    double m_maxTime; //!< max allowed time = 25*(m_NSamp-1)/2
+    double m_minTime; //!< min allowed time = -25*(m_NSamp-1)/2
+
+    /*
+     double a_phys_simp[2][9][25], b_phys_simp[2][9][25];
+     double a_cis_simp[2][7][25], b_cis_simp[2][7][25];
+     double a_phys[4][64][48][2][9][25], b_phys[4][64][48][2][9][25];
+     double a_cis[4][64][48][2][7][25], b_cis[4][64][48][2][7][25];
+     */
+    std::vector<double> m_LpulseShape_cis;  //!< vector for low gain/CIS pulse shape
+    std::vector<double> m_HpulseShape_cis;  //!< vector for high gain/CIS pulse shape
+    std::vector<double> m_LpulseShape_phys; //!< vector for low gain/Physics pulse shape
+    std::vector<double> m_HpulseShape_phys; //!< vector for high gain/Physics pulse shape
+
+    std::vector<double> m_LdpulseShape_cis;  //!< vector for low gain/CIS pulse derivative
+    std::vector<double> m_HdpulseShape_cis;  //!< vector for high gain/CIS pulse derivative
+    std::vector<double> m_LdpulseShape_phys; //!< vector for low gain/Physics pulse derivative
+    std::vector<double> m_HdpulseShape_phys; //!< vector for high gain/Physics pulse derivative
+
+    TilePulseShapesStruct* m_pulseShapes;       //!< structure for pulse shapes
+
+    TileOptFilterWeightsStruct *m_weights;      //!< structure for OF weights
+    //  TileOptFilterCorrelationStruct *m_correla;   //!< structure for pulse shapes
+
+    std::vector<float> OptFilterDigits;
+};
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelMaker.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelMaker.h
new file mode 100755
index 0000000000000000000000000000000000000000..2c39ffe9b8d5b0031353d84fdb0d5af68d33c674
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelMaker.h
@@ -0,0 +1,72 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELMAKER_H
+#define TILERECUTILS_TILERAWCHANNELMAKER_H
+
+/********************************************************************
+ *
+ * NAME:     TileRawChanelMaker 
+ * PACKAGE:  offline/TileCalorimeter/TileRecalgs
+ *
+ * AUTHOR :  K. Gellerstedt
+ * CREATED:  Nov 17 2003
+ *
+ * PURPOSE:  Creation of TileRawChannels using different sub algorithms
+ *
+ *  Input:  TileDigits container
+ *  Output: Containers with TileRawChannels
+ *  Parameters:
+ *    TileRawChannelContainer - Name of output containers
+ *    TileRawChannelBuilder - Name of builder sub-algorithms
+ *    TileDigitsContainer - Name of input container
+ *   
+ ********************************************************************/
+
+// Gaudi includes
+#include "GaudiKernel/ToolHandle.h"
+
+// Atlas includes
+#include "AthenaBaseComps/AthAlgorithm.h"
+
+class TileRawChannelBuilder;
+
+class TileRawChannelMaker: public AthAlgorithm {
+
+  public:
+    // constructor
+    TileRawChannelMaker(const std::string& name, ISvcLocator* pSvcLocator);
+    // destructor
+    virtual ~TileRawChannelMaker();
+
+    virtual StatusCode initialize();
+    virtual StatusCode execute();
+    virtual StatusCode finalize();
+
+  private:
+
+    void fitOverflowedChannels(void);
+
+    // name of TDS container with TileDigits
+    std::string m_TileDigitsContainerID;
+
+    /**
+     * Vector with class name[/instance name] of builder sub-algs
+     */
+    std::vector<std::string> m_TileRawChannelBuilderIDVec;
+
+    /**
+     * Vector of builder algtools
+     */
+    //  std::vector<TileRawChannelBuilder*> m_TileRawChannelBuilderVec;
+    ToolHandleArray<TileRawChannelBuilder> m_tileRawChannelBuilderList;
+
+    bool m_fitOverflow;
+    ToolHandle<TileRawChannelBuilder> m_tileRawChannelBuilderFitOverflow;
+
+    float m_overflowReplaceTimeCut;
+    float m_overflowReplacePedestalCut;
+    float m_overflowReplaceChi2Cut;
+};
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelNoiseFilter.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelNoiseFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..5b4d2eb925eadb47dfe009c7819fb96b7f356800
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelNoiseFilter.h
@@ -0,0 +1,77 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TILERAWCHANNELNOISEFILTER_H
+#define TILERAWCHANNELNOISEFILTER_H
+
+// Atlas includes
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "GaudiKernel/ToolHandle.h"
+#include "GaudiKernel/ServiceHandle.h"
+
+// Tile includes
+#include "TileIdentifier/TileRawChannelUnit.h"
+#include "TileConditions/TileCondToolEmscale.h"
+#include "TileConditions/TileCondToolNoiseSample.h"
+#include "TileConditions/ITileBadChanTool.h"
+#include "TileRecUtils/TileBeamInfoProvider.h"
+#include "TileRecUtils/ITileRawChannelTool.h"
+
+// forward declarations
+class TileHWID;
+class TileRawChannel;
+class TileRawChannelContainer;
+class TileRawChannelCollection;
+
+/**
+ @class TileRawChannelNoiseFilter
+ @brief This tool subtracts common-mode noise from all TileRawChannels in one container
+ */
+class TileRawChannelNoiseFilter: public AthAlgTool,
+    virtual public ITileRawChannelTool {
+  public:
+
+    /** AlgTool like constructor */
+    TileRawChannelNoiseFilter(const std::string& type, const std::string& name,
+        const IInterface* parent);
+
+    /** Virtual destructor */
+    virtual ~TileRawChannelNoiseFilter() {
+    }
+    ;
+
+    /** AlgTool InterfaceID */
+    static const InterfaceID& interfaceID();
+
+    /** AlgTool initialize method.*/
+    virtual StatusCode initialize();
+    /** AlgTool finalize method */
+    virtual StatusCode finalize();
+
+    /** proceed the coherent noise subtruction algorithm and correct TileRawChannel amplitudes */
+    virtual StatusCode process(const TileRawChannelContainer *rchCnt);
+
+    /** Callback to handle Data-driven GeoModel initialisation */
+    virtual StatusCode geoInit(IOVSVC_CALLBACK_ARGS);
+
+  private:
+
+    const TileHWID* m_tileHWID; //!< Pointer to TileHWID
+
+    ToolHandle<TileCondToolEmscale> m_tileToolEmscale; //!< main Tile Calibration tool
+    ToolHandle<TileCondToolNoiseSample> m_tileToolNoiseSample; //!< tool which provided noise values
+    ToolHandle<ITileBadChanTool> m_tileBadChanTool;   //!< Tile Bad Channel tool
+    ToolHandle<TileBeamInfoProvider> m_beamInfo; //!< Beam Info tool to get the DQ Status object
+
+    // properties
+    float m_truncationThresholdOnAbsEinSigma;
+    float m_minimumNumberOfTruncatedChannels;
+    bool m_useTwoGaussNoise;
+    bool m_useGapCells;
+};
+
+#endif // TILERAWCHANNELNOISEFILTER_H
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelVerify.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelVerify.h
new file mode 100755
index 0000000000000000000000000000000000000000..9ac482406e6fbcacbc0d7eb74a4e8b1c7f027634
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelVerify.h
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//****************************************************************************
+// Filename : TileRawChannelVerify.h
+// Author   : UC-ATLAS TileCal group
+// Created  : May 2002
+//
+// DESCRIPTION
+//    to verify the rawChannels reconstructed from different route or data 
+//    source, for example, hit->rawchannel, hit->digits->rawchannel, etc.
+//    Every time it can only compare the results from two routes.
+//    Print out the different on the console.
+// 
+// Properties (JobOption Parameters):
+//
+//    TileRawChannelContainer1  string  Name of RawChannel Container created
+//                                      by the first route for read
+//    TileRawChannelContainer2  string  Name of RawChannel Container created
+//                                      by the second route for read
+//
+// BUGS:
+//  
+// History:
+//  
+//  
+//****************************************************************************
+
+#ifndef TILERECUTILS_TILERAWCHANNELVERIFY_H
+#define TILERECUTILS_TILERAWCHANNElVERIFY_H
+
+// Tile includes
+#include "AthenaBaseComps/AthAlgorithm.h"
+
+class TileHWID;
+
+#include <string>
+
+/**
+ @class TileRawChannelVerify
+ @brief This class compares two sets of TileRawChannels
+
+ It is meant to check if both sets of TileRawChannels contain the same information, since the output is just one line confirming that. Optionally the differences are simply dumped to the screen. They TileRawChannels may have been obtained from different input data or from the same data reconstructed by different methods.
+ */
+class TileRawChannelVerify: public AthAlgorithm {
+  public:
+    
+    TileRawChannelVerify(std::string name, ISvcLocator* pSvcLocator); //!< Constructor
+
+    virtual ~TileRawChannelVerify();  //!< Destructor                         
+    
+    StatusCode initialize();  //!< initialize method 
+    StatusCode execute();     //!< execute method 
+    StatusCode finalize();    //!< finalize method
+
+  private:
+
+    const TileHWID* m_tileHWID; //!< Pointer to TileHWID  
+
+    std::string m_rawChannelContainer2; //!< name of one of the TileRawChannelContainer
+    std::string m_rawChannelContainer1; //!< name of one of the TileRawChannelContainer
+
+    double m_precision; //!< maximum difference between the amplitudes of the TileRawChannels to be compared
+
+    bool m_dumpRawChannels; //!< if true=> Differences found in the TileRawChannels are dumped on the screen
+    bool m_sortFlag;         //!< if true=> TileRawChannels are sorted by amplitude
+};
+
+#endif // TILEDIGITIZATION_TILERAWCHANNELVERIFY
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawCorrelatedNoise.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawCorrelatedNoise.h
new file mode 100755
index 0000000000000000000000000000000000000000..a5cdfe88f1f59c5905efcf4af6e62b9bb172a1d7
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawCorrelatedNoise.h
@@ -0,0 +1,62 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCORRELATEDNOISE_H
+#define TILERECUTILS_TILERAWCORRELATEDNOISE_H
+
+/********************************************************************
+ *
+ * NAME:     TileRawCorrelatedNoise 
+ * PACKAGE:  offline/TileCalorimeter/TileRecUtils
+ *
+ * AUTHOR :  F. Veloso
+ * CREATED:  12.07.2010
+ *
+ ********************************************************************/
+
+// Gaudi includes
+#include "GaudiKernel/ToolHandle.h"
+
+// Atlas includes
+#include "AthenaBaseComps/AthAlgorithm.h"
+
+class TileRawChannelBuilder;
+
+class TileRawCorrelatedNoise: public AthAlgorithm {
+
+  public:
+    // constructor
+    TileRawCorrelatedNoise(const std::string& name, ISvcLocator* pSvcLocator);
+    // destructor
+    virtual ~TileRawCorrelatedNoise();
+
+    virtual StatusCode initialize();
+    virtual StatusCode execute();
+    virtual StatusCode finalize();
+
+  private:
+
+    // name of TDS container with input TileDigits
+    std::string m_TileDigitsInputContainer;
+
+    // name of TDS container with output TileDigits
+    std::string m_TileDigitsOutputContainer;
+
+    // RMS threshold
+    float m_nRMS_threshold;
+
+    // file names
+    std::string m_AlphaMatrixFilePrefix;
+    std::string m_Sample3RMSFilePrefix;
+    std::string m_MeanFilePrefix;
+
+    // matrices
+    float AlphaMatrix[4][64][48][48];
+    float MeanSamples[4][64][48][7];
+    float Sample3RMS[4][64][48];
+
+    bool m_useMeanFiles;
+    bool m_pmtOrder;
+};
+#endif
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileTowerBuilderTool.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileTowerBuilderTool.h
new file mode 100755
index 0000000000000000000000000000000000000000..ead3b4980216dc34b1660ac96b45dd0461ab8c61
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileTowerBuilderTool.h
@@ -0,0 +1,37 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILETOWERBUILDERTOOL_H
+#define TILERECUTILS_TILETOWERBUILDERTOOL_H
+/**
+ @class TileTowerBuilderTool
+ @brief Concrete tool for CaloTower building in Tile
+
+ TileTowerBuilderTool fills CaloCells into CaloTowers for the Tile
+ Calorimeters.
+ */
+
+#include "CaloUtils/CaloTowerBuilderTool.h"
+
+#include <string>
+
+class CaloTowerContainer;
+
+class TileTowerBuilderTool: public CaloTowerBuilderTool {
+  public:
+
+    /// AlgTool constructor
+    TileTowerBuilderTool(const std::string& name, const std::string& type,
+        const IInterface* parent);
+    virtual ~TileTowerBuilderTool();
+
+  protected:
+
+    virtual StatusCode initializeTool();
+
+  private:
+    bool m_dumpTowers;
+    bool m_dumpWeightMap;
+};
+#endif // not TILERECUTILS_TILETOWERBUILDERTOOL_H
diff --git a/TileCalorimeter/TileRecUtils/cmt/requirements b/TileCalorimeter/TileRecUtils/cmt/requirements
new file mode 100755
index 0000000000000000000000000000000000000000..021e66802758f7d259aaa7a03b3141024ff8cb6e
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/cmt/requirements
@@ -0,0 +1,41 @@
+package TileRecUtils
+
+author Karl Gellerstedt <Karl.Gellerstedt@cern.ch>
+
+use AtlasPolicy		AtlasPolicy-*
+use AtlasCLHEP		AtlasCLHEP-*		External
+use GaudiInterface	GaudiInterface-*	External
+use DataModel		DataModel-*		Control
+use AthenaKernel	AthenaKernel-*		Control
+use AthenaBaseComps     AthenaBaseComps-*       Control
+use Identifier		Identifier-*		DetectorDescription
+
+use CaloInterface	CaloInterface-*		Calorimeter
+use CaloIdentifier	CaloIdentifier-*	Calorimeter
+use CaloConditions      CaloConditions-*        Calorimeter
+use CaloDetDescr	CaloDetDescr-*		Calorimeter
+use CaloUtils		CaloUtils-*		Calorimeter
+
+use TileIdentifier	TileIdentifier-*	TileCalorimeter
+use TileConditions	TileConditions-*	TileCalorimeter
+use TileDetDescr	TileDetDescr-*		TileCalorimeter
+use TileEvent		TileEvent-*		TileCalorimeter
+use TileCalibBlobObjs	TileCalibBlobObjs-*	TileCalorimeter/TileCalib
+
+private 
+
+use AtlasBoost		AtlasBoost-*		External
+use StoreGate		StoreGate-*		Control
+use GeoModelInterfaces	GeoModelInterfaces-*	DetectorDescription/GeoModel
+use EventContainers	EventContainers-*	Event
+use xAODEventInfo	xAODEventInfo-*		Event/xAOD
+use PathResolver	PathResolver-*		Tools
+use CaloEvent		CaloEvent-*		Calorimeter
+
+end_private
+
+apply_pattern dual_use_library files=Tile*.cxx
+
+apply_pattern declare_joboptions files="*.py"
+
+apply_pattern declare_python_modules files="*.py"
diff --git a/TileCalorimeter/TileRecUtils/doc/mainpage.h b/TileCalorimeter/TileRecUtils/doc/mainpage.h
new file mode 100644
index 0000000000000000000000000000000000000000..7440329d881a22b906832d4b877a281ae884c940
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/doc/mainpage.h
@@ -0,0 +1,64 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+
+This package contains the algorithms and tools used for TileCal reconstruction
+
+@mainpage
+
+@author Alexander Solodkov <Sanya.Solodkov@cern.ch>
+@author Tomas Davidek  <Tomas.Davidek@cern.ch>
+@author Ximo Poveda <Ximo.Poveda@cern.ch>
+
+@section Algos Algorithms
+  
+  The following algorithms are present in this package:
+
+    - TileRawChannelMaker: builds TileRawChannels from TileDigits by making use of one or several of the following tools: TileRawChannelBuilderFlatFilter, TileRawChannelBuilderFitFilter, TileRawChannelBuilderFitFilterCool, TileRawChannelBuilderOptFilter, TileRawChannelBuilderOpt2Filter,TileRawChannelBuilderManyAmps.
+
+  - TileRawChannelVerify: this algorithm compares two sets of  TileRawChannels (obtained from different data and/or reconstructed by different methods) dumping differences in the console. 
+
+
+@section ATools AlgTools
+
+  These are the tools in the TileRecutils package:
+
+  - TileRawChannelBuilderFlatFilter: used by TileDigitsMaker. TileRawChannels are built using the Flat Filtering method, based on the integration of the pulse shapes.
+
+  - TileRawChannelBuilderFitFilter: used by TileDigitsMaker. TileRawChannels are built using the Fit method, based on fit of the pulse samples minimizing the noise contribution. Default mehtod used for testbeam and cosmics reconstruction until 2008.
+
+  - TileRawChannelBuilderFitFilterCool: used by TileDigitsMaker. TileRawChannels are built using the Fit method, based on fit of the pulse samples minimizing the noise contribution. Pulse shapes are loaded from COOL DB.
+
+  - TileRawChannelBuilderOptFilter: used by TileDigitsMaker. TileRawChannels are built using the Optimal Filtering method, based on linear combinations of the samples minimizing the noise contribution. Two version of the method are used: OF1 (which computes amplitude and phase) and OF2 (which computes amplitude, phase and pedestal)
+
+  - TileRawChannelBuilderOpt2Filter: used by TileDigitsMaker. TileRawChannels are built using the Optimal Filtering method, based on linear combinations of the samples minimizing the noise contribution. Two version of the method are used: OF1 (which computes amplitude and phase) and OF2 (which computes amplitude, phase and pedestal). The Opt2Filter makes several iterations, so the time doesn't need to be known in advance. Default method for cosmics since 2008.
+
+  - TileRawChannelBuilderManyAmps: used by TileDigitsMaker. TileRawChannels are built using the method developed by Frank Marritt, based on fitting the digital samples to to a series of pulses (for pileup handling). Default method for full ATLAS reconstruction.
+
+  - TileBeamInfoProvider: provides the contents of cispar fragment, header, status and trigType from the objects in a TileBeamElemContainer
+
+  - TileCellBuilder: creates TileCells from TileRawChannels, which are stored in a container. By default uses Opt2 Optimal Filter, and on-th-fly cell masking.
+
+  - TileCellFakeProb: scales down the energy of TileCells due to simulated failure of drawers
+  
+  - TileCellMaskingTool:  interface to for TileCellBuilder to information on which hardware parts (channel, DMU, digitizer, MB, drawer) to maskout.
+
+  - TileTowerBuilderTool: creates Tower-type cell clusters to be used in jet reconstruction
+
+@ref used_MyPackage
+
+@ref requirements_MyPackage
+*/
+
+/**
+@page used_MyPackage Used Packages
+@htmlinclude used_packages.html
+*/
+
+/**
+@page requirements_MyPackage Requirements
+@include requirements
+
+*/
diff --git a/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter.py b/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc13680e8728bd429603db9934fa28bd5cec2520
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter.py
@@ -0,0 +1,472 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+# Author: J. Poveda (Ximo.Poveda@cern.ch)
+# TileRawChannel creation from TileDigits 
+# TileRawChannelMaker algorithm using
+
+from AthenaCommon.SystemOfUnits import *
+from AthenaCommon.Constants import *
+from AthenaCommon.Logging import logging
+from RecExConfig.Configured import Configured
+import traceback
+
+from RecExConfig.Configured import Configured
+#from TileRecUtils.TileRawChannelMakerBase import TileRawChannelMakerBase 
+
+class TileRawChannelGetter ( Configured)  :
+    """ This getter module creates an instance of the TileRawChannelMaker
+    algorithm to obtain TileRawChannel objects from the TileDigits stored
+    in a TileDigitsContainer.
+    According to the values of the TileRecFlags jobProperties, the
+    corresponding AlgTools are added tothe ToolSvc and associated to
+    TileRawChannelMaker algorithm.
+    """
+    
+    _outputType = "TileRawChannelContainer"
+    # _output = { _outputType : "TileRawChannelFit" }
+
+    def configure(self):
+
+        mlog = logging.getLogger( 'TileRawChannelGetter::configure:' )
+        mlog.info ("entering")        
+        
+        # Instantiation of the C++ algorithm
+        try:        
+            from TileRecUtils.TileRecUtilsConf import TileRawChannelMaker               
+            theTileRawChannelMaker = TileRawChannelMaker("TileRChMaker")
+        except:
+            mlog.error("could not import TileRecUtils.TileRawChannelMaker")
+            print traceback.format_exc()
+            return False
+    
+        self._TileRChMaker = theTileRawChannelMaker;
+
+        # Configure TileInfoLoader
+        from AthenaCommon.AppMgr import ServiceMgr
+        if not hasattr( ServiceMgr, "TileInfoLoader" ):
+            from TileConditions.TileInfoConfigurator import TileInfoConfigurator
+            tileInfoConfigurator = TileInfoConfigurator()
+
+        # register output in objKeyStore
+        from RecExConfig.ObjKeyStore import objKeyStore
+        
+        from AthenaCommon.AppMgr import ToolSvc
+
+        from TileRecUtils.TileRecFlags import jobproperties
+        from TileRecUtils.TileRecUtilsConf import TileBeamInfoProvider
+        theTileBeamInfoProvider = TileBeamInfoProvider()
+        if hasattr( ServiceMgr, "TileDCSSvc" ):
+            theTileBeamInfoProvider.CheckDCS = True
+
+        # true for real data, false for MC - GlobalFlags.DataSource.is_data()
+        # true for nominal ATLAS configuration - GlobalFlags.DetGeo.is_atlas()
+        from AthenaCommon.GlobalFlags import globalflags
+        if globalflags.DataSource()=='data':
+            # apply noise filter for real data (if this option was not set before)
+            if jobproperties.TileRecFlags.noiseFilter() < 0:
+                jobproperties.TileRecFlags.noiseFilter=1
+
+            if jobproperties.TileRecFlags.TileRunType() == 1 :
+                theTileBeamInfoProvider.TileBeamElemContainer="";
+            else:
+                theTileBeamInfoProvider.TileBeamElemContainer="TileBeamElemCnt";
+            if jobproperties.TileRecFlags.readDigits():
+                theTileBeamInfoProvider.TileDigitsContainer="TileDigitsCnt";
+            else:
+                theTileBeamInfoProvider.TileDigitsContainer="";
+            theTileBeamInfoProvider.TileRawChannelContainer="TileRawChannelCnt";
+        else:
+            theTileBeamInfoProvider.TileBeamElemContainer="";
+            theTileBeamInfoProvider.TileDigitsContainer="";
+            theTileBeamInfoProvider.TileRawChannelContainer="";
+
+        # set time window for amplitude correction if it was not set correctly before
+        if jobproperties.TileRecFlags.TimeMaxForAmpCorrection() <= jobproperties.TileRecFlags.TimeMinForAmpCorrection() :
+            from AthenaCommon.DetFlags import DetFlags
+            from AthenaCommon.BeamFlags import jobproperties
+            if globalflags.DataSource()=='data' or jobproperties.Beam.energy > 10000000.0:
+                mlog.info("adjusting min/max time of parabolic correction for %s" % jobproperties.Beam.bunchSpacing)
+                halfBS = jobproperties.Beam.bunchSpacing.get_Value()/2.
+                if halfBS > 25.1:
+                    mlog.info("Bunch spacing is too big, keeping default limits for parabolic correction")
+                else:
+                    jobproperties.TileRecFlags.TimeMinForAmpCorrection = -halfBS
+                    jobproperties.TileRecFlags.TimeMaxForAmpCorrection = halfBS
+            else:
+                mlog.info("DO NOT adjust min/max time of parabolic correction in digitization for %s" % jobproperties.Beam.energy)
+
+        ToolSvc += theTileBeamInfoProvider
+        
+        NoiseFilterTools = []
+        if jobproperties.TileRecFlags.noiseFilter() == 1:
+            from TileRecUtils.TileRecUtilsConf import TileRawChannelNoiseFilter
+            theTileRawChannelNoiseFilter = TileRawChannelNoiseFilter()
+            ToolSvc += theTileRawChannelNoiseFilter
+            NoiseFilterTools += [theTileRawChannelNoiseFilter]
+
+        TileFrameLength = ServiceMgr.TileInfoLoader.NSamples
+        if TileFrameLength!=7:
+            mlog.info("disabling reading of OFC from COOL because Nsamples!=7")
+            jobproperties.TileRecFlags.OfcFromCOOL = False
+
+        jobproperties.TileRecFlags.print_JobProperties('tree&value')
+        
+        # run optimal filter only if readDigits is set
+        if jobproperties.TileRecFlags.readDigits():
+            
+            # fit with several amplitudes 
+            if jobproperties.TileRecFlags.doTileManyAmps():
+      
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderManyAmps
+                    theTileRawChannelBuilderManyAmps= TileRawChannelBuilderManyAmps()
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderManyAmps Quit")
+                    print traceback.format_exc()
+                    return False
+      
+                #TileRawChannelBuilderManyAmps Options:
+                jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelCnt"
+                theTileRawChannelBuilderManyAmps.TileRawChannelContainer = "TileRawChannelCnt"
+                theTileRawChannelBuilderManyAmps.RunType = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderManyAmps.calibrateEnergy = jobproperties.TileRecFlags.calibrateEnergy()
+                theTileRawChannelBuilderManyAmps.correctTime     = jobproperties.TileRecFlags.correctTime()    
+                theTileRawChannelBuilderManyAmps.NoiseFilterTools= NoiseFilterTools
+                 
+                mlog.info(" adding now TileRawChannelBuilderManyAmps to ToolSvc")   
+                ToolSvc += theTileRawChannelBuilderManyAmps
+      
+                theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderManyAmps]
+      
+            # flat filter - sum of 5 samples
+            if jobproperties.TileRecFlags.doTileFlat():
+      
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderFlatFilter
+                    theTileRawChannelBuilderFlatFilter= TileRawChannelBuilderFlatFilter()
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderFlatFilter Quit")
+                    print traceback.format_exc()
+                    return False
+      
+                #TileRawChannelBuilderFlatFilter Options: 
+                jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelFlat"
+                theTileRawChannelBuilderFlatFilter.TileRawChannelContainer = "TileRawChannelFlat"
+                theTileRawChannelBuilderFlatFilter.RunType = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderFlatFilter.calibrateEnergy = jobproperties.TileRecFlags.calibrateEnergy()
+                theTileRawChannelBuilderFlatFilter.correctTime     = jobproperties.TileRecFlags.correctTime()    
+                theTileRawChannelBuilderFlatFilter.NoiseFilterTools= NoiseFilterTools
+                theTileRawChannelBuilderFlatFilter.FrameLength = TileFrameLength
+                theTileRawChannelBuilderFlatFilter.SignalLength = TileFrameLength - 1
+      
+                mlog.info(" adding now TileRawChannelBuilderFlatFilter to ToolSvc")   
+                ToolSvc += theTileRawChannelBuilderFlatFilter
+      
+                theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderFlatFilter]
+      
+            # Fit method
+            if jobproperties.TileRecFlags.doTileFit() or jobproperties.TileRecFlags.doTileOverflowFit():
+          
+                # configure TileRawChannelMaker here
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderFitFilter
+                    theTileRawChannelBuilderFitFilter= TileRawChannelBuilderFitFilter()
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderFitFilter Quit")
+                    print traceback.format_exc()
+                    return False
+                
+                #TileRawChannelBuilderFitFilter Options: 
+                theTileRawChannelBuilderFitFilter.RunType = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderFitFilter.calibrateEnergy = jobproperties.TileRecFlags.calibrateEnergy()
+                theTileRawChannelBuilderFitFilter.correctTime     = jobproperties.TileRecFlags.correctTime()    
+                theTileRawChannelBuilderFitFilter.NoiseFilterTools= NoiseFilterTools
+                theTileRawChannelBuilderFitFilter.FrameLength = TileFrameLength
+                
+                # add the tool to list of tool ( should use ToolHandle eventually)
+                mlog.info(" adding now TileRawChannelBuilderFitFilter to ToolSvc")   
+                ToolSvc += theTileRawChannelBuilderFitFilter
+
+                if jobproperties.TileRecFlags.doTileFit():
+                    jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelFit"
+                    theTileRawChannelBuilderFitFilter.TileRawChannelContainer = "TileRawChannelFit"
+                    theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderFitFilter]    
+                    
+                if jobproperties.TileRecFlags.doTileOverflowFit(): 
+                    theTileRawChannelMaker.FitOverflow = True
+                    theTileRawChannelMaker.TileRawChannelBuilderFitOverflow = ToolSvc.TileRawChannelBuilderFitFilter
+                    mlog.info(" set up TileRawChannelBuilderFitOverflow to TileRawChannelBuilderFitFilter") 
+                
+            # Fit method with reading from COOL
+            if jobproperties.TileRecFlags.doTileFitCool():
+          
+                # configure TileRawChannelMaker here
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderFitFilterCool
+                    theTileRawChannelBuilderFitFilterCool= TileRawChannelBuilderFitFilterCool()
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderFitFilterCool Quit")
+                    print traceback.format_exc()
+                    return False
+                
+                #TileRawChannelBuilderFitFilterCool Options: 
+                jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelFitCool"
+                theTileRawChannelBuilderFitFilterCool.TileRawChannelContainer = "TileRawChannelFitCool"
+                theTileRawChannelBuilderFitFilterCool.RunType = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderFitFilterCool.calibrateEnergy = jobproperties.TileRecFlags.calibrateEnergy()
+                theTileRawChannelBuilderFitFilterCool.correctTime     = jobproperties.TileRecFlags.correctTime()    
+                theTileRawChannelBuilderFitFilterCool.NoiseFilterTools= NoiseFilterTools
+                theTileRawChannelBuilderFitFilterCool.FrameLength = TileFrameLength
+                
+                # add the tool to list of tool ( should use ToolHandle eventually)
+                mlog.info(" adding now TileRawChannelBuilderFitFilterCool to ToolSvc")   
+                ToolSvc += theTileRawChannelBuilderFitFilterCool
+      
+                theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderFitFilterCool]
+                
+            # matched filter 
+            if jobproperties.TileRecFlags.doTileMF():
+      
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderMF
+                    theTileRawChannelBuilderMF= TileRawChannelBuilderMF()
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderMF Quit")
+                    print traceback.format_exc()
+                    return False
+      
+                #TileRawChannelBuilderMF Options:
+                jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelMF"
+                theTileRawChannelBuilderMF.TileRawChannelContainer = "TileRawChannelMF"
+                theTileRawChannelBuilderMF.RunType = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderMF.calibrateEnergy = jobproperties.TileRecFlags.calibrateEnergy()
+                theTileRawChannelBuilderMF.NoiseFilterTools = NoiseFilterTools
+                theTileRawChannelBuilderMF.correctTime = FALSE
+                 
+                mlog.info(" adding now TileRawChannelBuilderMF to ToolSvc")   
+                ToolSvc += theTileRawChannelBuilderMF
+      
+                theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderMF]
+
+            if jobproperties.TileRecFlags.doTileOpt():
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderOptFilter
+                    theTileRawChannelBuilderOptFilter= TileRawChannelBuilderOptFilter()
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderOptFilter Quit")
+                    print traceback.format_exc()
+                    return False
+                
+                #TileRawChannelBuilderOptFilter Options:
+                jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelOpt"
+                theTileRawChannelBuilderOptFilter.TileRawChannelContainer = "TileRawChannelOpt"
+                theTileRawChannelBuilderOptFilter.RunType = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderOptFilter.calibrateEnergy = jobproperties.TileRecFlags.calibrateEnergy()
+                theTileRawChannelBuilderOptFilter.correctTime     = jobproperties.TileRecFlags.correctTime()
+                theTileRawChannelBuilderOptFilter.OF2 = TRUE
+                theTileRawChannelBuilderOptFilter.PedestalMode = 1
+                theTileRawChannelBuilderOptFilter.MaxIterations = 5
+                theTileRawChannelBuilderOptFilter.Minus1Iteration = TRUE
+                theTileRawChannelBuilderOptFilter.AmplitudeCorrection = FALSE; # don't need correction after iterations
+                
+                ServiceMgr.TileInfoLoader.LoadOptFilterWeights=True
+                
+                mlog.info(" adding now TileRawChannelBuilderOptFilter to ToolSvc")   
+                ToolSvc += theTileRawChannelBuilderOptFilter
+      
+                theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderOptFilter]
+      
+            if jobproperties.TileRecFlags.doTileOF1():
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderOpt2Filter
+                    theTileRawChannelBuilderOF1 = TileRawChannelBuilderOpt2Filter("TileRawChannelBuilderOF1")
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderOF1 Quit")
+                    print traceback.format_exc()
+                    return False
+                
+                # setup COOL to get OFCs
+                if jobproperties.TileRecFlags.OfcFromCOOL():
+                    from TileConditions.TileInfoConfigurator import TileInfoConfigurator
+                    tileInfoConfigurator = TileInfoConfigurator()
+                    tileInfoConfigurator.setupCOOLOFC()
+                else:
+                    from TileConditions.TileInfoConfigurator import TileInfoConfigurator
+                    tileInfoConfigurator = TileInfoConfigurator()
+                    tileInfoConfigurator.setupCOOLPHYPULSE()
+                    tileInfoConfigurator.setupCOOLAutoCr()
+                    
+                #TileRawChannelBuilderOF1 Options:
+                jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelOF1"
+                theTileRawChannelBuilderOF1.TileRawChannelContainer = "TileRawChannelOF1"
+                theTileRawChannelBuilderOF1.RunType = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderOF1.calibrateEnergy = jobproperties.TileRecFlags.calibrateEnergy()
+                if jobproperties.TileRecFlags.BestPhaseFromCOOL(): # can't correct time and use best phase at the same time
+                    theTileRawChannelBuilderOF1.correctTime = FALSE
+                else:
+                    theTileRawChannelBuilderOF1.correctTime = jobproperties.TileRecFlags.correctTime()
+                theTileRawChannelBuilderOF1.BestPhase       = jobproperties.TileRecFlags.BestPhaseFromCOOL()
+                theTileRawChannelBuilderOF1.NoiseFilterTools= NoiseFilterTools
+                theTileRawChannelBuilderOF1.OF2 = FALSE
+                theTileRawChannelBuilderOF1.PedestalMode = -1
+                theTileRawChannelBuilderOF1.MaxIterations = 1; # just one iteration
+                theTileRawChannelBuilderOF1.Minus1Iteration = FALSE; # assume that max sample is at t=0
+                theTileRawChannelBuilderOF1.AmplitudeCorrection = jobproperties.TileRecFlags.correctAmplitude()
+                theTileRawChannelBuilderOF1.OfcfromCool = jobproperties.TileRecFlags.OfcFromCOOL()            
+                theTileRawChannelBuilderOF1.AmpMinForAmpCorrection = jobproperties.TileRecFlags.AmpMinForAmpCorrection()
+                if jobproperties.TileRecFlags.TimeMaxForAmpCorrection() > jobproperties.TileRecFlags.TimeMinForAmpCorrection():
+                    theTileRawChannelBuilderOF1.TimeMinForAmpCorrection = jobproperties.TileRecFlags.TimeMinForAmpCorrection()
+                    theTileRawChannelBuilderOF1.TimeMaxForAmpCorrection = jobproperties.TileRecFlags.TimeMaxForAmpCorrection()
+      
+                mlog.info(" adding now TileRawChannelBuilderOF1 to ToolSvc")   
+                ToolSvc += theTileRawChannelBuilderOF1
+      
+                theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderOF1]
+
+            if jobproperties.TileRecFlags.doTileOpt2():
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderOpt2Filter
+                    theTileRawChannelBuilderOpt2Filter= TileRawChannelBuilderOpt2Filter()
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderOpt2Filter Quit")
+                    print traceback.format_exc()
+                    return False
+                
+                # setup COOL to get OFCs
+                if jobproperties.TileRecFlags.OfcFromCOOL():
+                    from TileConditions.TileInfoConfigurator import TileInfoConfigurator
+                    tileInfoConfigurator = TileInfoConfigurator()
+                    tileInfoConfigurator.setupCOOLOFC()
+                else:
+                    from TileConditions.TileInfoConfigurator import TileInfoConfigurator
+                    tileInfoConfigurator = TileInfoConfigurator()
+                    tileInfoConfigurator.setupCOOLPHYPULSE()
+                    tileInfoConfigurator.setupCOOLAutoCr()
+                    
+                #TileRawChannelBuilderOpt2Filter Options:
+                jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelOpt2"
+                theTileRawChannelBuilderOpt2Filter.TileRawChannelContainer = "TileRawChannelOpt2"
+                theTileRawChannelBuilderOpt2Filter.RunType = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderOpt2Filter.calibrateEnergy = jobproperties.TileRecFlags.calibrateEnergy()
+                theTileRawChannelBuilderOpt2Filter.correctTime     = jobproperties.TileRecFlags.correctTime()
+                theTileRawChannelBuilderOpt2Filter.NoiseFilterTools= NoiseFilterTools
+                theTileRawChannelBuilderOpt2Filter.BestPhase       = FALSE; # no point to use best phase with interations
+                theTileRawChannelBuilderOpt2Filter.OF2 = TRUE
+                theTileRawChannelBuilderOpt2Filter.PedestalMode = 1
+                theTileRawChannelBuilderOpt2Filter.MaxIterations = 5
+                theTileRawChannelBuilderOpt2Filter.Minus1Iteration = TRUE
+                theTileRawChannelBuilderOpt2Filter.AmplitudeCorrection = FALSE; # don't need correction after iterations
+                theTileRawChannelBuilderOpt2Filter.OfcfromCool = jobproperties.TileRecFlags.OfcFromCOOL()            
+                #from TileConditions.TileCondToolConf import getTileCondToolPulseShape
+                #ToolSvc.TileCondToolOfc.TileCondToolPulseShape = getTileCondToolPulseShape('COOL','PHY')
+      
+                mlog.info(" adding now TileRawChannelBuilderOpt2Filter to ToolSvc")   
+                ToolSvc += theTileRawChannelBuilderOpt2Filter
+      
+                theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderOpt2Filter]
+      
+            if jobproperties.TileRecFlags.doTileOptATLAS():
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderOpt2Filter
+                    theTileRawChannelBuilderOptATLAS= TileRawChannelBuilderOpt2Filter("TileRawChannelBuilderOptATLAS")
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderOpt2Filter Quit")
+                    print traceback.format_exc()
+                    return False
+                
+                # setup COOL to get OFCs
+                if jobproperties.TileRecFlags.OfcFromCOOL():
+                    from TileConditions.TileInfoConfigurator import TileInfoConfigurator
+                    tileInfoConfigurator = TileInfoConfigurator()
+                    tileInfoConfigurator.setupCOOLOFC()
+                else:
+                    from TileConditions.TileInfoConfigurator import TileInfoConfigurator
+                    tileInfoConfigurator = TileInfoConfigurator()
+                    tileInfoConfigurator.setupCOOLPHYPULSE()
+                    tileInfoConfigurator.setupCOOLAutoCr()
+                    
+                #TileRawChannelBuilderOptATLAS Options:
+                if globalflags.DataSource()=='data': # don't use the name which is used for reco data from DSP
+                    if jobproperties.TileRecFlags.TileRawChannelContainer == "TileRawChannelCnt":
+                        jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelFixed"
+                    theTileRawChannelBuilderOptATLAS.TileRawChannelContainer = "TileRawChannelFixed"
+                else:
+                    jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelCnt"
+                    theTileRawChannelBuilderOptATLAS.TileRawChannelContainer = "TileRawChannelCnt"
+                theTileRawChannelBuilderOptATLAS.RunType         = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderOptATLAS.calibrateEnergy = jobproperties.TileRecFlags.calibrateEnergy()
+                if jobproperties.TileRecFlags.BestPhaseFromCOOL(): # can't correct time and use best phase at the same time
+                    theTileRawChannelBuilderOptATLAS.correctTime = FALSE
+                else:
+                    theTileRawChannelBuilderOptATLAS.correctTime = jobproperties.TileRecFlags.correctTime()
+                theTileRawChannelBuilderOptATLAS.BestPhase       = jobproperties.TileRecFlags.BestPhaseFromCOOL()
+                theTileRawChannelBuilderOptATLAS.NoiseFilterTools= NoiseFilterTools
+                theTileRawChannelBuilderOptATLAS.OF2 = TRUE
+                #theTileRawChannelBuilderOptATLAS.PedestalMode = 1; # not sure if we need this option here
+                theTileRawChannelBuilderOptATLAS.MaxIterations = 1; # just one iteration
+                theTileRawChannelBuilderOptATLAS.Minus1Iteration = FALSE; # assume that max sample is at t=0
+                theTileRawChannelBuilderOptATLAS.AmplitudeCorrection = jobproperties.TileRecFlags.correctAmplitude()
+                theTileRawChannelBuilderOptATLAS.OfcfromCool = jobproperties.TileRecFlags.OfcFromCOOL()            
+                theTileRawChannelBuilderOptATLAS.AmpMinForAmpCorrection = jobproperties.TileRecFlags.AmpMinForAmpCorrection()
+                if jobproperties.TileRecFlags.TimeMaxForAmpCorrection() > jobproperties.TileRecFlags.TimeMinForAmpCorrection():
+                    theTileRawChannelBuilderOptATLAS.TimeMinForAmpCorrection = jobproperties.TileRecFlags.TimeMinForAmpCorrection()
+                    theTileRawChannelBuilderOptATLAS.TimeMaxForAmpCorrection = jobproperties.TileRecFlags.TimeMaxForAmpCorrection()
+                
+                mlog.info(" adding now TileRawChannelBuilderOpt2Filter with name TileRawChannelBuilderOptATLAS to ToolSvc")
+                ToolSvc += theTileRawChannelBuilderOptATLAS
+                
+                theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderOptATLAS]
+            
+
+            jobproperties.TileRecFlags.print_JobProperties('tree&value')
+
+            # now add algorithm to topSequence
+            # this should always come at the end
+
+            mlog.info(" now adding to topSequence")        
+            from AthenaCommon.AlgSequence import AlgSequence
+            topSequence = AlgSequence()
+
+            if jobproperties.TileRecFlags.noiseFilter() == 2:
+                # Instantiation of the C++ algorithm
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawCorrelatedNoise
+                    theTileRawCorrelatedNoise=TileRawCorrelatedNoise("TileRCorreNoise")
+                except:
+                    mlog.error("could not import TileRecUtils.TileRawCorrelatedNoise")
+                    print traceback.format_exc()
+                    return False
+                #theTileRawCorrelatedNoise.UseMeanFiles = FALSE
+                #theTileRawCorrelatedNoise.PMTOrder = TRUE
+                theTileRawChannelMaker.TileDigitsContainer = "NewDigitsContainer"
+                topSequence += theTileRawCorrelatedNoise;
+
+            topSequence += theTileRawChannelMaker;
+
+        else:
+            mlog.info(" Disable all OF methods because readDigits flag set to False ")
+            jobproperties.TileRecFlags.doTileFlat = False
+            jobproperties.TileRecFlags.doTileFit = False
+            jobproperties.TileRecFlags.doTileFitCool = False
+            jobproperties.TileRecFlags.doTileOpt = False
+            jobproperties.TileRecFlags.doTileOpt2 = False
+            jobproperties.TileRecFlags.doTileOptATLAS = False
+            jobproperties.TileRecFlags.doTileManyAmps = False
+            jobproperties.TileRecFlags.doTileMF = False
+            jobproperties.TileRecFlags.doTileOF1 = False
+            jobproperties.TileRecFlags.OfcFromCOOL = False
+            jobproperties.TileRecFlags.print_JobProperties('tree&value')
+
+        return True
+
+    def TileRChMaker(self):
+        return self._TileRChMaker
+   
+##    # would work only if one output object type
+##    def outputKey(self):
+##        return self._output[self._outputType]
+
+##    def outputType(self):
+##        return self._outputType
+
+
diff --git a/TileCalorimeter/TileRecUtils/python/TileRecFlags.py b/TileCalorimeter/TileRecUtils/python/TileRecFlags.py
new file mode 100644
index 0000000000000000000000000000000000000000..c3ea5fc6c47a64ee8c7fb62168fe79462332413c
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/python/TileRecFlags.py
@@ -0,0 +1,245 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+# @file: TileRecFlags.py
+# @purpose: a container of flags for TileCal Reconstruction
+# @author: Ximo Poveda <Ximo.Poveda@cern.ch>
+
+"""  A container of flags for TileCal Reconstruction  
+
+"""
+#
+#
+__author__  = 'Ximo Poveda'
+__version__ = "$Revision: 1.10 $"
+__doc__     = "A container of flags for TileCal Reconstruction"
+
+from AthenaCommon.JobProperties import JobProperty, JobPropertyContainer
+from AthenaCommon.JobProperties import jobproperties
+
+#
+class doTileFlat(JobProperty):
+    """ Use Flat filtering for energy reconstruction
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+    
+#
+class doTileFit(JobProperty):
+    """ Use Fit method for energy reconstruction
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+    
+#
+class doTileFitCool(JobProperty):
+    """ Use FitCool method for energy reconstruction
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+    
+#
+class doTileOpt(JobProperty):
+    """ Use Optimal Filtering for energy reconstruction
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
+#
+class doTileOpt2(JobProperty):
+    """ Use Optimal Filtering (version 2) for energy reconstruction
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
+#
+class doTileOF1(JobProperty):
+    """ Use Optimal Filtering (version 1) for energy reconstruction, using pedestals from the conditions DB
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
+#
+class doTileOptATLAS(JobProperty):
+    """ Use Optimal Filtering (no iterations) for energy reconstruction
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
+#
+class doTileManyAmps(JobProperty):
+    """ Use ManyAmps fit method for energy reconstruction
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
+#
+class doTileMF(JobProperty):
+    """ Use Matched filter method for energy reconstruction
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
+#
+class noiseFilter(JobProperty):
+    """ appply one or another noise suppression algorithm at the level of raw channels
+    """
+    statusOn     = True
+    allowedTypes = ['int']
+    StoredValue  = -1
+
+#
+class TileRawChannelContainer(JobProperty):
+    """ name of the container which will be produced by TileRawChannelMaker
+    """
+    statusOn     = True
+    allowedTypes = ['str']
+    StoredValue  = 'TileRawChannelCnt'
+
+#
+class TileRunType(JobProperty):
+    """ declare type of all events in a run (0=unknown, 1=physics, 2=laser, 4=ped, 8=cis)
+    """
+    statusOn     = True
+    allowedTypes = ['int']
+    StoredValue  = 0
+
+#
+class calibrateEnergy(JobProperty):
+    """ convert reconstructed raw channel amplitude to pCb
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
+#
+class correctTime(JobProperty):
+    """ apply time correction using info from DB (for physics and unknown trigger types)
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
+#
+class correctAmplitude(JobProperty):
+    """ apply parabolic amplitude correction in Optimal filter without iterations
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = True
+
+#
+class AmpMinForAmpCorrection(JobProperty):
+    """ apply parabolic amplitude correction if amplitude is above threshold (in ADC counts)
+    """
+    statusOn     = True
+    allowedTypes = ['float']
+    StoredValue  = 15.
+
+#
+class TimeMinForAmpCorrection(JobProperty):
+    """ apply parabolic amplitude correction if time is above min threshold
+    """
+    statusOn     = True
+    allowedTypes = ['float']
+    StoredValue  = 1000.0
+
+#
+class TimeMaxForAmpCorrection(JobProperty):
+    """ apply parabolic amplitude correction if time is below max threshold
+    """
+    statusOn     = True
+    allowedTypes = ['float']
+    StoredValue  = -999.0
+
+#
+class OfcFromCOOL(JobProperty):
+    """ read OFCs from COOL - no on the fly calculation
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = True
+
+#
+class BestPhaseFromCOOL(JobProperty):
+    """ read best phase from COOL for OF without iterations
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
+#
+class readDigits(JobProperty):
+    """ set to True if digits are available in input file for Optimal Filter
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = True
+
+
+class doTileOverflowFit(JobProperty):
+    """ set to True if apply fit builder for raw channels with overflow
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = True
+    
+class simulateTrips(JobProperty):
+    """ set to True if drawer trips simulation has to be done
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False    
+
+                    
+# Defines the container for the performance monitoring flags  
+class TileRecFlags(JobPropertyContainer):
+    """ The global Tile reconstruction flag/job property container.
+    """
+    pass
+
+
+# add the perfmon flags container to the top container 
+jobproperties.add_Container(TileRecFlags)
+
+
+# We want always the following flags in the container  
+list_jobproperties = [
+    doTileFlat,
+    doTileFit,
+    doTileFitCool,
+    doTileOpt,
+    doTileOF1,
+    doTileOpt2,
+    doTileManyAmps,
+    doTileMF,
+    doTileOptATLAS,
+    TileRawChannelContainer,
+    TileRunType,
+    noiseFilter,
+    calibrateEnergy,
+    correctTime,
+    correctAmplitude,
+    AmpMinForAmpCorrection,
+    TimeMinForAmpCorrection,
+    TimeMaxForAmpCorrection,
+    OfcFromCOOL,
+    BestPhaseFromCOOL,
+    readDigits,
+    doTileOverflowFit,
+    simulateTrips
+    ]
+
+for i in list_jobproperties:
+    jobproperties.TileRecFlags.add_JobProperty(i)
+
+## module clean-up
+del list_jobproperties
diff --git a/TileCalorimeter/TileRecUtils/share/TNF_post.py b/TileCalorimeter/TileRecUtils/share/TNF_post.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c130f095708236659bba497ea49ce3125607b6c
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/share/TNF_post.py
@@ -0,0 +1,11 @@
+from TileRecUtils.TileRecFlags import jobproperties
+
+if jobproperties.TileRecFlags.noiseFilter == 1:
+     tag = "CaloOflNoiseCellnoise-tile_filter"
+else:
+     tag = "CaloOflNoiseCellnoise-tile_nofilter"
+        
+folder  = "/CALO/Ofl/Noise/CellNoise"
+
+from AthenaCommon.AppMgr import ServiceMgr as svcMgr
+svcMgr.IOVDbSvc.overrideTags +=  ["<prefix>"+folder+"</prefix> <tag>"+tag+"</tag>"]
diff --git a/TileCalorimeter/TileRecUtils/share/TNF_pre.py b/TileCalorimeter/TileRecUtils/share/TNF_pre.py
new file mode 100644
index 0000000000000000000000000000000000000000..c45f8179716b657102240fcdff67f2aa9368af0c
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/share/TNF_pre.py
@@ -0,0 +1,5 @@
+from AthenaCommon.GlobalFlags import GlobalFlags
+GlobalFlags.DataSource.set_data()
+
+from TileRecUtils.TileRecFlags import jobproperties
+jobproperties.TileRecFlags.noiseFilter=1
diff --git a/TileCalorimeter/TileRecUtils/share/TileCellBuilder_cosmics_jobOptions.py b/TileCalorimeter/TileRecUtils/share/TileCellBuilder_cosmics_jobOptions.py
new file mode 100755
index 0000000000000000000000000000000000000000..d247736aeeb3d6229d0fcb04337230c334f6dcc8
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/share/TileCellBuilder_cosmics_jobOptions.py
@@ -0,0 +1,39 @@
+#==============================================================
+#
+# Job options file : TileCellBuilder_cosmics_jobOptions.py
+# Choose proper container with TileRawChannels
+# It should be always  TileRawChannelCnt for simulated data
+# and normally TileRawChannelOpt2 (new Opt Filt with iterations) for real data
+#
+#==============================================================
+
+# real data by default
+if not 'doTileCosmicsSim' in dir():
+    doTileCosmicsSim=False
+
+from AthenaCommon.AppMgr import ToolSvc
+
+if not hasattr( ToolSvc, "TileCellBuilder" ):
+    from TileRecUtils.TileRecUtilsConf import TileCellBuilder
+    theTileCellBuilder=TileCellBuilder()
+    ToolSvc += theTileCellBuilder
+
+from TileRecUtils.TileRecFlags import jobproperties
+
+if doTileCosmicsSim:
+    # set correct name of input container in simulation
+    jobproperties.TileRecFlags.TileRawChannelContainer = "TileRawChannelCnt"
+
+ToolSvc.TileCellBuilder.maskBadChannels = True
+ToolSvc.TileCellBuilder.TileRawChannelContainer = jobproperties.TileRecFlags.TileRawChannelContainer()
+
+# Configure TileROD_Decoder
+#if not hasattr( ToolSvc, "TileROD_Decoder" ):
+from TileByteStream.TileByteStreamConf import TileROD_Decoder
+ToolSvc += TileROD_Decoder()
+
+ToolSvc.TileROD_Decoder.calibrateEnergy = True
+ToolSvc.TileROD_Decoder.suppressDummyFragments = True
+ToolSvc.TileROD_Decoder.maskBadDigits = True
+
+print ToolSvc.TileROD_Decoder
diff --git a/TileCalorimeter/TileRecUtils/share/TileCellBuilder_jobOptions.py b/TileCalorimeter/TileRecUtils/share/TileCellBuilder_jobOptions.py
new file mode 100755
index 0000000000000000000000000000000000000000..4119b078b36353b95ad74a55a7abe4651e6ca56c
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/share/TileCellBuilder_jobOptions.py
@@ -0,0 +1,30 @@
+###############################################################
+#
+# Job options file : TileCellBuilder
+# Read TileRawChannels and create TileCells
+# All Det.Descr and Conditions options
+# as well as CaloRec DLLs should be loaded before
+#
+#==============================================================
+#-----------
+# Load DLLs 
+#-----------
+theApp.Dlls += ["TileRecUtils"]
+
+#theApp.Dlls += [ "CaloRec" ]
+
+#--------------------------------
+# Algorithms Properties settings 
+#--------------------------------
+
+#theApp.TopAlg += ["CaloCellMaker"]
+
+CaloCellMaker=Algorithm("CaloCellMaker")
+
+#CaloCellMaker.CaloCellsOutputName = "AllCalo"
+
+CaloCellMaker.CaloCellMakerToolNames += [ "TileCellBuilder/TileCellBuilder" ]
+
+from AthenaCommon.AppMgr import ToolSvc
+ToolSvc.TileCellBuilder.TileRawChannelContainer = "TileRawChannelCnt"
+
diff --git a/TileCalorimeter/TileRecUtils/src/TileBeamInfoProvider.cxx b/TileCalorimeter/TileRecUtils/src/TileBeamInfoProvider.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..e711552d91306320b85c576f9b696c737873b101
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileBeamInfoProvider.cxx
@@ -0,0 +1,940 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// Atlas includes
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/Property.h"
+#include "GaudiKernel/IToolSvc.h"
+#include "GaudiKernel/IIncidentSvc.h"
+#include "StoreGate/StoreGateSvc.h"
+#include "AthenaKernel/errorcheck.h"
+//EventInfo
+#include "xAODEventInfo/EventInfo.h"
+// For the Athena-based random numbers.
+#include "AthenaKernel/IAtRndmGenSvc.h"
+
+//CLHEP includes
+#include <CLHEP/Random/Randomize.h>
+
+
+// Calo include
+#include "CaloIdentifier/CaloLVL1_ID.h" 
+
+// TileCal includes
+#include "TileRecUtils/TileBeamInfoProvider.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileIdentifier/TileTBFrag.h"
+#include "TileIdentifier/TileTTL1Hash.h"
+#include "TileEvent/TileTrigger.h"
+#include "TileEvent/TileTriggerContainer.h"
+#include "TileEvent/TileBeamElemContainer.h"
+#include "TileEvent/TileDigitsContainer.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileLaserObject.h"
+#include "TileConditions/TileDCSSvc.h"
+#include "TileConditions/TileBadChanTool.h"
+#include "TileCalibBlobObjs/TileCalibUtils.h"
+
+#include <iomanip>
+
+// uncomment line below for debug output
+// #define TILECELL_DEBUG 1
+
+static const InterfaceID IID_ITileBeamInfoProvider("TileBeamInfoProvider", 1, 0);
+
+const InterfaceID&
+TileBeamInfoProvider::interfaceID() {
+  return IID_ITileBeamInfoProvider;
+}
+
+/**
+ * Constructor
+ */
+TileBeamInfoProvider::TileBeamInfoProvider(const std::string& type,
+    const std::string& name, const IInterface* parent)
+    : AthAlgTool(type, name, parent)
+    //  , m_TileBeamContainerID("TileBeamElemCnt")
+    //  , m_TileDigitsContainerID("TileDigitsCnt")
+    //  , m_TileRawChContainerID("TileRawChannelCnt")
+    , m_TileBeamContainerID("")
+    , m_TileDigitsContainerID("")
+    , m_TileRawChContainerID("")
+    , m_TileTriggerContainerID("")
+    , m_TileLaserObjectID("")
+    , m_tileDCSSvc("TileDCSSvc", name)
+    , m_rndmSvc ("AtRndmGenSvc", name)
+    , m_tileBadChanTool("TileBadChanTool")
+    , m_tileHWID(0)
+    , m_trigType(0)
+    , m_laserFlag(0xFFFFFFFF)
+    , m_calibMode(0xFFFFFFFF)
+    , m_digiSize(0U)
+    , m_BCID(0U)
+    , m_evt(0U)
+    , m_digitsCnt(0)
+    , m_rcCnt(0)
+    , m_beamElemCnt(0)
+    , m_pHRengine(0)
+    , m_rndmVec(0)
+{
+
+  declareInterface < TileBeamInfoProvider > (this);
+
+  declareProperty("TileBeamElemContainer", m_TileBeamContainerID);
+  declareProperty("TileDigitsContainer", m_TileDigitsContainerID);
+  declareProperty("TileRawChannelContainer", m_TileRawChContainerID);
+  declareProperty("TileTriggerContainer", m_TileTriggerContainerID);
+  declareProperty("TileLaserObject", m_TileLaserObjectID);
+
+  declareProperty("CheckDCS", m_checkDCS = false);
+  declareProperty("SimulateTrips", m_simulateTrips = false, "Simulate drawer trips (default=false)");
+  declareProperty("RndmSvc", m_rndmSvc, "Random Number Service used in TileCondToolTrip");
+
+  m_checkDQ = m_checkDigi = m_checkBeam = false; //until begin of run inibit DQ status
+
+  m_noiseFilterApplied = false;
+  m_incompleteDigits = true;
+}
+
+/**
+ * Destructor
+ */
+TileBeamInfoProvider::~TileBeamInfoProvider() {
+}
+
+/**
+ * Initializer
+ */
+StatusCode TileBeamInfoProvider::initialize() {
+
+  ATH_MSG_INFO("in initialize()");
+
+  // Pass pointer of TileBeamInfoProvider to DQ status to make available some data members
+  m_DQstatus.setTBIP(this);
+
+  // retrieve TileID helpers from det store
+  CHECK(detStore()->retrieve(m_tileHWID, "TileHWID"));
+
+  //=== TileDCSSvc
+  if (m_checkDCS)
+    CHECK(m_tileDCSSvc.retrieve());
+
+  m_evt = 0;
+  m_trigType = 0;
+  m_laserFlag = 0xFFFFFFFF;
+  memset(m_cispar, 0, sizeof(m_cispar));
+
+  m_calibMode = 0xFFFFFFFF;
+
+
+  if (m_simulateTrips) {
+    CHECK( m_tileBadChanTool.retrieve() );
+    CHECK( m_rndmSvc.retrieve());
+    m_pHRengine = m_rndmSvc->GetEngine("Tile_CondToolTrip");
+    m_rndmVec = new double[TileCalibUtils::MAX_DRAWER];
+    ATH_MSG_INFO("Drawer trips will be simulated");
+  }
+
+  if (m_TileBeamContainerID.length() > 0 
+      || m_TileDigitsContainerID.length() > 0
+      || m_TileRawChContainerID.length() > 0
+      || m_simulateTrips) {
+
+    // Listen for begin of event
+    ServiceHandle<IIncidentSvc> incSvc("IncidentSvc", this->name());
+    CHECK(incSvc.retrieve());
+
+    int PRIORITY = 100;
+    incSvc->addListener(this, "BeginRun", 101);
+    incSvc->addListener(this, "BeginEvent", PRIORITY);
+
+    if (msgLvl(MSG::DEBUG)) {
+      msg(MSG::DEBUG) << "TileBeamInfoProvider created, taking info from '"
+                      << m_TileBeamContainerID << "'" << endmsg;
+      msg(MSG::DEBUG) << " from '" << m_TileDigitsContainerID << "'"
+                      << " and from '" << m_TileRawChContainerID << "'"
+                      << ((m_simulateTrips) ? ", and from drawer trips simulation" : "") << endmsg;
+    }
+
+  } else {
+    ATH_MSG_DEBUG("TileBeamInfoProvider created, but BeginOfEvent incident not activated");
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TileBeamInfoProvider::finalize() {
+  ATH_MSG_INFO("Finalizing");
+  if (m_rndmVec) {
+    delete[] m_rndmVec;
+    m_rndmVec = NULL;
+  }
+  return StatusCode::SUCCESS;
+}
+
+/****************************************************************************
+ * This setContainer is called in event overlay jobs from TileDigitsMaker
+ when containers are in non-usual event store
+ ****************************************************************************/
+void TileBeamInfoProvider::setContainers(const TileDigitsContainer * digitsCnt,
+    const TileRawChannelContainer * rcCnt,
+    const TileBeamElemContainer * beamElemCnt) {
+  m_rcCnt = rcCnt;
+  m_digitsCnt = digitsCnt;
+  m_beamElemCnt = beamElemCnt;
+
+  m_checkDQ = (m_rcCnt != NULL);
+  m_checkDigi = (m_digitsCnt != NULL);
+  m_checkBeam = (m_beamElemCnt != NULL);
+  m_noiseFilterApplied = false;
+  m_incompleteDigits = false;
+
+  m_DQstatus.setAllGood();
+  m_DQstatus.setCheckDigi(m_checkDigi);
+
+  if (m_evt == 0) {
+    ATH_MSG_DEBUG("setContainer is called in first event");
+  }
+  ++m_evt;
+}
+
+/****************************************************************************
+ * Implementation of Incident listener
+ ****************************************************************************/
+void TileBeamInfoProvider::handle(const Incident& inc) {
+  if (inc.type() == "BeginRun") {
+    ATH_MSG_DEBUG("Handling BeginRun incident");
+    m_evt = 0;
+    m_DQstatus.setAllGood();
+    m_DQstatus.setFilled(true);
+    return;
+  }
+
+  ATH_MSG_DEBUG("Handling BeginEvent incident");
+
+  if (m_evt == 0) {
+    //enable DQstatus checks
+    m_checkDQ = (m_TileRawChContainerID.length() > 0);
+    m_checkDigi = (m_TileDigitsContainerID.length() > 0);
+    m_checkBeam = (m_TileBeamContainerID.length() > 0);
+    if (msgLvl(MSG::DEBUG)) {
+      msg(MSG::DEBUG) << "Check DQ=" << m_checkDQ << endmsg;
+      msg(MSG::DEBUG) << "Check Digi=" << m_checkDigi << endmsg;
+      msg(MSG::DEBUG) << "Check Beam=" << m_checkBeam << endmsg;
+    }
+    m_DQstatus.setCheckDigi(m_checkDigi);
+  }
+
+  m_rcCnt = NULL;
+  m_digitsCnt = NULL;
+  m_beamElemCnt = NULL;
+  m_noiseFilterApplied = false;
+
+  // retrive all containers from detector store and cache pointers for future use
+
+  if (m_checkBeam) {
+    StatusCode sc = evtStore()->retrieve(m_beamElemCnt, m_TileBeamContainerID);
+    if (sc.isFailure()) {
+      ATH_MSG_ERROR("can't retrieve BeamElem from TDS");
+      if (m_evt == 0) {
+        m_checkBeam = false;
+      }
+    }
+  }
+
+  if (m_checkDigi) {
+    StatusCode sc = evtStore()->retrieve(m_digitsCnt, m_TileDigitsContainerID);
+    if (sc.isFailure()) {
+      ATH_MSG_ERROR("can't retrieve Digits from TDS");
+      if (m_evt == 0) {
+        m_checkDigi = false;
+        m_DQstatus.setCheckDigi(false);
+      }
+    }
+  }
+
+  if (m_checkDQ) {
+    m_DQstatus.setAllGood();
+    StatusCode sc = evtStore()->retrieve(m_rcCnt, m_TileRawChContainerID);
+    if (sc.isFailure()) {
+      ATH_MSG_ERROR("can't retrieve RawChannels '" << m_TileRawChContainerID << "' from TDS");
+      if (m_evt == 0) {
+        m_checkDQ = false;
+        m_DQstatus.setFilled(true);
+      }
+    }
+  }
+
+  m_BCID = 0;
+  m_trigType = 0;
+  m_laserFlag = 0xFFFFFFFF;
+  memset(m_cispar, 0, sizeof(m_cispar));
+  memset(m_laspar, 0, sizeof(m_laspar));
+
+  if (m_beamElemCnt != NULL) {
+
+    TileBeamElemContainer::const_iterator collItr = m_beamElemCnt->begin();
+    TileBeamElemContainer::const_iterator lastColl = m_beamElemCnt->end();
+
+    for (; collItr != lastColl; ++collItr) {
+
+      int frag = (*collItr)->identify();
+
+      if (m_trigType == 0 && (*collItr)->getLvl1Type() != 0) // take it from the ROD header
+        m_trigType = -(*collItr)->getLvl1Type(); // make negative to distinguish from TileCal internal trig types
+
+      switch (frag) {
+
+        case LASE_PTN_FRAG: {
+
+          TileBeamElemCollection::const_iterator beamItr = (*collItr)->begin();
+          TileBeamElemCollection::const_iterator lastBeam = (*collItr)->end();
+
+          if (beamItr != lastBeam) {
+            std::vector < uint32_t > digits = (*beamItr)->get_digits();
+
+            if (digits.size() > 0) {
+              m_laserFlag = digits[0];
+              if (m_laserFlag & 0xFF00)
+                m_trigType = m_laserFlag >> 8;
+            }
+          }
+        }
+          break;
+
+        case DIGI_PAR_FRAG: {
+
+          m_BCID = (*collItr)->getRODBCID();
+
+          TileBeamElemCollection::const_iterator beamItr = (*collItr)->begin();
+          TileBeamElemCollection::const_iterator lastBeam = (*collItr)->end();
+
+          for (; beamItr != lastBeam; ++beamItr) {
+
+            HWIdentifier id = (*beamItr)->adc_HWID();
+            std::vector < uint32_t > digits = (*beamItr)->get_digits();
+            int cha = m_tileHWID->channel(id);
+
+            if (cha < 15) {
+              if (digits.size() > 0) {
+                m_cispar[cha] = digits[0];
+                ATH_MSG_VERBOSE("cispar [" << cha << "] = " << m_cispar[cha]);
+              }
+            } else if (cha == 15) {
+              int siz = 15 + digits.size();
+              if (siz > 110)
+                siz = 110;
+              for (int i = 15; i < siz; ++i) {
+                m_cispar[i] = digits[i - 15];
+                ATH_MSG_VERBOSE("cispar [" << i << "] = " << m_cispar[i]);
+              }
+              switch (m_cispar[16]) {
+
+                case 0x02: {
+                  int aux_ext = m_cispar[17];
+                  m_cispar[17] = (aux_ext & 0x00ff); // dac
+                  m_cispar[18] = (aux_ext >> 8) & 0x00ff; // 00
+                  m_cispar[19] = (aux_ext >> 16) & 0x00ff; // 00
+                  m_cispar[20] = (aux_ext >> 24) & 0x00ff; // small/large cap
+                }
+                  break;
+                case 0x07: {
+                  unsigned int *iCharge = &m_cispar[17];
+                  float *fCharge = reinterpret_cast<float *>(iCharge); // this is for the charge
+                  m_cispar[17] = *fCharge;
+
+                  int aux_ext = m_cispar[18];
+                  m_cispar[18] = (aux_ext & 0x00ff) - 1; // pmt ext cispar starts from 1
+                  m_cispar[19] = (aux_ext >> 8) & 0x00ff; // tower
+                  m_cispar[20] = (aux_ext >> 16) & 0x00ff; // drawer
+                }
+                  break;
+              }
+            }
+          }
+        }
+          break;
+
+        case LASER_OBJ_FRAG: {
+
+          m_BCID = (*collItr)->getRODBCID();
+
+          TileBeamElemCollection::const_iterator beamItr = (*collItr)->begin();
+          TileBeamElemCollection::const_iterator lastBeam = (*collItr)->end();
+
+          if (beamItr != lastBeam) { // one channel is expected, check that it's really there
+
+            //HWIdentifier id=(*beamItr)->adc_HWID();
+            std::vector < uint32_t > digits = (*beamItr)->get_digits();
+            int cha = std::min(32, (int) digits.size());
+
+            while (--cha >= 0) {
+              m_laspar[cha] = digits[cha];
+              ATH_MSG_VERBOSE("laspar [" << cha << "] = " << m_laspar[cha]);
+            }
+          }
+        }
+          break;
+
+        case COIN_TRIG1_FRAG:
+        case COIN_TRIG2_FRAG:
+        case COIN_TRIG3_FRAG:
+        case COIN_TRIG4_FRAG:
+        case COIN_TRIG5_FRAG:
+        case COIN_TRIG6_FRAG:
+        case COIN_TRIG7_FRAG:
+        case COIN_TRIG8_FRAG: {
+          unsigned int board = frag - COIN_TRIG1_FRAG;
+          // make sure that we have enough space
+          if (board >= m_coincTrig.size()) {
+            m_coincTrig.resize(board + 1);
+          }
+
+          TileBeamElemCollection::const_iterator beamItr = (*collItr)->begin();
+          TileBeamElemCollection::const_iterator lastBeam = (*collItr)->end();
+
+          // loop over 4 integer words for one board
+          for (; beamItr != lastBeam; ++beamItr) {
+
+            HWIdentifier id = (*beamItr)->adc_HWID();
+            std::vector < uint32_t > digits = (*beamItr)->get_digits();
+            uint32_t amplitude = (digits.size() > 0) ? digits[0] : 0;
+            int cha = m_tileHWID->channel(id);
+
+            if (cha < 3) {
+              int idx = cha * 32;
+              for (int ibit = 0; ibit < 32; ++ibit) {
+                m_coincTrig[board].trig[idx++] = (amplitude >> ibit) & 1;
+              }
+            } else if (cha == 3) {
+              m_coincTrig[board].amp = amplitude;
+            }
+          }
+        }
+          break;
+
+        default:
+          break;
+      }
+    }
+  }
+  /*
+   for (unsigned int i=0; i<m_coincTrig.size(); ++i) {
+   std::cout << "board "<<i+1<<" amp="<<m_coincTrig[i].amp<<std::endl;
+   for (unsigned int j=0; j<96; ++j) {
+   std::cout << m_coincTrig[i].trig[j];
+   }
+   std::cout << std::endl;
+   }
+   */
+  // we are going to put TileTrigger to StoreGate
+  if (m_TileTriggerContainerID.length() > 0) {
+
+    TileTriggerContainer* pTileTriggerContainer = new TileTriggerContainer();
+
+    int maxboard = m_coincTrig.size();
+    if (maxboard > 0) {
+      Identifier mtid;
+      double mtsum(0.0);
+      std::vector < Identifier > boardtid(maxboard);
+      std::vector<double> boardtsum(maxboard);
+      std::vector < Identifier > backtid(maxboard);
+      std::vector<double> backtsum(maxboard);
+
+      // FIXME:: convert coincTrig to TileTrigger
+
+      TileTrigger * tileTrigger = new TileTrigger(mtid, mtsum, boardtid,
+          boardtsum, backtid, backtsum);
+      pTileTriggerContainer->push_back(tileTrigger);
+    }
+
+    StatusCode sc = evtStore()->record(pTileTriggerContainer,
+        m_TileTriggerContainerID, false);
+    if (sc.isFailure()) {
+      ATH_MSG_ERROR("failed to register the TileTrigger container: "
+                    << m_TileTriggerContainerID << " in StoreGate");
+    }
+  }
+
+  // we are going to put TileLaserObject to StoreGate
+  if (m_TileLaserObjectID.length() > 0) {
+
+    TileLaserObject* pTileLaserObject = new TileLaserObject();
+
+    pTileLaserObject->setBCID(m_BCID);
+
+    // FIXME: a lot of set methods here 
+    // to copy m_laspar to TileLaserObject
+
+    StatusCode sc = evtStore()->record(pTileLaserObject, m_TileLaserObjectID, false);
+    if (sc.isFailure()) {
+      ATH_MSG_ERROR("failed to register the TileLaserObject: "
+                    << m_TileLaserObjectID << " in StoreGate");
+    }
+  }
+
+  if (m_calibMode == 0xFFFFFFFF) {
+
+    m_digiSize = 0;
+
+    if (m_digitsCnt != NULL) {
+      // Get iterator for all TDColl in TDCont
+      TileDigitsContainer::const_iterator itColl = m_digitsCnt->begin();
+      TileDigitsContainer::const_iterator itCollEnd = m_digitsCnt->end();
+
+      bool found = false;
+      bool isCalib = false;
+      bool incomplete = false;
+      for (; itColl != itCollEnd; ++itColl) {
+        if (m_BCID == 0)
+          m_BCID = (*itColl)->getRODBCID();
+        incomplete |= ((*itColl)->size() < 48);
+        if ((*itColl)->size() > 0) {
+          int dsize = (*((*itColl)->begin()))->NtimeSamples();
+          if (4 < dsize && dsize < 15) { // don't use strange fragments
+            isCalib |= (*itColl)->isCalibMode();
+            m_digiSize = dsize;
+            found = true;
+          }
+        }
+      }
+      m_incompleteDigits = incomplete;
+      if (found)
+        m_calibMode = (isCalib) ? 1 : 0;
+      m_DQstatus.setBiGain(isCalib);
+    }
+  }
+
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "BCID = " << m_BCID << endmsg;
+    msg(MSG::VERBOSE) << "digi size = " << m_digiSize << endmsg;
+    msg(MSG::VERBOSE) << "zero-suppressed digi container = " << m_incompleteDigits << endreq;
+
+    if (m_trigType < 0)
+      msg(MSG::VERBOSE) << "trig type = " << m_trigType << " (Level-1 type) " << endmsg;
+    else
+      msg(MSG::VERBOSE) << "trig type = " << m_trigType
+                        << " (TileCal internal type) " << endmsg;
+
+    msg(MSG::VERBOSE) << "calib mode = " << m_calibMode << endmsg;
+  }
+
+  // Set BCID in DQstatus object for  explicit BCID check
+  m_DQstatus.setRODBCID(m_BCID);
+
+  if (m_simulateTrips) m_DQstatus.setFilled(false);
+
+  if (m_evt == 0) {
+    ATH_MSG_DEBUG("End of handling first BeginEvent incident");
+  }
+  ++m_evt;
+}
+
+uint32_t TileBeamInfoProvider::checkCalibMode(void) {
+  // on demand - loop over all collection to check if
+  // we are running in bi-gain mode
+  if (m_digitsCnt != NULL) {
+    bool found = false;
+    bool calibMode = false;
+    bool incomplete = false;
+    // Get iterator for all TDColl in TDCont
+    TileDigitsContainer::const_iterator itColl = m_digitsCnt->begin();
+    TileDigitsContainer::const_iterator itCollEnd = m_digitsCnt->end();
+
+    for (; itColl != itCollEnd; ++itColl) {
+
+      incomplete |= ((*itColl)->size() < 48);
+      if ((*itColl)->size() > 0) {
+        calibMode |= (*itColl)->isCalibMode();
+        found = true;
+      }
+    }
+    m_incompleteDigits = incomplete;
+    if (found)
+      m_calibMode = (calibMode) ? 1 : 0;
+  }
+  return m_calibMode;
+}
+
+// returns DCS HV channel status
+bool TileBeamInfoProvider::isChanDCSgood(int partition, int drawer,
+    int channel) const {
+  if (m_checkDCS) {
+    TileDCSSvc::TileDCSStatus Status = m_tileDCSSvc->getDCSSTATUS(partition,
+        drawer, channel);
+    if (Status > TileDCSSvc::WARNING) {
+      ATH_MSG_DEBUG("Module=" << m_tileDCSSvc->partitionName(partition) << std::setw(2) << std::setfill('0') << drawer + 1
+                    << " channel=" << channel
+                    << " masking becasue of bad DCS status=" << Status);
+      return false;
+    } else {
+      return true;
+    }
+  }
+  return true;
+}
+
+// returns pointer to TileDQstatus and calls function to fill the data members if it's not filled yet
+const TileDQstatus * TileBeamInfoProvider::getDQstatus() {
+
+  // return immediately if it's filled already
+  if (m_DQstatus.isFilled())
+    return &m_DQstatus;
+
+  //Fill BCID from the EventInfo, if available
+  const DataHandle<xAOD::EventInfo> eventInfo(0);
+  StatusCode sc = evtStore()->retrieve(eventInfo);
+  if (sc.isSuccess()) {
+    m_DQstatus.setRODBCID(eventInfo->bcid());
+  }
+
+  if (m_rcCnt != NULL) {
+
+    if (m_digitsCnt != NULL) {
+
+      TileDigitsContainer::const_iterator collItr = m_digitsCnt->begin();
+      TileDigitsContainer::const_iterator lastColl = m_digitsCnt->end();
+
+      for (; collItr != lastColl; collItr++) {// Loop over TileModules
+        int frag = (*collItr)->identify();
+        int partition = (frag >> 8);
+        int drawer = (frag & 0x3F);
+
+        std::vector < uint32_t > data = (*collItr)->getFragChipHeaderWords();
+        unsigned int dataSize = std::min(16u, (unsigned int) data.size());
+        for (unsigned int dmu = 0; dmu < dataSize; ++dmu) {
+          if (data[dmu] == 0xFFFFFFFF)
+            m_DQstatus.setEmptyEvent(partition, drawer, dmu, 0, 1);
+        }
+
+        data = (*collItr)->getFragChipHeaderWordsHigh();
+        dataSize = std::min(16u, (unsigned int) data.size());
+        for (unsigned int dmu = 0; dmu < dataSize; ++dmu) {
+          if (data[dmu] == 0xFFFFFFFF)
+            m_DQstatus.setEmptyEvent(partition, drawer, dmu, 1, 1);
+        }
+      }
+    } // end if m_checkDigi
+
+    TileFragHash::TYPE RChType = m_rcCnt->get_type();
+    if (RChType != TileFragHash::OptFilterDsp
+        && RChType != TileFragHash::OptFilterDspCompressed) {
+      ATH_MSG_INFO("RawChannelContaier didn't come from BS - don't check DQ flags");
+      ATH_MSG_INFO("RChType = " << RChType);
+      m_DQstatus.setAllGood();
+      m_checkDQ = false;
+    } else {
+      TileRawChannelContainer::const_iterator itColl = m_rcCnt->begin();
+      TileRawChannelContainer::const_iterator itCollEnd = m_rcCnt->end();
+
+      // Go through all TileRawChannelCollections
+      for (; itColl != itCollEnd; ++itColl) {
+        const TileRawChannelCollection *coll = (*itColl);
+        ATH_MSG_VERBOSE("RCh collection 0x" << MSG::hex
+                       << coll->identify() << MSG::dec
+                       << " size=" << coll->size());
+
+        m_DQstatus.fillArrays(coll, 0);
+        m_DQstatus.fillArrays(coll, 1);
+      }
+      if (m_evt == 1 && m_DQstatus.nonZeroCounter() == 0) {
+        ATH_MSG_INFO("all DQ elements are empty - don't check DQ flags");
+        m_DQstatus.setAllGood();
+        m_checkDQ = false;
+      } else {
+        ATH_MSG_DEBUG("BiGain mode: " << ((m_DQstatus.isBiGain()) ? "true" : "false"));
+      }
+    }
+  }
+
+
+  if (m_simulateTrips) {
+    for (unsigned int partition = 1; partition < TileCalibUtils::MAX_ROS; ++partition) {
+      CLHEP::RandFlat::shootArray(m_pHRengine, TileCalibUtils::MAX_DRAWER, m_rndmVec);
+      std::vector<float> trips = m_tileBadChanTool->getTripsProbabilities(partition);
+      m_DQstatus.fillTrips(partition, trips, m_rndmVec);
+    }
+  }
+
+  m_DQstatus.setFilled(true);
+  return &m_DQstatus;
+}
+
+//-----------------------------------------------------------------
+// Implementation of TileDQstatus class
+//-----------------------------------------------------------------
+
+const int TileDQstatus::ch2dmuLB[48] = { 0, 0, 0, 0, 0, 0, 
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+  0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+  0, 0, 0, 0 };
+
+const int TileDQstatus::ch2dmuEB[48] = { 0, 0, 0, 0, 0, 0, 
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 2, 
+  2, 2, 2, 2, 2, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2,
+  2, 2, 2, 2 };
+
+const int TileDQstatus::ch2dmuEBspecial[48] = { 2, 2, 2, 1, 
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+  0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+  2, 2, 2, 2, 2, 2 };  
+
+// Constructor
+TileDQstatus::TileDQstatus():m_TBIP(0) {
+  setAllGood();
+  m_isBiGain = false;
+  m_checkDigi = false;
+  m_BCID = 0;
+}
+
+void TileDQstatus::setAllGood() {
+  // Intitalize all arrays to no errors
+  m_isFilled = false;
+  //m_isBiGain = false; //BAD!
+  m_counter = 0;
+  memset(m_EmptyEventArray, 0, sizeof(m_EmptyEventArray));
+  memset(m_GlobalCRCErrArray, 0, sizeof(m_GlobalCRCErrArray));
+  memset(m_FE_DMUmaskArray, -1, sizeof(m_FE_DMUmaskArray));
+  memset(m_ROD_DMUmaskArray, -1, sizeof(m_ROD_DMUmaskArray));
+  memset(m_BCIDErrArray, 0, sizeof(m_BCIDErrArray));
+  memset(m_BCIDErrArrayDetail, 0, sizeof(m_BCIDErrArrayDetail));
+  memset(m_HeaderFormatErrArray, 0, sizeof(m_HeaderFormatErrArray));
+  memset(m_HeaderParityErrArray, 0, sizeof(m_HeaderParityErrArray));
+  memset(m_SampleFormatErrArray, 0, sizeof(m_SampleFormatErrArray));
+  memset(m_SampleParityErrArray, 0, sizeof(m_SampleParityErrArray));
+  memset(m_MemoryParityErrArray, 0, sizeof(m_MemoryParityErrArray));
+  memset(m_SingleStrobeErrArray, 0, sizeof(m_SingleStrobeErrArray));
+  memset(m_DoubleStrobeErrArray, 0, sizeof(m_DoubleStrobeErrArray));
+}
+
+// Destructor
+TileDQstatus::~TileDQstatus() {
+}
+
+// Fucntion to fill error arrays from DQ fragment stores in DSP RawChannelContainer
+// If monogain run, both gains contain the same results
+void TileDQstatus::fillArrays(const TileRawChannelCollection *coll, int gain) {
+
+  int frag = coll->identify();
+  int partition = (frag >> 8); // 0=AUX,1=LBA,2=LBC,3=EBA,4=EBC
+  int drawer = (frag & 0x3F);  // 0-63
+  bool eb = (frag > 0x2ff);    // true if ext.barrel
+  bool ebsp = (frag == 0x30e || frag == 0x411);// EBA15 or EBC18
+
+  m_isBiGain |= (coll->size() > 48);
+  // attention! it's assignment below, i.e. only single "=", not "=="
+  // LF: ... which is something very dangerous. Does it provide any speed advantage?
+  if ((m_GlobalCRCErrArray[partition][drawer][gain] = coll->getFragGlobalCRC()))    ++m_counter;
+  if ((m_BCIDErrArray[partition][drawer][gain] = coll->getFragBCID()))              ++m_counter;
+  if ((m_MemoryParityErrArray[partition][drawer][gain] = coll->getFragMemoryPar())) ++m_counter;
+  if ((m_SingleStrobeErrArray[partition][drawer][gain] = coll->getFragSstrobe()))   ++m_counter;
+  if ((m_DoubleStrobeErrArray[partition][drawer][gain] = coll->getFragDstrobe()))   ++m_counter;
+  if ((m_HeaderFormatErrArray[partition][drawer][gain] = coll->getFragHeaderBit())) ++m_counter;
+  if ((m_HeaderParityErrArray[partition][drawer][gain] = coll->getFragHeaderPar())) ++m_counter;
+  if ((m_SampleFormatErrArray[partition][drawer][gain] = coll->getFragSampleBit())) ++m_counter;
+  if ((m_SampleParityErrArray[partition][drawer][gain] = coll->getFragSamplePar())) ++m_counter;
+  if ((m_ROD_DMUmaskArray[partition][drawer][gain] = coll->getFragRODChipMask()))   ++m_counter;
+// special treatment of FE DMU mask - take into account that some DMUs in Ext.barrel do not exist
+  if(uint32_t FE_DMUmask = coll->getFragFEChipMask()) {
+    if (eb) { // EBA or EBC
+      if (ebsp)  FE_DMUmask <<= 1; // shift by one DMU in EBA15 EBC18
+      FE_DMUmask = (FE_DMUmask & 0xFF) | ((FE_DMUmask & 0xF00) << 2); // shift upper half by two DMUs
+    }
+     m_FE_DMUmaskArray[partition][drawer][gain] = FE_DMUmask;
+    ++m_counter;
+  }
+
+  unsigned short BCIDerr =
+      (unsigned short) m_BCIDErrArray[partition][drawer][gain];
+  if (BCIDerr & 0x2) { // DMU1 (second DMU) is bad - can not trust others 
+    m_BCIDErrArray[partition][drawer][gain] = -1;
+  } else {
+    // additional check if DQ frag BCID is the same as event BCID 
+    uint32_t DSPBCID = coll->getFragDSPBCID();
+    if (DSPBCID != 0xDEAD && DSPBCID != m_BCID) { // DSP BCID doesn't match! all wrong
+      m_BCIDErrArray[partition][drawer][gain] = -1; // I preferred 0xFFFF; but variable is decleared as signed!
+      ++m_counter;
+    } else {
+      int n_badMB = 0;
+      if (eb) { // do not count non-existing DMUs in EB
+        if (ebsp) {
+          BCIDerr &= 0x3cfe;
+        } else {
+          BCIDerr &= 0x3cff;
+        }
+      }
+      while (BCIDerr) {
+        if (BCIDerr & 0xF)
+          ++n_badMB;
+        BCIDerr >>= 4;
+      }
+      if (n_badMB == 4) { // BCID errors in all 4 motherboards - assume that all DMUs are bad
+        m_BCIDErrArray[partition][drawer][gain] = -1;
+#ifdef TILECELL_DEBUG
+        std::cout << "masking whole drawer " << drawer << " in partition " << partition << " because all 4 MB have BCID errors"
+                  << std::endl;
+#endif
+      }
+    }
+  }
+
+  if ((m_BCIDErrArray[partition][drawer][gain] & 0x2) && m_checkDigi)
+      fillBCIDErrDetail(frag, gain);
+  
+  if (m_HeaderFormatErrArray[partition][drawer][gain]
+      || m_HeaderParityErrArray[partition][drawer][gain]
+      || m_SampleFormatErrArray[partition][drawer][gain]
+      || m_SampleParityErrArray[partition][drawer][gain]) {
+    // can not trust FE mask, assume that FE is good
+    m_FE_DMUmaskArray[partition][drawer][gain] = -1;
+  }
+
+#ifdef TILECELL_DEBUG
+  std::cout << std::hex 
+            << " " << coll->getFragGlobalCRC()
+            << " " << coll->getFragFEChipMask()
+            << " " << coll->getFragRODChipMask()
+            << " " << coll->getFragDSPBCID()
+            << " " << coll->getFragBCID()
+            << " " << coll->getFragMemoryPar()
+            << " " << coll->getFragSstrobe()
+            << " " << coll->getFragDstrobe()
+            << " " << coll->getFragHeaderBit()
+            << " " << coll->getFragHeaderPar()
+            << " " << coll->getFragSampleBit()
+            << " " << coll->getFragSamplePar()
+            << " counter is " << std::dec << m_counter << std::endl;
+#endif
+  
+}
+
+
+//  Returns AND of all error check reults
+bool TileDQstatus::isAdcDQgood(int partition, int drawer, int ch, int gain) const {
+
+  int dmu = ch/3;
+
+#ifdef TILECELL_DEBUG
+
+  int errorArray[11];
+  errorArray[0] = checkGlobalCRCErr    (partition,drawer,gain);
+  errorArray[1] = checkROD_CRCErr      (partition,drawer,dmu,gain);
+  errorArray[2] = (m_isBiGain) ? 0 : checkFE_CRCErr(partition,drawer,dmu,gain);  // Skip FE CRC for bigain runs
+  errorArray[3] = checkBCIDErr         (partition,drawer,dmu,gain);
+  errorArray[4] = checkHeaderFormatErr (partition,drawer,dmu,gain);
+  errorArray[5] = checkHeaderParityErr (partition,drawer,dmu,gain);
+  errorArray[6] = checkSampleFormatErr (partition,drawer,dmu,gain);
+  errorArray[7] = checkSampleParityErr (partition,drawer,dmu,gain);
+  errorArray[8] = checkMemoryParityErr (partition,drawer,dmu,gain);
+  errorArray[9] = checkSingleStrobeErr (partition,drawer,dmu,gain);
+  errorArray[10]= checkDoubleStrobeErr (partition,drawer,dmu,gain);
+
+  int nError=0;
+  for(int i=0;i<9;++i){ // exclude Strobe errors from decision
+    nError += errorArray[i];
+  }
+  std::cout << std::dec <<"Part: " << partition << " Drawer: " << drawer+1 << " DMU: " << dmu << " ch: " << ch << std::endl;
+  std::cout << "IsBigain "        << m_isBiGain << std::endl;
+  std::cout << "EmptyEvent (LG) " << m_EmptyEventArray[partition][drawer][dmu][0]  << std::endl;
+  std::cout << "EmptyEvent (HG) " << m_EmptyEventArray[partition][drawer][dmu][1]  << std::endl;
+  std::cout << "GlobalCRCErr "    << errorArray[0] << std::endl;
+  std::cout << "ROD_CRCmask (LG) " << std::hex <<  m_ROD_DMUmaskArray[partition][drawer][0] << std::endl;
+  std::cout << "ROD_CRCmask (HG) " << std::hex <<  m_ROD_DMUmaskArray[partition][drawer][1] << std::endl;
+  std::cout << "ROD_CRCErr "      << std::dec << errorArray[1] << std::endl;
+  std::cout << "FE_CRCmask (LG) "  << std::hex << m_FE_DMUmaskArray[partition][drawer][0] << std::endl;
+  std::cout << "FE_CRCmask (HG) "  << std::hex << m_FE_DMUmaskArray[partition][drawer][1] << std::endl;
+  std::cout << "FE_CRCErr "       << std::dec << errorArray[2] << std::endl;
+  std::cout << "BCIDErr "         << std::dec << errorArray[3] << std::endl;
+  std::cout << "HeaderFormatErr " << std::dec << errorArray[4] << std::endl;
+  std::cout << "HeaderParityErr " << std::dec << errorArray[5] << std::endl;
+  std::cout << "SampleFormatErr " << std::dec << errorArray[6] << std::endl;
+  std::cout << "SampleParityErr " << std::dec << errorArray[7] << std::endl;
+  std::cout << "MemoryParityErr " << std::dec << errorArray[8] << std::endl;
+  std::cout << "SingleStrobeErr " << std::dec << errorArray[9] << std::endl;
+  std::cout << "DoubleStrobeErr " << std::dec << errorArray[10] << std::endl;
+  std::cout << "Total number of errors: " << std::dec << nError << std::endl;
+
+#endif
+
+  if (checkGlobalCRCErr(partition, drawer, gain)
+      || checkROD_CRCErr(partition, drawer, dmu, gain)
+      || ((m_isBiGain) ? 0 : checkFE_CRCErr(partition, drawer, dmu, gain)) // Skip FE CRC for bigain runs
+      || checkBCIDErr(partition, drawer, dmu, gain)
+      || checkHeaderFormatErr(partition, drawer, dmu, gain)
+      || checkHeaderParityErr(partition, drawer, dmu, gain)
+      || checkSampleFormatErr(partition, drawer, dmu, gain)
+      || checkSampleParityErr(partition, drawer, dmu, gain)
+      || checkMemoryParityErr(partition, drawer, dmu, gain)) {
+    return false;
+  }
+
+  return true;
+}
+
+// Returns AND of error checks for both adc's for a single channel
+bool TileDQstatus::isChanDQgood(int partition, int drawer, int ch) const {
+  bool isGood = isAdcDQgood(partition, drawer, ch, 0);
+
+  if (m_isBiGain)
+    isGood &= isAdcDQgood(partition, drawer, ch, 1);
+
+  return isGood;
+}
+
+void TileDQstatus::fillBCIDErrDetail(int frag, int gain) {
+
+  int partition = (frag >> 8);
+  int drawer = (frag & 0x3F);
+  m_BCIDErrArrayDetail[partition][drawer][gain] =
+      m_BCIDErrArray[partition][drawer][gain]; //initialize
+  const TileDigitsContainer* digitsCnt = m_TBIP->m_digitsCnt;
+  if (digitsCnt == NULL)
+    // by default a conservative return value of 1 (channel Mask)
+    m_BCIDErrArrayDetail[partition][drawer][gain] = short(-1);  
+  else {
+    TileDigitsContainer::const_iterator collItr = digitsCnt->begin();
+    TileDigitsContainer::const_iterator lastColl = digitsCnt->end();
+
+    for (; collItr != lastColl; collItr++) { // Loop over TileModules
+      if ((*collItr)->identify() != frag)
+        continue;
+
+      std::vector < uint32_t > data;
+      if ( isBiGain() && gain ) //high Gain in bigain run
+        data = (*collItr)->getFragChipHeaderWordsHigh();
+      else
+        data = (*collItr)->getFragChipHeaderWords();
+
+      unsigned int dataSize = std::min(16u, (unsigned int) data.size());
+      short bcidCheck = 0x0;
+      uint32_t rodbcid = (*collItr)->getRODBCID();
+      for (unsigned int dmu = 0; dmu < dataSize; ++dmu) {
+        bcidCheck |= ((data[dmu] & 0xFFF) != rodbcid) << dmu;
+#ifdef TILECELL_DEBUG
+        std::cout << "Part: " << partition << " Drawer: " << drawer << " DMU: " << dmu << (gain?"HG":"LG") 
+                  << " DMU BCID: " << (data[dmu] & 0xFFF) << " ROD BCID: " << rodbcid << std::endl;
+#endif
+      }
+      if (dataSize > 0)
+        m_BCIDErrArrayDetail[partition][drawer][gain] = bcidCheck;
+      break; // break after the digit is found
+    }
+  }
+}
+
+
+void TileDQstatus::fillTrips(unsigned int partition, const std::vector<float>& trips, double* rndmVec) {
+  for (unsigned int drawer = 0; drawer < TileCalibUtils::MAX_DRAWER; ++drawer) {
+    if (trips[drawer] > rndmVec[drawer]) {
+      if (m_TBIP->msgLvl(MSG::DEBUG)) {
+        m_TBIP->msg(MSG::DEBUG) << "The drawer has been tripped (simulation): "
+                                << TileCalibUtils::getDrawerString(partition, drawer) << endmsg;
+      }
+      m_GlobalCRCErrArray[partition][drawer][0] = -1;
+      m_GlobalCRCErrArray[partition][drawer][1] = -1;
+      m_counter += 2;
+    }
+  }
+}
+
diff --git a/TileCalorimeter/TileRecUtils/src/TileCellBuilder.cxx b/TileCalorimeter/TileRecUtils/src/TileCellBuilder.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..6ccb94943a0096484e1f146bb59d78fa5222dc24
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileCellBuilder.cxx
@@ -0,0 +1,997 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+// small hack to enable datapool usage
+#define private public
+#define protected public
+#include "TileEvent/TileCell.h"
+#undef private
+#undef protected
+ 
+// Gaudi includes
+#include "GaudiKernel/Bootstrap.h"
+
+// Atlas includes
+#include "GeoModelInterfaces/IGeoModelSvc.h"
+#include "DataModel/DataPool.h"
+// access all RawChannels inside container
+#include "EventContainers/SelectAllObject.h" 
+#include "xAODEventInfo/EventInfo.h"
+#include "AthenaKernel/errorcheck.h"
+
+// Calo includes
+#include "CaloIdentifier/CaloCell_ID.h"
+#include "CaloEvent/CaloCellContainer.h"
+#include "CaloDetDescr/CaloDetDescrElement.h"
+#include "CaloConditions/CaloAffectedRegionInfoVec.h"
+#include "Identifier/IdentifierHash.h"
+
+// Tile includes
+#include "TileRecUtils/TileCellBuilder.h"
+#include "CaloIdentifier/TileID.h"
+#include "CaloIdentifier/TileTBID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "CaloDetDescr/MbtsDetDescrManager.h"
+#include "TileDetDescr/TileDetDescrManager.h"
+#include "TileConditions/ITileBadChanTool.h"
+#include "TileEvent/TileRawChannel.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileCellCollection.h"
+#include "TileRecUtils/ITileRawChannelTool.h"
+
+using xAOD::EventInfo;	  	
+using CLHEP::MeV;
+ 
+// uncomment line below for debug output
+// #define ALLOW_DEBUG_COUT 1
+
+static const InterfaceID IID_ITileCellBuilder("TileCellBuilder", 1, 0); 	 
+
+const InterfaceID& TileCellBuilder::interfaceID( ) {
+  return IID_ITileCellBuilder; 	 
+}
+
+//Constructor
+TileCellBuilder::TileCellBuilder(const std::string& type, const std::string& name,
+    const IInterface* parent)
+  : AthAlgTool(type, name, parent)
+  , m_rawChannelContainer("TileRawChannelCnt")
+  , m_MBTSContainer("MBTSContainer")
+  , m_eneForTimeCut(35. * MeV) // keep time only for cells above 70 MeV (more than 35 MeV in at least one PMT to be precise)
+  , m_eneForTimeCutMBTS(0.03675) // the same cut for MBTS, but in pC, corresponds to 3 ADC counts or 35 MeV
+  , m_qualityCut(254) // cut on overflow in quality (if quality is 255 - assume that channel is bad)
+  , m_eThreshold(-100000.)
+  , m_maxTimeDiff(100000.)
+  , m_maxTime (100000.)
+  , m_minTime(-100000.)
+  , m_maxChi2(100000.)
+  , m_minChi2(-100000.)
+  , m_thresholdNotSet(true)
+  , m_fullSizeCont(true)
+  , m_maskBadChannels(true)
+  , m_fakeCrackCells(false)
+  , m_tileID(0)
+  , m_tileTBID(0)
+  , m_tileHWID(0)
+  , theDQstatus(0)
+  , m_tileBadChanTool("TileBadChanTool")
+  , m_tileToolEmscale("TileCondToolEmscale")
+  , m_tileToolTiming("TileCondToolTiming")
+  , m_beamInfo("TileBeamInfoProvider/TileBeamInfoProvider")
+  , m_noiseFilterTools() // "TileRawChannelNoiseFilter/TileRawChannelNoiseFilter")
+  , m_tileMgr(0)
+  , m_mbtsMgr(0)
+  , m_MBTSCells(0)
+  , m_RChType(TileFragHash::Default)
+  , m_RChUnit(TileRawChannelUnit::ADCcounts)
+  , m_maxTimeCorr(75.0)
+{
+  declareInterface<ICaloCellMakerTool>( this );
+  declareInterface<TileCellBuilder>( this );
+
+  memset(m_drawerEvtStatus, 0, sizeof(m_drawerEvtStatus));
+  memset(m_drawerRunStatus, 0, sizeof(m_drawerRunStatus));
+  memset(m_eventErrorCounter, 0, sizeof(m_eventErrorCounter));
+
+  // never set energy to zero, but set it to some small number
+  // this will help TopoCluster to assign proper weight to the cell if needed
+  m_zeroEnergy = 0.5 * MeV; // half a MeV in both PMTs i.e. one MeV in a cell
+
+  //!< normal channel energy threshold for masking
+  m_minEneChan[0] = -5000. * MeV;
+  //!< gap channel energy threshold for masking
+  m_minEneChan[1] = -10000. * MeV;
+  //!< MBTS channel energy threshold for masking (not used currently(
+  m_minEneChan[2] = -999999. * MeV;
+
+  declareProperty("TileRawChannelContainer", m_rawChannelContainer);  // Raw channel to convert
+  declareProperty("MBTSContainer",           m_MBTSContainer);        // Where to put MBTS cells
+  declareProperty("TileDSPRawChannelContainer", m_dspRawChannelContainer = "TileRawChannelCnt");
+  declareProperty("TileBadChanTool"        , m_tileBadChanTool);
+  declareProperty("TileCondToolEmscale"    , m_tileToolEmscale);
+  declareProperty("TileCondToolTiming"     , m_tileToolTiming);
+  declareProperty("BeamInfo", m_beamInfo);
+  declareProperty("NoiseFilterTools", m_noiseFilterTools);
+
+  declareProperty( "MinEnergyChan", m_minEneChan[0]);
+  declareProperty( "MinEnergyGap",  m_minEneChan[1]);
+  declareProperty( "MinEnergyMBTS", m_minEneChan[2]);
+
+  // Energy threshold in MeV that the Cell must exceed to be considered:
+  declareProperty("EThreshold",m_eThreshold);
+
+  // Maximum difference between times of two PMTs in cell:
+  declareProperty("MaxTimeDiff", m_maxTimeDiff);
+
+  // Maximum and minimum time for a cell to be included:
+  declareProperty("MaxTime", m_maxTime);
+  declareProperty("MinTime", m_minTime);
+
+  // Maximum and Minimum fit quality for cell to be considered:
+  declareProperty("MaxChi2", m_maxChi2);
+  declareProperty("MinChi2", m_minChi2);
+
+  declareProperty("fullSizeCont", m_fullSizeCont);
+
+  // put zero energy in bad channels and recover from single-channel failure using second PMT is a cell
+  declareProperty("maskBadChannels", m_maskBadChannels);
+
+  // create fake E3/E4 crack scintillators with zero energy when they do not exist 
+  declareProperty("fakeCrackCells", m_fakeCrackCells);
+
+  // PMT energy will be set to this value if channel is bad
+  declareProperty("BadChannelZeroEnergy", m_zeroEnergy);
+  // PMT with energy above cut will preserve time info in ESD
+  declareProperty("EneForTimeCut", m_eneForTimeCut);
+  declareProperty("EneForTimeCutMBTS", m_eneForTimeCutMBTS);
+  // PMT with quality greater than this cut will be masked
+  declareProperty("QualityCut", m_qualityCut);
+
+  // apply time correction taking numbers from CondDB (if not yet done in OF)
+  declareProperty("correctTime", m_correctTime = false);
+
+  // apply parabolic amplitude correction (if not yet done in OF without iterations)
+  declareProperty("correctAmplitude", m_correctAmplitude = false);
+
+  // merge DSP results with offline reco results
+  declareProperty("mergeChannels", m_mergeChannels = false);
+
+  // thresholds for parabolic amplitude correction 
+  declareProperty("AmpMinForAmpCorrection", m_ampMinThresh = 15.0);
+  declareProperty("TimeMinForAmpCorrection", m_timeMinThresh = -25.0);
+  declareProperty("TimeMaxForAmpCorrection", m_timeMaxThresh = 25.0);
+
+  declareProperty("SkipGain", m_skipGain = -1); // never skip any gain by default
+}
+
+/**
+ * Destructor
+ */
+TileCellBuilder::~TileCellBuilder(){ 
+}
+
+/**
+ * Initializer
+ */
+StatusCode TileCellBuilder::initialize() {
+  
+  const IGeoModelSvc *geoModel = 0;
+  CHECK( service("GeoModelSvc", geoModel) );
+  
+  // dummy parameters for the callback:
+  int dummyInt = 0;
+  std::list<std::string> dummyList;
+  
+  if (geoModel->geoInitialized()) {
+    return geoInit(dummyInt, dummyList);
+  } else {
+    CHECK( detStore()->regFcn(&IGeoModelSvc::geoInit, geoModel, &TileCellBuilder::geoInit, this) );
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TileCellBuilder::geoInit(IOVSVC_CALLBACK_ARGS) {
+  
+  // retrieve MBTS and Tile detector manager, TileID helper and TileIfno from det store
+  if (m_MBTSContainer.size() > 0) {
+    if (detStore()->retrieve(m_mbtsMgr).isFailure()) {
+      ATH_MSG_WARNING( "Unable to retrieve MbtsDetDescrManager from DetectorStore" );
+      m_mbtsMgr = 0;
+    }
+  } else {
+    m_mbtsMgr = 0;
+  }
+
+  CHECK( detStore()->retrieve(m_tileMgr) );
+  CHECK( detStore()->retrieve(m_tileID) );
+  CHECK( detStore()->retrieve(m_tileTBID) );
+  CHECK( detStore()->retrieve(m_tileHWID) );
+
+  //=== get TileBadChanTool
+  CHECK( m_tileBadChanTool.retrieve() );
+
+  //=== get TileBeamInfo
+  CHECK( m_beamInfo.retrieve() );
+
+  // access tools and store them
+  CHECK( m_noiseFilterTools.retrieve() );
+
+  //=== get TileCondToolEmscale
+  CHECK( m_tileToolEmscale.retrieve() );
+
+  //=== get TileCondToolTiming
+  CHECK( m_tileToolTiming.retrieve() );
+
+
+  reset(true, false);
+
+  if (m_MBTSContainer.size() > 0)
+    ATH_MSG_INFO( "Storing MBTS cells in " << m_MBTSContainer );
+
+  ATH_MSG_INFO( "TileCellBuilder initialization completed" );
+
+  return StatusCode::SUCCESS;
+}
+
+void TileCellBuilder::reset(bool /* fullSizeCont */, bool printReset) {
+
+  if (printReset) ATH_MSG_INFO( "Resetting options in " << name() );
+  
+  // check if any threshold was set in jobOptions
+  m_thresholdNotSet = ((fabs(m_eThreshold + 100000.) < 1)
+                       && (fabs(m_maxTimeDiff - 100000.) < 1)
+                       && (fabs(m_maxTime - 100000.) < 1)
+                       && (fabs(m_minTime + 100000.) < 1)
+                       && (fabs(m_maxChi2 - 100000.) < 1)
+                       && (fabs(m_minChi2 + 100000.) < 1));
+
+  if (m_thresholdNotSet) {
+    ATH_MSG_INFO( "none of thresholds set, all RawChannels will be converted to Cells");
+  } else {
+    ATH_MSG_INFO( "Ene threshold " << m_eThreshold << " MeV" );
+    ATH_MSG_INFO( "max time diff " << m_maxTimeDiff << " ns" );
+    ATH_MSG_INFO( "max time thr  " << m_maxTime << " ns" );
+    ATH_MSG_INFO( "min time thr  " << m_minTime << " ns" );
+    ATH_MSG_INFO( "max qual thr  " << m_maxChi2 );
+    ATH_MSG_INFO( "min qual thr  " << m_minChi2 );
+  }
+  
+  // prepare empty vector for all cell pointers
+  m_fullSizeCont = true;
+  m_allCells.resize(m_tileID->cell_hash_max(), 0);
+  
+  m_MBTSCells = NULL;
+
+  ATH_MSG_INFO( "size of temp vector set to " << m_allCells.size() );
+  ATH_MSG_INFO( "taking RawChannels from '" << m_rawChannelContainer << "'" );
+}
+
+void TileCellBuilder::set_type_and_unit(TileFragHash::TYPE type
+    , TileRawChannelUnit::UNIT unit) {
+
+  // this method might be called from TileROD_Decoder
+  // otherwise it's not needed - type and unit are available from 
+  // TileRawChannelContainer itself (see TileCellBuilder::process() below)
+
+  m_RChType = type;
+  m_RChUnit = unit;
+
+  ATH_MSG_INFO( "type of container is '" << m_RChType << "'" );
+  ATH_MSG_INFO( "RawChannel unit [0=ADC, 1=pCb, 2=CspCb, 3=MeV] are '" << m_RChUnit << "'" );
+}
+
+StatusCode TileCellBuilder::finalize() {
+
+  ATH_MSG_INFO( "Finalizing" );
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TileCellBuilder::process(CaloCellContainer * theCellContainer) {
+
+  //**
+  //* Get TileRawChannels
+  //**
+
+  memset(m_drawerEvtStatus, 0, sizeof(m_drawerEvtStatus));
+
+  const TileRawChannelContainer* rawChannels;
+  if (evtStore()->retrieve(rawChannels, m_rawChannelContainer).isFailure()) {
+
+    ATH_MSG_WARNING( " Could not find container " << m_rawChannelContainer );
+    ATH_MSG_WARNING( " do not fill CaloCellContainer " );
+
+  } else {
+    
+    ATH_MSG_DEBUG( "Container " << m_rawChannelContainer << " with TileRawChannles found ");
+
+    m_RChType = rawChannels->get_type();
+    m_RChUnit = rawChannels->get_unit();
+    unsigned int m_bsflags = rawChannels->get_bsflags();
+    if (m_correctAmplitude || m_correctTime) {
+      int DataType = (m_bsflags & 0x30000000) >> 28;
+      if (DataType < 3) { // real data
+        m_maxTimeCorr = 63.9375; // 64-1/16 ns is hard limit in DSP
+        if (m_correctAmplitude && ((m_bsflags & 0x3000000) != 0)) {
+          ATH_MSG_WARNING( "Using results of Opt filter with interations from DSP, disabling amplitude correction" );
+          m_correctAmplitude = false;
+        }
+        if (m_correctTime && ((m_bsflags & 0x3000000) == 0)) {
+          ATH_MSG_WARNING( "Using results of Opt filter without interations from DSP, disabling time correction" );
+          m_correctTime = false;
+        }
+      } else {
+        m_maxTimeCorr = ((m_bsflags >> 27) & 1) ? 100.0 : 75.0; // 100 or 75 ns is the limit for 9 or 7 samples
+        if (m_correctAmplitude && ((m_bsflags & 0x6000) != 0)) {
+          ATH_MSG_WARNING( "Amplitude correction was done already in optimal filter, disabling it here" );
+          m_correctAmplitude = false;
+        }
+        if (m_correctTime && ((m_bsflags & 0x1000) != 0)) {
+          ATH_MSG_WARNING( "Time correction was done already in optimal filter, disabling it here" );
+          m_correctTime = false;
+        }
+      }
+    }
+
+    if (m_MBTSContainer.size() > 0)
+      m_MBTSCells = new TileCellContainer(SG::VIEW_ELEMENTS);
+    else
+      m_MBTSCells = NULL;
+
+    SelectAllObject<TileRawChannelContainer> selAll(rawChannels);
+    SelectAllObject<TileRawChannelContainer>::const_iterator begin = selAll.begin();
+    SelectAllObject<TileRawChannelContainer>::const_iterator end = selAll.end();
+
+    if (m_mergeChannels
+        && m_dspRawChannelContainer != m_rawChannelContainer
+        && m_dspRawChannelContainer.size() > 0) {
+
+      ATH_MSG_DEBUG( "Merging " << m_rawChannelContainer
+                    << " and " << m_dspRawChannelContainer );
+      const TileRawChannelContainer* dspChannels;
+      if (evtStore()->retrieve(dspChannels, m_dspRawChannelContainer).isFailure()) {
+        // no DSP channels, build cells from primary container
+        ATH_MSG_DEBUG( " No " << m_dspRawChannelContainer << " found, nothing to merge " );
+
+      } else {
+
+        if (m_beamInfo->noiseFilterApplied()) {
+          ATH_MSG_DEBUG( " Noise filter for " << m_dspRawChannelContainer 
+                         << " was already applied, use it directly " );
+        } else if (m_noiseFilterTools.size() > 0) {
+          ATH_MSG_DEBUG( " Running noise filter on " << m_dspRawChannelContainer 
+                         << " (i.e. on second container only) " );
+          // apply noise filter on dsp container before merging it with offline contaier
+          ToolHandleArray<ITileRawChannelTool>::iterator itrTool=m_noiseFilterTools.begin();
+          ToolHandleArray<ITileRawChannelTool>::iterator endTool=m_noiseFilterTools.end();
+
+          for (; itrTool != endTool; ++itrTool) {
+            if ((*itrTool)->process(dspChannels).isFailure()) {
+              ATH_MSG_ERROR( " Error status returned from noise filter " );
+            } else {
+              ATH_MSG_DEBUG( "Noise filter applied to the container" );
+            }
+          }
+          m_beamInfo->setNoiseFilterApplied(true);
+        }
+        
+        TileFragHash::TYPE dspType = dspChannels->get_type();
+        TileRawChannelUnit::UNIT dspUnit = dspChannels->get_unit();
+        unsigned int dspFlags = dspChannels->get_bsflags();
+        int DataType = (dspFlags & 0x30000000) >> 28;
+        float dspTimeCut = m_maxTimeCorr;
+        bool dspCorrectAmplitude = false, dspCorrectTime = false;
+        if (DataType < 3) { // real data
+          dspTimeCut = 63.9375; // 64-1/16 ns is hard limit in DSP
+          dspCorrectAmplitude = ((dspFlags & 0x3000000) == 0);
+          dspCorrectTime = ((dspFlags & 0x3000000) != 0);
+        } else { // dsp container contains resluts of offline reco
+          dspTimeCut = ((dspFlags >> 27) & 1) ? 100.0 : 75.0; // 100 or 75 ns is the limit for 9 or 7 samples
+        }
+
+        SelectAllObject<TileRawChannelContainer> selAllDsp(dspChannels);
+        SelectAllObject<TileRawChannelContainer>::const_iterator beginDsp = selAllDsp.begin();
+        SelectAllObject<TileRawChannelContainer>::const_iterator endDsp = selAllDsp.end();
+
+        std::vector<const TileRawChannel *> oflVec;
+        std::vector<const TileRawChannel *> dspVec;
+
+        SelectAllObject<TileRawChannelContainer>::const_iterator oflItr = begin;
+        SelectAllObject<TileRawChannelContainer>::const_iterator dspItr = beginDsp;
+        
+        if (oflItr != end) {
+          const TileRawChannel *p1 = (*oflItr);
+          HWIdentifier id1 = p1->adc_HWID();
+
+          for (; dspItr != endDsp; ++dspItr) {
+
+            const TileRawChannel *p2 = (*dspItr);
+            HWIdentifier id2 = p2->adc_HWID();
+
+            if (id2 < id1) {
+              dspVec.push_back(p2);
+            } else if (id2 == id1) {
+              oflVec.push_back(p1);
+              ++oflItr;
+              if (oflItr != end) {
+                p1 = (*oflItr);
+                id1 = p1->adc_HWID();
+              } else {
+                ++dspItr;
+                break;
+              }
+            } else {
+              while (id2 >= id1) {
+                oflVec.push_back(p1);
+                ++oflItr;
+                if (oflItr != end) {
+                  p1 = (*oflItr);
+                  bool id2gtid1 = (id2 > id1);
+                  id1 = p1->adc_HWID();
+                  if (id2gtid1 && (id2 < id1)) {
+                    dspVec.push_back(p2); // id2 is in the gap between old and new id1 - keep it
+                  }
+                } else {
+                  if (id2 == id1) ++dspItr;
+                  break;
+                }
+              }
+              if (id2 >= id1) break;
+            }
+          }
+          // copy all remaining channels 
+          for (; oflItr != end; ++oflItr) {
+            oflVec.push_back(*oflItr);
+          }
+        }
+        for (; dspItr != endDsp; ++dspItr) {
+          dspVec.push_back(*dspItr);
+        }
+
+        // build here with special iterator over 2 vectors
+        DoubleVectorIterator<std::vector<const TileRawChannel *>, const TileRawChannel *> vecBeg(
+            &oflVec, m_RChType, m_RChUnit, m_maxTimeCorr, m_correctAmplitude, m_correctTime,
+            &dspVec, dspType, dspUnit, dspTimeCut, dspCorrectAmplitude, dspCorrectTime, this, 0);
+        DoubleVectorIterator<std::vector<const TileRawChannel *>, const TileRawChannel *> vecEnd(
+            &oflVec, m_RChType, m_RChUnit, m_maxTimeCorr, m_correctAmplitude, m_correctTime,
+            &dspVec, dspType, dspUnit, dspTimeCut, dspCorrectAmplitude, dspCorrectTime, this, 2);
+
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " Calling build() method for rawChannels from two vectors" << endmsg;
+          msg(MSG::DEBUG) << " offline vector size = " << oflVec.size()
+                          << " dsp vector size = " << dspVec.size() << endmsg;
+        }
+        build(vecBeg, vecEnd, theCellContainer);
+        begin = end;
+      }
+
+    }
+
+    if (begin != end) { // no merging applied, use original raw channel container
+        
+      if (m_noiseFilterTools.size() > 0) {
+        ATH_MSG_DEBUG( " Running noise filter on " << m_rawChannelContainer );
+        // apply noise filter on input container before sending it to the build() method
+        ToolHandleArray<ITileRawChannelTool>::iterator itrTool = m_noiseFilterTools.begin();
+        ToolHandleArray<ITileRawChannelTool>::iterator endTool = m_noiseFilterTools.end();
+
+        for (; itrTool != endTool; ++itrTool) {
+          if ((*itrTool)->process(rawChannels).isFailure()) {
+            ATH_MSG_ERROR( " Error status returned from noise filter " );
+          } else {
+            ATH_MSG_DEBUG( "Noise filter applied to the container" );
+          }
+        }
+      }
+
+      ATH_MSG_DEBUG( " Calling build() method for rawChannels from " << m_rawChannelContainer );
+      build(begin, end, theCellContainer);
+    }
+    
+    if (m_MBTSContainer.size() > 0) {
+      if (evtStore()->record(m_MBTSCells, m_MBTSContainer, false).isFailure()) {
+        ATH_MSG_ERROR( " Could not register TileCellContainer for MBTS with name " << m_MBTSContainer );
+      }
+    }
+    
+    CaloCell_ID::SUBCALO caloNum = CaloCell_ID::TILE;
+    //specify that a given calorimeter has been filled
+    if (theCellContainer->hasCalo(caloNum)) {
+      // log << MSG::WARNING << "CaloCellContainer has already been filled with TileCells (caloNum = " 
+      //  <<	caloNum << ")" << endreq ;    
+    }
+    theCellContainer->setHasCalo(caloNum);
+  }
+  
+  //enum EventFlagErrorState { NotSet, Warning, Error };
+  EventInfo::EventFlagErrorState error = EventInfo::NotSet;
+  // flag will contain status of a given event
+  // every 4 bits - status of partitions LBA,LBC,EBA,EBC
+  // bits 0-3   - there is a signal above threshold in partitions
+  // bits 4-7   - there are channels with underflow (sample=0) in partition (since rel 17.2.6.4)
+  // bits 8-11  - there are channels with overflow (sample=1023) in partition (since rel 17.2.6.4)
+  // bits 12-15 - there are at least 16 drawers with bad quality in partition
+  // bits 16-19 - maximal length of consecutive bad area (since rel 17.2.6.5)
+  // bits 20-23 - there are at least 16 drawers which are completely masked in partition
+  // bits 24-27 - there are at least 16 drawers which do not send data in partition
+  // bits 28-31 - reserved for global good/warning/bad status
+  // bits 20-27 are also used for module number which gives warning status (since release 17.2.6.5)
+  //            in case of warning we are sure that bits which indicates error are not filled
+  unsigned int flag = 0;
+  
+  int drConsecMaxMax = 0;
+  int drConsecNum = 0;
+
+  for (int p = 1; p < 5; ++p) {
+    TileDrawerEvtStatus * evt = m_drawerEvtStatus[p];
+    TileDrawerRunStatus * run = m_drawerRunStatus[p];
+    int drAbsent = 0;
+    int drMasked = 0;
+    int drConsec = 0;
+    int drConsecMax = 0;
+    int hasBadQ = 0;
+    int hasOver = 0;
+    int hasUnder = 0;
+    int hasSig = 0;
+    for (int d = 0; d < 64; ++d) {
+      if (evt[d].nChannels == 0) {
+        ++drConsec;
+        ++drAbsent;
+        ++(run[d].drawerAbsent);
+      } else if (evt[d].nMaskedChannels >= evt[d].nChannels) {
+        ++drConsec;
+        ++drMasked;
+        ++(run[d].drawerMasked);
+      } else {
+        if (drConsec > drConsecMax) {
+          drConsecMax = drConsec;
+          if (drConsecMax > drConsecMaxMax) {
+            drConsecMaxMax = drConsecMax;
+            drConsecNum = ((p - 1) << 6) | (d - drConsec);
+          }
+        }
+        drConsec = 0;
+        if (evt[d].nMaskedChannels > 0) {
+          ++(run[d].channelsMasked);
+        }
+        if (evt[d].nBadQuality) ++hasBadQ;
+        if (evt[d].nOverflow) ++hasOver;
+        if (evt[d].nUnderflow) ++hasUnder;
+        if (evt[d].nSomeSignal) ++hasSig;
+      }
+    }
+    if (drConsec != 0 && drConsecMax < 64) { // 64th drawer is bad - check transition from 64th to 1st drawer
+      for (int d = 0; d < drConsecMax; ++d) {
+        if (evt[d].nChannels == 0 || evt[d].nMaskedChannels >= evt[d].nChannels) {
+          ++drConsec;
+        } else {
+          break;
+        }
+      }
+      if (drConsec > drConsecMax) {
+        drConsecMax = drConsec;
+      }
+    }
+    unsigned int fl = 0;
+    if (drAbsent > 15) {
+      fl |= 0x01000000;
+      error = EventInfo::Error;
+    }
+    if (drMasked > 15) {
+      fl |= 0x00100000;
+      error = EventInfo::Error;
+    }
+    //if (drConsecMax > 1)fl |= 0x00010000; // want to use these bits for length of consecutive area
+    if (hasBadQ > 15) fl |= 0x00001000;
+    if (hasOver) fl |= 0x00000100;
+    if (hasUnder) fl |= 0x00000010;
+    if (hasSig) fl |= 0x00000001;
+
+#ifdef ALLOW_DEBUG_COUT
+    std::cout<<"partition "<<p<<" drAbsent "<<drAbsent<<" drMasked "<<drMasked<<" drConsec "<<drConsecMax
+    <<" hasBadQ "<<hasBadQ<<" hasOver "<<hasOver<<" hasUnder "<<hasUnder<<" hasSig "<<hasSig<<std::endl;
+#endif
+    flag |= fl << (p - 1);
+  }
+
+  // number of cosecutively masked modules (if it's > 15 we have error already set)
+  flag |= (std::min(15, drConsecMaxMax) << 16);
+
+  if (drConsecMaxMax > 1 && error < EventInfo::Warning) {
+    // setting warning flag
+    error = EventInfo::Warning;
+    // putting starting module number of consecutive bad area
+    // instead of bits which indicates 16 masked or 16 absent modules in partition
+    flag |= (drConsecNum << 20);
+#ifdef ALLOW_DEBUG_COUT
+    std::cout<<"warning in partition " << (drConsecNum>>6)+1 << " for modules "
+    <<(drConsecNum)%64 <<" - " <<(drConsecNum+drConsecMaxMax-1)%64 <<std::endl;
+#endif
+  }
+  
+#ifdef ALLOW_DEBUG_COUT
+  std::cout<<"partition flag 0x0"<<std::hex<<flag<<std::dec<<" error "<<error<<std::endl;
+#endif
+
+  ++m_eventErrorCounter[error]; // error index is 0 or 1 or 2 here
+  ++m_eventErrorCounter[3]; // count separately total number of events
+  
+  // retrieve EventInfo
+  const EventInfo* eventInfo_c = 0;
+  if (evtStore()->retrieve(eventInfo_c).isFailure()) {
+    ATH_MSG_WARNING( " cannot retrieve EventInfo, will not set Tile information " );
+  }
+  EventInfo* eventInfo = 0;
+  if (eventInfo_c) {
+    eventInfo = const_cast<EventInfo*>(eventInfo_c);
+  }
+
+  if (eventInfo) {
+
+    if (flag != 0) {
+      ATH_MSG_DEBUG( " set eventInfo for Tile for this event to 0x" << MSG::hex << flag << MSG::dec );
+      if (!eventInfo->setEventFlags(EventInfo::Tile, flag)) {
+        ATH_MSG_WARNING( " cannot set eventInfo for Tile " );
+      }
+    }
+    
+    if (error != EventInfo::NotSet) {
+      ATH_MSG_DEBUG( " set error bits for Tile for this event to " << error );
+      if (!eventInfo->setErrorState(EventInfo::Tile, error)) {
+        ATH_MSG_WARNING( " cannot set error state for Tile " );
+      }
+    }
+
+  }
+  
+  // Execution completed.
+  ATH_MSG_DEBUG( "TileCellBuilder execution completed." );
+
+  return StatusCode::SUCCESS;
+}
+
+//************************************************************************
+void TileCellBuilder::correctCell(TileCell* pCell, int correction, int pmt, int gain
+    , float ener, float time, unsigned char iqual, unsigned char qbit, int ch_type) {
+//************************************************************************
+
+// Merge two pmts in one cell if needed
+// and apply corrections
+
+  // do not trust to energies below certain threshold
+  if (ener < m_minEneChan[ch_type]) {
+#ifdef ALLOW_DEBUG_COUT
+    std::cout << "channel with negative energy " << ener << " => setting quality to 255" << std::endl;
+#endif
+    iqual = 255;
+  }
+
+  switch (correction) {
+    case 1: // first pmt for this cell
+      pCell->addEnergy(ener, pmt, gain);
+      pCell->setTime(time); // overwrite time completely
+      pCell->setQuality(iqual, qbit, pmt);
+      pCell->setQuality(0, 0, 1 - pmt);
+      break;
+    case 2: // second pmt for this cell
+      pCell->addEnergy(ener, pmt, gain);
+      pCell->setTime(time, pmt); // calculate average time and timeDiff
+      pCell->setQuality(iqual, qbit, pmt);
+      break;
+  }
+}
+
+unsigned char TileCellBuilder::qbits(int ros, int drawer, bool count_over
+    , bool good_time, bool good_ener, bool overflow, bool underflow, bool overfit) {
+
+  ++m_drawerEvtStatus[ros][drawer].nChannels;
+  // new feature in rel 17.2.7 - count underflows and overflows
+  if (count_over) {
+    if (overflow) ++m_drawerEvtStatus[ros][drawer].nOverflow;
+    if (underflow) ++m_drawerEvtStatus[ros][drawer].nUnderflow;
+  }
+#ifdef ALLOW_DEBUG_COUT
+  if (overflow)  std::cout << "channel with overflow " << ((count_over)?"":"MBTS") << std::endl;
+  if (underflow) std::cout << "channel with underflow " << ((count_over)?"":"MBTS") << std::endl;
+  if (overfit)   std::cout << "channel with corrected overflow " << ((count_over)?"":"MBTS") << std::endl;
+#endif
+
+  unsigned char qbit = (overfit) ? (TileFragHash::FitFilter & TileCell::MASK_ALGO)
+                                 : (m_RChType & TileCell::MASK_ALGO);
+  if (good_time) qbit |= TileCell::MASK_TIME;
+  if (overflow || underflow) qbit |= TileCell::MASK_OVER;
+      
+  if (good_ener) {
+    qbit |= TileCell::MASK_AMPL;
+    if (count_over) {
+      ++m_drawerEvtStatus[ros][drawer].nSomeSignal;
+    }
+  }
+
+  return qbit;
+}
+
+// masking for MBTS with single channel
+bool TileCellBuilder::maskBadChannel(TileCell* pCell, HWIdentifier hwid) {
+  int ros = m_tileHWID->ros(hwid);
+  int drawer = m_tileHWID->drawer(hwid);
+  int chan = m_tileHWID->channel(hwid);
+  int gain = m_tileHWID->adc(hwid);
+  int drawerIdx = TileCalibUtils::getDrawerIdx(ros, drawer);
+  TileBchStatus chStatus = m_tileBadChanTool->getAdcStatus(drawerIdx, chan, gain);
+
+  // check quality first
+  bool bad = ((int) pCell->qual1() > m_qualityCut);
+  if (bad) {
+    ++m_drawerEvtStatus[ros][drawer].nBadQuality;
+
+  } else {
+    // check bad status in DB
+    bad = chStatus.isBad();
+
+    // Now checking the DQ status
+    if (!bad) {
+      bad = !(theDQstatus->isAdcDQgood(ros, drawer, chan, gain)
+            && m_beamInfo->isChanDCSgood(ros, drawer, chan));
+    }
+  }
+
+  if (bad) {
+    // only one channel in this cell and it is bad
+    ++m_drawerEvtStatus[ros][drawer].nMaskedChannels;
+
+    //pCell->setEnergy(m_zeroEnergy,0.0,TileID::LOWGAIN,CaloGain::INVALIDGAIN); // reset energy completely, indicate problem putting low gain
+    //pCell->setTime(0.0); // reset time completely
+    //pCell->setQuality(255,TileCell::MASK_BADCH,0); // reset quality flag for first pmt
+
+    if (gain == CaloGain::INVALIDGAIN) {
+      pCell->setEnergy(0.0, 0.0, TileID::LOWGAIN, CaloGain::INVALIDGAIN); // reset energy completely, indicate problem putting low gain
+    } else {
+      pCell->setEnergy(0.0, 0.0); // reset energy completely without changing the gain
+    }
+    pCell->setTime(-100.0); // reset time to big negative number to distinguish this bad cell from good cells
+    pCell->setQuality(255, (TileCell::MASK_BADCH | (pCell->qbit1() & TileCell::MASK_ALGO)), 0); // reset quality flag for first pmt
+    pCell->setQuality(0, TileCell::MASK_BADCH, 1); // reset quality flag for second pmt
+
+    return true;
+
+  } else if (chStatus.isBadTiming()) {
+    pCell->setTime(0.0); // channel with bad timing - no cell time
+    uint8_t qbit1 = pCell->qbit1() & (~(TileCell::MASK_TIME)); // clear time bit for first pmt
+    pCell->setQuality(pCell->qual1(), qbit1, 0); // update qbits for first pmt
+  }
+
+  return false;
+}
+  
+
+// masking for normal cells
+bool TileCellBuilder::maskBadChannels(TileCell* pCell) {
+  bool single_PMT_C10 = false;
+
+  const CaloDetDescrElement* caloDDE = pCell->caloDDE();
+
+  IdentifierHash hash1 = caloDDE->onl1();
+  IdentifierHash hash2 = caloDDE->onl2();
+
+  int gain1 = pCell->gain1();
+
+  HWIdentifier ch_id1 = m_tileHWID->channel_id(hash1);
+
+  int ros1 = m_tileHWID->ros(ch_id1);
+  int drawer1 = m_tileHWID->drawer(ch_id1);
+  int chan1 = m_tileHWID->channel(ch_id1);
+  int drawerIdx1 = TileCalibUtils::getDrawerIdx(ros1, drawer1);
+  const TileBchStatus& chStatus1 = m_tileBadChanTool->getAdcStatus(drawerIdx1, chan1, (gain1 < 0) ? 1 : gain1);
+  
+  // check quality first
+  bool bad1 = ((int) pCell->qual1() > m_qualityCut);
+  if (bad1) {
+    ++m_drawerEvtStatus[ros1][drawer1].nBadQuality;
+
+  } else {
+    // check bad status in DB
+    bad1 = (gain1 < 0) || chStatus1.isBad();
+
+    // Now checking the DQ status
+    if (!bad1) {
+      bad1 = !(theDQstatus->isAdcDQgood(ros1, drawer1, chan1, gain1)
+              && m_beamInfo->isChanDCSgood(ros1, drawer1, chan1));
+    }
+  }
+
+  if (hash2 == TileHWID::NOT_VALID_HASH) {
+    // gap/crack scintillators with one PMT per cell
+
+    if (bad1) {
+      // only one channel in this cell and it is bad
+      ++m_drawerEvtStatus[ros1][drawer1].nMaskedChannels;
+
+      if (gain1 == CaloGain::INVALIDGAIN) {
+        pCell->setEnergy(m_zeroEnergy, 0.0, TileID::LOWGAIN, CaloGain::INVALIDGAIN); // reset energy completely, indicate problem putting low gain
+      } else {
+        pCell->setEnergy(m_zeroEnergy, 0.0); // reset energy completely without changing gain
+      }
+      pCell->setTime(0.0); // reset time completely
+      pCell->setQuality(255, (TileCell::MASK_BADCH | (pCell->qbit1() & TileCell::MASK_ALGO)), 0); // reset quality flag for first pmt
+      pCell->setQuality(0, TileCell::MASK_BADCH, 1); // reset quality flag for second pmt
+
+      return true;
+
+    } else if (chStatus1.isBadTiming()) {
+      pCell->setTime(0.0); // channel with bad timing - no cell time
+      uint8_t qbit1 = pCell->qbit1() & (~(TileCell::MASK_TIME)); // clear time bit for first pmt
+      pCell->setQuality(pCell->qual1(), qbit1, 0); // update qbits for first pmt
+    }
+    
+  } else { //cell has both PMTs
+
+    int gain2 = pCell->gain2();
+    
+    HWIdentifier ch_id2 = m_tileHWID->channel_id(hash2);
+
+    int ros2 = m_tileHWID->ros(ch_id2);
+    int drawer2 = m_tileHWID->drawer(ch_id2);
+    int chan2 = m_tileHWID->channel(ch_id2);
+    int drawerIdx2 = TileCalibUtils::getDrawerIdx(ros2, drawer2);
+    const TileBchStatus& chStatus2 = m_tileBadChanTool->getAdcStatus(drawerIdx2, chan2, (gain2 < 0) ? 1 : gain2);
+
+    // check quality first
+    bool bad2 = ((int) pCell->qual2() > m_qualityCut);
+    if (bad2) {
+      ++m_drawerEvtStatus[ros2][drawer2].nBadQuality;
+
+    } else {
+      // check bad status in DB
+      bad2 = (gain2 < 0) || chStatus2.isBad();
+
+      // Now checking the DQ status
+      if (!bad2) {
+        bad2 = !(theDQstatus->isAdcDQgood(ros2, drawer2, chan2, gain2)
+                && m_beamInfo->isChanDCSgood(ros2, drawer2, chan2));
+      }
+    }
+
+    static const TileCablingService * s_cabling = TileCablingService::getInstance();
+    static const bool run2 = (s_cabling->getCablingType() == TileCablingService::RUN2Cabling);
+    single_PMT_C10 = (((ros2 == TileHWID::EXTBAR_POS && chan1 == 4)
+                      || (ros2 == TileHWID::EXTBAR_NEG && chan2 == 4))
+                      && !s_cabling->C10_connected(drawer2));
+    if (single_PMT_C10) {
+      // for special C10 disconnected channel might be masked in DB
+      // and energy of good channel is taken twice with correct weight
+      // but if this channel is not masked in DB - set its bad status
+      // equal to bad status of real channel, so that cell is masked correctly
+      // if real channel connected to a cell is bad
+#ifdef ALLOW_DEBUG_COUT
+      static int cnt=0;
+      if (++cnt < 17) {
+        std::cout << "special C10 in " << ((ros2==TileHWID::EXTBAR_POS) ? "EBA" : "EBC")
+        << drawer2+1 << " status " << chan1 << "/" << chan2 << " "
+        << (chStatus1.isBad()?"bad":"good") << "/"
+        << (chStatus2.isBad()?"bad":"good") << "/"
+        << ((run2)?" RUN2 cabling": "RUN1 cabling")
+        << std::endl;
+      }
+#endif
+      if (chan1 == 4) {
+        if (run2 || !chStatus1.isBad()) {
+#ifdef ALLOW_DEBUG_COUT
+          if (cnt < 17) {
+            std::cout << "Ene of chan1 was " << pCell->ene1() << " changing to half of " << pCell->ene2()
+            << " and setting bad1=true" << std::endl;
+          }
+#endif
+          pCell->setEnergy(pCell->ene2()/2., pCell->ene2()/2., gain2, gain2);
+          //bad1 = bad2;
+          bad1 = true;
+          --m_drawerEvtStatus[ros1][drawer1].nMaskedChannels; // since it's fake masking, decrease counter by 1 in advance
+        }
+      } else {
+        if (run2 || !chStatus2.isBad()) {
+#ifdef ALLOW_DEBUG_COUT
+          if (cnt < 17) {
+            std::cout << "Ene of chan2 was " << pCell->ene2() << " changing to half of " << pCell->ene1()
+            << " and setting bad2=true" << std::endl;
+          }
+#endif
+          pCell->setEnergy(pCell->ene1()/2., pCell->ene1()/2., gain1, gain1);
+          //bad2 = bad1;
+          bad2 = true;
+          --m_drawerEvtStatus[ros2][drawer2].nMaskedChannels; // since it's fake masking, decrease counter by 1 in advance
+        }
+      }
+    }
+    if (bad1 && bad2) {
+      // both channels are bad
+      ++m_drawerEvtStatus[ros1][drawer1].nMaskedChannels;
+      ++m_drawerEvtStatus[ros2][drawer2].nMaskedChannels;
+
+      if (gain1 == CaloGain::INVALIDGAIN || gain2 == CaloGain::INVALIDGAIN) {
+        if (gain1 == CaloGain::INVALIDGAIN) gain1 = (CaloGain::CaloGain) TileID::LOWGAIN;
+        if (gain2 == CaloGain::INVALIDGAIN) gain2 = (CaloGain::CaloGain) TileID::LOWGAIN;
+        pCell->setEnergy(m_zeroEnergy, m_zeroEnergy, gain1, gain2); // reset energy completely, indicate problem putting low gain
+      } else {
+        pCell->setEnergy(m_zeroEnergy, m_zeroEnergy); // reset energy completely without changing gain
+      }
+      pCell->setTime(0.0);  // reset time completely
+      pCell->setQuality(255, (TileCell::MASK_BADCH | (pCell->qbit1() & TileCell::MASK_ALGO)), 0); // reset quality flag for first pmt
+      pCell->setQuality(255, (TileCell::MASK_BADCH | (pCell->qbit2() & TileCell::MASK_ALGO)), 1); // reset quality flag for second pmt
+
+      return true;
+
+    } else if (bad1 && !bad2) {
+      // first channel is bad
+      ++m_drawerEvtStatus[ros1][drawer1].nMaskedChannels;
+
+      float ene2 = pCell->ene2();
+      pCell->setEnergy(ene2, ene2, gain2, gain2); // use energy/gain from second pmt for both pmts
+
+      if (chStatus2.isBadTiming()) {
+        pCell->setTime(0.0); // time in second pmt is bad - no cell time
+        uint8_t qual2 = pCell->qual2();
+        uint8_t qbit2 = pCell->qbit2() & (~(TileCell::MASK_TIME)); // clear time bit for second pmt
+        uint8_t qbit1 = qbit2 | TileCell::MASK_BADCH; // set bad channel bit for first pmt
+        uint8_t qual1 = qual2 + (gain1 - gain2); // if gains are different, qua11 and qual2 will be different
+        if (qual1 > m_qualityCut && gain1 > gain2) qual1 = qual2 - (gain1 - gain2); // new feature in release 17.2
+        pCell->setQuality(qual1, qbit1, 0); // change quality and qbits for first pmt
+        pCell->setQuality(qual2, qbit2, 1); // update qbits for second pmt
+      } else {
+        pCell->setTime(pCell->time2());  // use time from second pmt as cell time
+        pCell->setQuality(pCell->qual2(), (pCell->qbit2() | TileCell::MASK_BADCH), 0); // change quality flag for first pmt
+      }
+      
+      return true;
+
+    } else if (!bad1 && bad2) {
+      // second channel is bad
+      ++m_drawerEvtStatus[ros2][drawer2].nMaskedChannels;
+
+      float ene1 = pCell->ene1();
+      pCell->setEnergy(ene1, ene1, gain1, gain1);  // use energy/gain from first pmt for both pmts
+
+      if (chStatus1.isBadTiming()) {
+        pCell->setTime(0.0); // time in first pmt is bad - no cell time
+        uint8_t qual1 = pCell->qual1();
+        uint8_t qbit1 = pCell->qbit1() & (~(TileCell::MASK_TIME)); // clear time bit for first pmt
+        uint8_t qbit2 = qbit1 | TileCell::MASK_BADCH; // set bad channel bit for second pmt
+        uint8_t qual2 = qual1 + (gain2 - gain1); // if gains are different, qua11 and qual2 will be different
+        if (qual2 > m_qualityCut && gain2 > gain1) qual2 = qual1 - (gain2 - gain1); // new feature in release 17.2
+        pCell->setQuality(qual1, qbit1, 0); // update qbits for first pmt
+        pCell->setQuality(qual2, qbit2, 1); // change quality and qbits for second pmt
+      } else {
+        pCell->setTime(pCell->time1());  // use time from first pmt as cell time
+        pCell->setQuality(pCell->qual1(), (pCell->qbit1() | TileCell::MASK_BADCH), 1); // change quality flag for second pmt
+      }
+      
+      return true;
+
+    } else {
+
+      if (chStatus1.isBadTiming()) {
+
+        if (chStatus2.isBadTiming()) {
+          pCell->setTime(0.0); // time in both pmts is bad - no cell time
+          uint8_t qbit2 = pCell->qbit2() & (~(TileCell::MASK_TIME)); // clear time bit for second pmt
+          pCell->setQuality(pCell->qual2(), qbit2, 1); // update qbits for second pmt
+        } else {
+          pCell->setTime(pCell->time2());  // use time from second pmt as cell time
+        }
+        uint8_t qbit1 = pCell->qbit1() & (~(TileCell::MASK_TIME)); // clear time bit for first pmt
+        pCell->setQuality(pCell->qual1(), qbit1, 0); // update qbits for first pmt
+
+      } else if (chStatus2.isBadTiming()) {
+
+        pCell->setTime(pCell->time1());  // use time from first pmt as cell time
+        uint8_t qbit2 = pCell->qbit2() & (~(TileCell::MASK_TIME)); // clear time bit for second pmt
+        pCell->setQuality(pCell->qual2(), qbit2, 1); // update qbits for second pmt
+      }
+    }
+    
+  }
+  
+  return single_PMT_C10;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileCellFakeProb.cxx b/TileCalorimeter/TileRecUtils/src/TileCellFakeProb.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..ada6734ce9119896e8fdc4f556307e34a1599ef4
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileCellFakeProb.cxx
@@ -0,0 +1,124 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "AthenaKernel/errorcheck.h"
+
+#include "CaloIdentifier/TileID.h"
+#include "CaloIdentifier/CaloCell_ID.h"
+#include "CaloEvent/CaloCell.h"
+
+#include "TileIdentifier/TileHWID.h"
+#include "TileConditions/TileCablingService.h" 
+#include "TileRecUtils/TileCellFakeProb.h"
+
+
+
+// Constructor:
+
+TileCellFakeProb::TileCellFakeProb(const std::string& type,
+    const std::string& name, const IInterface* parent)
+  : AthAlgTool(type, name, parent)
+  , m_caloID(0)
+  , m_tileID(0)
+  , m_tileHWID(0)
+  , m_cabling(0)
+{
+  declareInterface<ICellWeightTool>(this);
+  declareProperty("DeadDrawerList", m_deadDrawerInput);
+}
+
+StatusCode TileCellFakeProb::initialize() {
+
+  // Get the CaloCell_ID helper from the detector store
+  CHECK( detStore()->retrieve(m_caloID, "CaloCell_ID") );
+
+  // Get the TileID helper from the detector store
+  CHECK( detStore()->retrieve(m_tileID, "TileID") );
+
+  // Get the TileHWID helper from the detector store
+  CHECK( detStore()->retrieve(m_tileHWID, "TileHWID") );
+
+  // Instantiate Cabling Svc to initialize pointers to helpers there
+  m_cabling = TileCablingService::getInstance();
+  if (m_cabling == 0) {
+    ATH_MSG_FATAL( " Cannot get instance of TileCablingService" );
+    return StatusCode::FAILURE;
+  }
+
+  // process properties from jobOptions
+  CHECK( createMiscalibratedCellList() );
+
+  return StatusCode::SUCCESS;
+}
+
+// Desctructor
+TileCellFakeProb::~TileCellFakeProb() {
+}
+
+// MakeCorrection:  This is called with a pointer to the Cell Object.
+double TileCellFakeProb::wtCell(const CaloCell* theCell) {
+
+  Identifier id = theCell->ID();
+  double totalweight = 1.0; // default weight
+
+  if (m_tileID->is_tile(id)) {
+    std::map<Identifier, double>::iterator cur = m_celllist.find(id);
+
+    if (cur != m_celllist.end()) {
+      totalweight = (cur->second);
+      ATH_MSG_VERBOSE( "eta = " << theCell->eta()
+                      << ", phi = " << theCell->phi()
+                      << ", weight = " << totalweight );
+    }
+  }
+
+  return totalweight;
+}
+
+StatusCode TileCellFakeProb::createMiscalibratedCellList() {
+
+//  ros         1 to 4      ReadOutSystem number ( 1,2 = pos/neg Barrel (side A/C)
+//                                                 3,4 = pos/neg Ext.Barrel (side A/C) )
+//  drawer      0 to 63     64 drawers (modules) in one cylinder (phi-slices)
+//  channel     0 to 47     channel number in the drawer
+//  adc         0 to 1      ADC number for the channel (0 = low gain, 1 = high gain)
+
+  // read in dead drawers from jopOptions
+  std::vector<std::string>::const_iterator itrStringID = m_deadDrawerInput.begin();
+  for (; itrStringID != m_deadDrawerInput.end(); ++itrStringID) {
+    std::string theString = *itrStringID;
+    std::stringstream is;
+    is << theString << std::endl;
+
+    int iros, idrawer;
+    double weight;
+    is >> iros >> idrawer >> weight;
+
+    ATH_MSG_DEBUG( "scale ROS " << iros
+                  << " Drawer " << idrawer
+                  << " with weight=" << weight );
+
+    // loop over all TileCal cells and check if they belong to dead drawers
+    int sub_calo_num = (int) CaloCell_ID::TILE;
+    IdentifierHash hmin, hmax;
+    m_caloID->calo_cell_hash_range(sub_calo_num, hmin, hmax);
+
+    int nCells = 0;
+    for (unsigned int i = hmin; i < hmax; ++i) {
+      Identifier id = m_caloID->cell_id((IdentifierHash) i);
+      HWIdentifier myhwid = m_cabling->s2h_drawer_id(id);
+      int ros = m_tileHWID->ros(myhwid);
+      int drawer = m_tileHWID->drawer(myhwid);
+
+      if ((ros == iros) && (drawer == idrawer)) {
+        m_celllist[id] = weight;
+        ++nCells;
+      }
+    }
+    ATH_MSG_DEBUG( "looped over " << (hmax - hmin)
+                  << " cells, " << nCells << " cells matched" );
+  }
+
+  return StatusCode::SUCCESS;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileCellMaskingTool.cxx b/TileCalorimeter/TileRecUtils/src/TileCellMaskingTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8f73f039fcdcc66901d3b3efc755c5031ee60084
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileCellMaskingTool.cxx
@@ -0,0 +1,331 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// Gaudi includes
+#include "GaudiKernel/Service.h"
+#include "GaudiKernel/Property.h"
+
+// Atlas includes
+#include "AthenaKernel/errorcheck.h"
+
+// Calo includes
+#include "CaloIdentifier/TileID.h"
+#include "CaloEvent/CaloCellContainer.h"
+
+// Tile includes
+#include "TileRecUtils/TileCellMaskingTool.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileEvent/TileCell.h"
+
+#include <CLHEP/Units/SystemOfUnits.h>
+
+using CLHEP::MeV;
+
+TileCellMaskingTool::TileCellMaskingTool(const std::string& type
+    , const std::string& name, const IInterface* parent)
+  : AthAlgTool(type, name, parent)
+  , m_tileID(0)
+  , m_tileHWID(0)
+{
+  declareInterface<ICaloCellMakerTool>( this );
+
+  declareProperty("RejectedTileDrawer", m_rejectedTileDrawer) ;
+  declareProperty("RejectedTileMB", m_rejectedTileMB) ;
+  declareProperty("RejectedTileDigitizer", m_rejectedTileDigitizer) ;
+  declareProperty("RejectedTileDMU", m_rejectedTileDMU) ;
+  declareProperty("RejectedTileChannels", m_rejectedTileChannels) ;
+
+  declareProperty("BadChannelZeroEnergy",m_zeroEnergy = 0.5 * MeV); // half a MeV in both PMTs i.e. one MeV in a cell
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+StatusCode TileCellMaskingTool::initialize() {
+
+  ATH_MSG_DEBUG( " TileCellMaskingTool called " );
+
+  CHECK( detStore() -> retrieve(m_tileID, "TileID") );
+  CHECK( detStore() -> retrieve(m_tileHWID, "TileHWID") );
+
+  CHECK( fillIncludedCellsMap() );
+  ATH_MSG_DEBUG( " Will exclude "
+                << m_includedCellsMap.size() - m_includedCellsMap.count()
+                << " channels " );
+
+  return StatusCode::SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+StatusCode TileCellMaskingTool::fillIncludedCellsMap() {
+
+  m_includedCellsMap.set();
+
+  int ros = 0, drw = 0, index = -1;
+
+  std::vector<std::string>::const_iterator it_dr = m_rejectedTileDrawer.begin();
+  std::vector<std::string>::const_iterator it_dr_e = m_rejectedTileDrawer.end();
+  for (; it_dr != it_dr_e; it_dr++) {
+    std::stringstream dris;
+    dris << (*it_dr);
+    dris >> ros >> drw;
+    killer("drawer", ros, drw, index);
+  }
+
+  std::vector<std::string>::const_iterator it_mb = m_rejectedTileMB.begin();
+  std::vector<std::string>::const_iterator it_mb_e = m_rejectedTileMB.end();
+  for (; it_mb != it_mb_e; it_mb++) {
+    std::stringstream dris;
+    dris << (*it_mb);
+    dris >> ros >> drw >> index;
+    killer("mb", ros, drw, index);
+  }
+
+  std::vector<std::string>::const_iterator it_dig = m_rejectedTileDigitizer.begin();
+  std::vector<std::string>::const_iterator it_dig_e = m_rejectedTileDigitizer.end();
+  for (; it_dig != it_dig_e; it_dig++) {
+    std::stringstream dris;
+    dris << (*it_dig);
+    dris >> ros >> drw >> index;
+    killer("dig", ros, drw, index);
+  }
+
+  std::vector<std::string>::const_iterator it_dmu = m_rejectedTileDMU.begin();
+  std::vector<std::string>::const_iterator it_dmu_e = m_rejectedTileDMU.end();
+  for (; it_dmu != it_dmu_e; it_dmu++) {
+    std::stringstream dris;
+    dris << (*it_dmu);
+    dris >> ros >> drw >> index;
+    killer("dmu", ros, drw, index);
+  }
+
+  std::vector<std::string>::const_iterator it = m_rejectedTileChannels.begin();
+  std::vector<std::string>::const_iterator it_e = m_rejectedTileChannels.end();
+  for (; it != it_e; it++) {
+    std::stringstream dris;
+    dris << (*it);
+    dris >> ros >> drw >> index;
+    killer("channel", ros, drw, index);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TileCellMaskingTool::killer(std::string component, int ros, int drw, int index) {
+
+  int begin = 0, end = 0;
+
+  if (component == "drawer") {
+    begin = 0;
+    end = 48;
+  } else if (component == "mb") {
+    begin = (4 - index) * 12;
+    end = begin + 12;
+  } else if (component == "dig") {
+    begin = (8 - index) * 6;
+    end = begin + 6;
+  } else if (component == "dmu") {
+    begin = index * 3;
+    end = begin + 3;
+  } else {
+    begin = index;
+    end = begin + 1;
+  }
+
+  if (ros < 1 || ros > 4) {
+    ATH_MSG_WARNING( " wrong selection of ros = " << ros << " in TileCellMaskingTool::killer ");
+  } else if (drw < 0 || drw > 63) {
+    ATH_MSG_WARNING( " wrong selection of drawer = " << drw << " in TileCellMaskingTool::killer ");
+  } else if (begin < 0 || end > 48) {
+    ATH_MSG_WARNING( " wrong selection of "<< component
+                    << " = " << index << " in TileCellMaskingTool::killer ");
+  } else {
+    msg(MSG::INFO) << " killing ros " << ros << " drawer " << drw;
+    if (component != "drawer") {
+      msg(MSG::INFO) << component << " " << index << endmsg;
+    } else {
+      msg(MSG::INFO) << endmsg;
+    }
+
+    for (int channel = begin; channel < end; ++channel) {
+      HWIdentifier chid = m_tileHWID->channel_id(ros, drw, channel);
+      IdentifierHash hash = m_tileHWID->get_channel_hash(chid);
+      m_includedCellsMap.reset(hash);
+      ATH_MSG_DEBUG( "deleting channel " << m_tileHWID->to_string(chid,-1)
+                    << " hash " << hash );
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+StatusCode TileCellMaskingTool::process(CaloCellContainer* theCont) {
+
+  int n_cells = 0, n_masked_1 = 0, n_masked_2 = 0, n_masked_12 = 0;
+  double ene_before = 0.0, ene_after = 0.0;
+
+  size_t firstCell = theCont->indexFirstCellCalo(CaloCell_ID::TILE);
+  size_t lastCell = theCont->indexLastCellCalo(CaloCell_ID::TILE);
+
+  for (size_t iCell = firstCell; iCell <= lastCell; ++iCell) {
+
+    CaloCell* aCell = (*theCont)[iCell];
+    const CaloDetDescrElement * caloDDE = aCell->caloDDE();
+
+    if (caloDDE->is_tile()) { // check if cell belongs to TileCalorimeter
+
+      if (msgLvl(MSG::DEBUG)) {
+        ++n_cells;
+        ene_before += aCell->energy();
+      }
+
+      IdentifierHash hash1 = caloDDE->onl1();
+      IdentifierHash hash2 = caloDDE->onl2();
+
+      bool bit1 = m_includedCellsMap.test(hash1); // true - good channel
+      bool bit2 = m_includedCellsMap.test(hash2); // false - channel should be masked
+
+      TileCell* pCell = (TileCell*) aCell;
+      int gain1 = pCell->gain1();
+      int gain2 = pCell->gain2();
+
+      if (bit1) { // first is good
+
+        if (!bit2) { // second is bad
+
+          if (msgLvl(MSG::DEBUG)) {
+            ++n_masked_12;
+            if (msgLvl(MSG::VERBOSE)) {
+              msg(MSG::VERBOSE) << " second channel is OFF"
+                                << ", hash2: " << hash2
+                                << "  before " << pCell->ene1()
+                                << " + " << pCell->ene2()
+                                << " = " << pCell->energy();
+            }
+          }
+
+          float ene1 = pCell->ene1();
+          pCell->setEnergy(ene1, ene1, gain1, gain1); // use energy/gain from first pmt for both pmts
+          pCell->setTime(pCell->time1());  // use time from first pmt as cell time
+          pCell->setQuality(pCell->qual1(), (pCell->qbit1() | TileCell::MASK_BADCH), 1); // change quality flag for second pmt
+
+          if (msgLvl(MSG::VERBOSE)) {
+            msg(MSG::VERBOSE) << "  after " << pCell -> ene1()
+                              << " + " << pCell -> ene2()
+                              << " = " << pCell -> energy() << endmsg;
+          }
+        }
+      } else { // first is bad
+
+        if (hash2 == TileHWID::NOT_VALID_HASH) { // cells wih single PMT
+
+          if (msgLvl(MSG::DEBUG)) {
+            ++n_masked_1;
+            if (msgLvl(MSG::VERBOSE)) {
+              msg(MSG::VERBOSE) << " channel in GAP is OFF"
+                                << ", hash1: " << hash1
+                                << "  before " << pCell->ene1()
+                                << " + " << pCell->ene2()
+                                << " = " << pCell->energy();
+            }
+          }
+
+          if (gain1 == CaloGain::INVALIDGAIN) {
+            pCell->setEnergy(m_zeroEnergy, 0.0, TileID::LOWGAIN, CaloGain::INVALIDGAIN); // reset energy completely, indicate problem putting low gain
+          } else {
+            pCell->setEnergy(m_zeroEnergy, 0.0); // reset energy completely without changing gain
+          }
+          pCell->setTime(0.0); // reset time completely
+          pCell->setQuality(255, TileCell::MASK_BADCH, 0); // reset quality flag for first pmt
+          pCell->setQuality(0, TileCell::MASK_BADCH, 1); // reset quality flag for second pmt
+
+          if (msgLvl(MSG::VERBOSE)) {
+            msg(MSG::VERBOSE) << "  after " << pCell->ene1()
+                              << " + " << pCell->ene2()
+                              << " = " << pCell->energy() << endmsg;
+          }
+
+        } else if (bit2) { // second is good
+
+          if (msgLvl(MSG::DEBUG)) {
+            ++n_masked_12;
+            if (msgLvl(MSG::VERBOSE)) {
+              msg(MSG::VERBOSE) << " first  channel is OFF"
+                                << ", hash1: " << hash1
+                                << "  before " << pCell->ene1()
+                                << " + " << pCell->ene2()
+                                << " = " << pCell->energy();
+            }
+          }
+
+          float ene2 = pCell->ene2();
+          pCell->setEnergy(ene2, ene2, gain2, gain2); // use energy/gain from second pmt for both pmts
+          pCell->setTime(pCell->time2());  // use time from second pmt as cell time
+          pCell->setQuality(pCell->qual2(), (pCell->qbit2() | TileCell::MASK_BADCH), 0); // change quality flag for first pmt
+
+          if (msgLvl(MSG::VERBOSE))
+            msg(MSG::VERBOSE) << "  after " << pCell->ene1()
+                              << " + " << pCell->ene2()
+                              << " = " << pCell->energy() << endmsg;
+
+        } else { // second is bad
+
+          if (msgLvl(MSG::DEBUG)) {
+            ++n_masked_2;
+            if (msgLvl(MSG::VERBOSE)) {
+              msg(MSG::VERBOSE) << " both channels are OFF"
+                                << ", hash1: " << hash1
+                                << ", hash2: " << hash2
+                                << "  before " << pCell->ene1()
+                                << " + " << pCell->ene2()
+                                << " = " << pCell->energy();
+            }
+          }
+
+          if (gain1 == CaloGain::INVALIDGAIN || gain2 == CaloGain::INVALIDGAIN) {
+            if (gain1 == CaloGain::INVALIDGAIN) gain1 = (CaloGain::CaloGain) TileID::LOWGAIN;
+            if (gain2 == CaloGain::INVALIDGAIN) gain2 = (CaloGain::CaloGain) TileID::LOWGAIN;
+            pCell->setEnergy(m_zeroEnergy, m_zeroEnergy, gain1, gain2); // reset energy completely, indicate problem putting low gain
+          } else {
+            pCell->setEnergy(m_zeroEnergy, m_zeroEnergy); // reset energy completely without changing gain
+          }
+          pCell->setTime(0.0);  // reset time completely
+          pCell->setQuality(255, TileCell::MASK_BADCH, 0); // reset quality flag for first pmt
+          pCell->setQuality(255, TileCell::MASK_BADCH, 1); // reset quality flag for second pmt
+
+          if (msgLvl(MSG::VERBOSE)) {
+            msg(MSG::VERBOSE) << "  after " << pCell->ene1()
+                              << " + " << pCell->ene2()
+                              << " = " << pCell->energy() << endmsg;
+          }
+        }
+      }
+      ene_after += aCell->energy();
+    }
+  }
+
+  ATH_MSG_DEBUG( " Ncells: " << n_cells
+                << " N masked gap/normal/half-masked: " << n_masked_1 << " " << n_masked_2 << " " << n_masked_12
+                << " Ene before/after/delta: " << ene_before << " " << ene_after << " " << ene_after-ene_before );
+
+  return StatusCode::SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+StatusCode TileCellMaskingTool::finalize() {
+
+  return StatusCode::SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool TileCellMaskingTool::channel_is_good(HWIdentifier &hwid) {
+
+  IdentifierHash ChHash = m_tileHWID->get_channel_hash(m_tileHWID->channel_id(hwid));
+
+  return m_includedCellsMap.test(ChHash);
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileCellNoiseFilter.cxx b/TileCalorimeter/TileRecUtils/src/TileCellNoiseFilter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..cdca3358dc68c26b8b7531f2eb39215dc0823cdb
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileCellNoiseFilter.cxx
@@ -0,0 +1,403 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// Atlas includes
+#include "AthenaKernel/errorcheck.h"
+#include "GeoModelInterfaces/IGeoModelSvc.h"
+#include "Identifier/Identifier.h"
+#include "Identifier/IdentifierHash.h"
+
+// Calo includes
+#include "CaloEvent/CaloCell.h"
+#include "CaloEvent/CaloCellContainer.h"
+#include "CaloDetDescr/CaloDetDescrElement.h"
+
+// Tile includes
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileEvent/TileCell.h"
+#include "TileCalibBlobObjs/TileCalibUtils.h"
+#include "TileRecUtils/TileCellNoiseFilter.h"
+
+#include <cmath>
+#include <algorithm>
+#include <functional>
+
+static const InterfaceID IID_ITileCellNoiseFilter("TileCellNoiseFilter", 1, 0);
+
+const InterfaceID& TileCellNoiseFilter::interfaceID() {
+  return IID_ITileCellNoiseFilter;
+}
+
+//========================================================
+// constructor
+TileCellNoiseFilter::TileCellNoiseFilter(const std::string& type,
+    const std::string& name, const IInterface* parent)
+    : AthAlgTool(type, name, parent)
+    , m_tileID(0)
+    , m_tileHWID(0)
+    , m_tileToolEmscale("TileCondToolEmscale")
+    , m_tileToolNoiseSample("TileCondToolNoiseSample")
+    , m_noiseTool("CaloNoiseTool")
+    , m_truncationThresholdOnAbsEinSigma(4.0) // 4 sigma of ADC HF noise by default
+    , m_minimumNumberOfTruncatedChannels(0.6) // at least 60% of channels should be below threshold
+    , m_useTwoGaussNoise(false) // do not use 2G - has no sense for ADC HF noise for the moment
+    , m_useTileNoiseDB(true)         // Tile DB with ADC HF noise by defaul
+{
+  declareInterface<ICaloCellMakerTool>(this);
+  declareInterface<TileCellNoiseFilter>(this);
+
+  declareProperty("TileCondToolEmscale", m_tileToolEmscale);
+  declareProperty("TileCondToolNoiseSample", m_tileToolNoiseSample);
+  declareProperty("CaloNoiseTool", m_noiseTool);
+
+  declareProperty("UseTwoGaussNoise", m_useTwoGaussNoise);
+  declareProperty("UseTileNoiseDB", m_useTileNoiseDB);
+  declareProperty("TruncationThresholdOnAbsEinSigma", m_truncationThresholdOnAbsEinSigma);
+  declareProperty("MinimumNumberOfTruncatedChannels", m_minimumNumberOfTruncatedChannels);
+}
+
+//========================================================
+// Initialize
+StatusCode TileCellNoiseFilter::initialize() {
+  ATH_MSG_INFO("Initializing...");
+
+  const IGeoModelSvc *geoModel = 0;
+  CHECK( service("GeoModelSvc", geoModel));
+
+  // dummy parameters for the callback:
+  int dummyInt = 0;
+  std::list<std::string> dummyList;
+
+  if (geoModel->geoInitialized()) {
+    return geoInit(dummyInt, dummyList);
+  } else {
+    CHECK( detStore()->regFcn(&IGeoModelSvc::geoInit
+          , geoModel, &TileCellNoiseFilter::geoInit, this));
+
+    ATH_MSG_INFO( "geoInit callback registered");
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// ============================================================================
+// delayed initialize
+StatusCode TileCellNoiseFilter::geoInit(IOVSVC_CALLBACK_ARGS) {
+  ATH_MSG_INFO("Entering GeoInit");
+
+  CHECK( detStore()->retrieve(m_tileID));
+  CHECK( detStore()->retrieve(m_tileHWID));
+
+  //=== get TileCondToolEmscale
+  CHECK( m_tileToolEmscale.retrieve() );
+
+  if (m_useTileNoiseDB) {
+    //=== get TileCondToolNoiseSample
+    CHECK( m_tileToolNoiseSample.retrieve());
+  } else {
+    //=== CaloNoiseTool
+    CHECK( m_noiseTool.retrieve());
+  }
+
+  ATH_MSG_INFO("geoInit() end");
+
+  return StatusCode::SUCCESS;
+}
+
+// ============================================================================
+// process container
+StatusCode TileCellNoiseFilter::process(CaloCellContainer *cellcoll) {
+
+  ATH_MSG_DEBUG("in process()");
+
+  int nCells = cellcoll->nCellsCalo(m_caloIndex);
+  if (nCells <= 0) {
+    ATH_MSG_DEBUG("No TileCells in the container - nothing to do");
+    return StatusCode::SUCCESS;
+  }
+
+  // common-mode shift calculation
+  ATH_MSG_DEBUG("Calculating common-mode shift...");
+  int ncorr = this->calcCM(cellcoll);
+  if (ncorr <= 0) {
+    ATH_MSG_DEBUG( "Failed to calculate common-mode shift - no corrections applied");
+    return StatusCode::SUCCESS;
+  } else {
+    ATH_MSG_DEBUG("common-mode shift calculation ended");
+  }
+
+  size_t cellItr = cellcoll->indexFirstCellCalo(m_caloIndex);
+  size_t lastCell = cellcoll->indexLastCellCalo(m_caloIndex);
+
+  for (; cellItr != lastCell; ++cellItr) {
+    CaloCell* cell = (*cellcoll)[cellItr];
+    TileCell* tilecell = dynamic_cast<TileCell*>(cell);
+    if (tilecell == 0) continue;
+    if (tilecell->badcell()) continue;
+
+    setCMSEnergy(tilecell);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// ============================================================================
+// finalize
+StatusCode TileCellNoiseFilter::finalize() {
+  return StatusCode::SUCCESS;
+}
+
+// ============================================================================
+// correct energy
+void TileCellNoiseFilter::setCMSEnergy(TileCell* tilecell) {
+  //Identifier id  = tilecell->ID(); 
+  bool good1 = !tilecell->badch1();
+  bool good2 = !tilecell->badch2();
+  int gain1 = tilecell->gain1();
+  int gain2 = tilecell->gain2();
+  const CaloDetDescrElement * caloDDE = tilecell->caloDDE();
+  IdentifierHash hash1 = caloDDE->onl1();
+  IdentifierHash hash2 = caloDDE->onl2();
+
+  float e1 = tilecell->ene1();
+  float e2 = tilecell->ene2();
+
+  if (gain1 == TileID::HIGHGAIN && good1 && hash1 != TileHWID::NOT_VALID_HASH) {
+    HWIdentifier adc_id = m_tileHWID->adc_id(hash1, gain1);
+    int partition = m_tileHWID->ros(adc_id); // 1-4
+    int drawer = m_tileHWID->drawer(adc_id); // 0-63
+    int chan = m_tileHWID->channel(adc_id);  // 0-47
+    unsigned int drawerIdx = TileCalibUtils::getDrawerIdx(partition, drawer);
+    e1 -= m_tileToolEmscale->channelCalib(drawerIdx, chan, gain1,
+        this->getCMShift(partition - 1, drawer, chan),
+        TileRawChannelUnit::ADCcounts, TileRawChannelUnit::MegaElectronVolts);
+  }
+
+  if (gain2 == TileID::HIGHGAIN && good2 && hash2 != TileHWID::NOT_VALID_HASH) {
+    HWIdentifier adc_id = m_tileHWID->adc_id(hash2, gain2);
+    int partition = m_tileHWID->ros(adc_id); // 1-4
+    int drawer = m_tileHWID->drawer(adc_id); // 0-63
+    int chan = m_tileHWID->channel(adc_id);  // 0-47
+    unsigned int drawerIdx = TileCalibUtils::getDrawerIdx(partition, drawer);
+    e2 -= m_tileToolEmscale->channelCalib(drawerIdx, chan, gain2,
+        this->getCMShift(partition - 1, drawer, chan),
+        TileRawChannelUnit::ADCcounts, TileRawChannelUnit::MegaElectronVolts);
+  }
+
+  if ((good1 && good2)
+      || hash1 == TileHWID::NOT_VALID_HASH
+      || hash2 == TileHWID::NOT_VALID_HASH) {
+
+    // either both channels are good or there is only one channel in a cell
+    tilecell->setEnergy(e1, e2);
+  } else if (!good1 && good2) {
+    // first channel is bad, so second channel is used twice
+    tilecell->setEnergy(e2, e2);
+  } else if (good1 && !good2) {
+    // second channel is bad, so first channel is used twice
+    tilecell->setEnergy(e1, e1);
+  } else {
+    // both are bad - nothing to do
+    // but should not be here, because this function is not called for completely bad cells
+  }
+
+  return;
+}
+
+// ============================================================================
+// calculate correction
+int TileCellNoiseFilter::calcCM(const CaloCellContainer* cellcoll) {
+
+  bool useCaloNoise = (!m_useTileNoiseDB);
+
+  memset(m_commonmode, 0, sizeof(m_commonmode));
+  memset(m_nemptychan, 0, sizeof(m_nemptychan));
+  memset(m_ngoodchan, 0, sizeof(m_ngoodchan));
+
+  size_t cellItr = cellcoll->indexFirstCellCalo(m_caloIndex);
+  size_t lastCell = cellcoll->indexLastCellCalo(m_caloIndex);
+
+  for (; cellItr != lastCell; ++cellItr) {
+    const CaloCell* cell = (*cellcoll)[cellItr];
+    const TileCell* tilecell = dynamic_cast<const TileCell*>(cell);
+    if (tilecell == 0) continue;
+    if (tilecell->badcell()) continue;
+
+    float noise_sigma = 1.5, significance = 0.0;
+
+    if (useCaloNoise) {
+      if (m_useTwoGaussNoise) {
+        noise_sigma = m_noiseTool->getEffectiveSigma(cell
+            , ICalorimeterNoiseTool::MAXSYMMETRYHANDLING
+            , ICalorimeterNoiseTool::ELECTRONICNOISE);
+
+      } else {
+        noise_sigma = m_noiseTool->getNoise(cell, ICalorimeterNoiseTool::ELECTRONICNOISE);
+      }
+
+      significance = (noise_sigma != 0.0) ? fabs(cell->energy() / noise_sigma) : 999.999;
+
+      ATH_MSG_VERBOSE( "ID " << m_tileID->to_string(tilecell->ID())
+                      << " ene " << cell->energy()
+                      << " noise " << noise_sigma
+                      << " significance " << significance );
+    }
+
+    //Identifier id  = tilecell->ID(); 
+    bool good1 = !tilecell->badch1();
+    bool good2 = !tilecell->badch2();
+    int gain1 = tilecell->gain1();
+    int gain2 = tilecell->gain2();
+    const CaloDetDescrElement * caloDDE = tilecell->caloDDE();
+    IdentifierHash hash1 = caloDDE->onl1();
+    IdentifierHash hash2 = caloDDE->onl2();
+
+    if (good1 && hash1 != TileHWID::NOT_VALID_HASH) {
+      HWIdentifier adc_id = m_tileHWID->adc_id(hash1, gain1);
+      int partition = m_tileHWID->ros(adc_id); // 1-4
+      int drawer = m_tileHWID->drawer(adc_id); // 0-63
+      int chan = m_tileHWID->channel(adc_id); // 0-47
+      int mob = (int) (chan / maxChannel);
+
+      ++m_ngoodchan[partition - 1][drawer][mob];
+
+      if (gain1 == TileID::HIGHGAIN) {
+
+        unsigned int drawerIdx = TileCalibUtils::getDrawerIdx(partition, drawer);
+        float chanCalMeV = m_tileToolEmscale->channelCalib(drawerIdx, chan,
+            gain1, 1., TileRawChannelUnit::ADCcounts, TileRawChannelUnit::MegaElectronVolts);
+
+        float amp = tilecell->ene1() / chanCalMeV;
+        if (amp != 0.0) { // second iteration (in case there is non-linear correction)
+          chanCalMeV = m_tileToolEmscale->channelCalib(drawerIdx, chan, gain1,
+              amp, TileRawChannelUnit::ADCcounts, TileRawChannelUnit::MegaElectronVolts) / amp;
+
+          amp = tilecell->ene1() / chanCalMeV;
+        }
+
+        if (m_useTileNoiseDB) {
+          if (m_useTwoGaussNoise) {
+            // nothing for the moment - keep 1.5 ADC counts
+          } else {
+            noise_sigma = m_tileToolNoiseSample->getHfn(drawerIdx, chan, gain1);
+          }
+
+          significance = (noise_sigma != 0.0) ? fabs(amp / noise_sigma) : 999.999;
+          ATH_MSG_VERBOSE( "HWID " << m_tileHWID->to_string(adc_id)
+                          << " calib " << chanCalMeV
+                          << " amp " << amp
+                          << " noise " << noise_sigma
+                          << " significance " << significance );
+        }
+
+        // use only empty channels with less significance
+        if (significance <= m_truncationThresholdOnAbsEinSigma) {
+          m_commonmode[partition - 1][drawer][mob] += amp;
+          m_nemptychan[partition - 1][drawer][mob]++;
+        }
+      }
+    }
+
+    if (good2 && hash2 != TileHWID::NOT_VALID_HASH) {
+      HWIdentifier adc_id = m_tileHWID->adc_id(hash2, gain2);
+      int partition = m_tileHWID->ros(adc_id); // 1-4
+      int drawer = m_tileHWID->drawer(adc_id); // 0-63
+      int chan = m_tileHWID->channel(adc_id); // 0-47
+      int mob = (int) (chan / maxChannel);
+
+      ++m_ngoodchan[partition - 1][drawer][mob];
+
+      if (gain2 == TileID::HIGHGAIN) {
+
+        unsigned int drawerIdx = TileCalibUtils::getDrawerIdx(partition, drawer);
+        float chanCalMeV = m_tileToolEmscale->channelCalib(drawerIdx, chan
+              , gain2, 1., TileRawChannelUnit::ADCcounts, TileRawChannelUnit::MegaElectronVolts);
+
+        float amp = tilecell->ene2() / chanCalMeV;
+        if (amp != 0.0) { // second iteration (in case there is non-linear correction)
+          chanCalMeV = m_tileToolEmscale->channelCalib(drawerIdx, chan, gain2,
+              amp, TileRawChannelUnit::ADCcounts, TileRawChannelUnit::MegaElectronVolts) / amp;
+
+          amp = tilecell->ene2() / chanCalMeV;
+        }
+
+        if (m_useTileNoiseDB) {
+          if (m_useTwoGaussNoise) {
+            // nothing for the moment - keep 1.5 ADC counts
+          } else {
+            noise_sigma = m_tileToolNoiseSample->getHfn(drawerIdx, chan, gain2);
+          }
+          // use only empty channels with less significance
+          significance = (noise_sigma != 0.0) ? fabs(amp / noise_sigma) : 999.999;
+          ATH_MSG_VERBOSE( "HWID " << m_tileHWID->to_string(adc_id)
+                          << " calib " << chanCalMeV
+                          << " amp " << amp
+                          << " noise " << noise_sigma
+                          << " significance " << significance );
+        }
+
+        // use only empty channels with less significance
+        if (significance <= m_truncationThresholdOnAbsEinSigma) {
+          m_commonmode[partition - 1][drawer][mob] += amp;
+          m_nemptychan[partition - 1][drawer][mob]++;
+        }
+      }
+    }
+  }
+
+  int ncorr = 0;
+  int nchmin = m_minimumNumberOfTruncatedChannels;
+
+  for (int partition = 0; partition < maxPartition; partition++) {
+    for (int drawer = 0; drawer < maxDrawer; drawer++) {
+      for (int mob = 0; mob < maxMOB; mob++) {
+
+        if (m_minimumNumberOfTruncatedChannels < 1.0) {
+          nchmin = ceil(m_minimumNumberOfTruncatedChannels
+                        * m_ngoodchan[partition][drawer][mob]);
+          if (nchmin < 2) nchmin = 2;
+        }
+
+        if (m_nemptychan[partition][drawer][mob] >= nchmin) {
+          m_commonmode[partition][drawer][mob] /= m_nemptychan[partition][drawer][mob];
+          ++ncorr;
+          ATH_MSG_VERBOSE( "ros " << partition + 1 << std::setw(2)
+                          << " drawer " << std::setw(2) << drawer
+                          << " mb " << mob
+                          << " mean " << m_commonmode[partition][drawer][mob]
+                          << " taken from " << m_nemptychan[partition][drawer][mob] << " channels"
+                          << " nchgood " << m_ngoodchan[partition][drawer][mob]
+                          << " nchmin " << nchmin );
+
+        } else {
+          if (msgLvl(MSG::VERBOSE)) {
+            if (m_commonmode[partition][drawer][mob] != 0.0) {
+              msg(MSG::VERBOSE) << "ros " << partition + 1 << std::setw(2)
+                                << " drawer " << std::setw(2) << drawer
+                                << " mb " << mob
+                                << " mean is zero instead of " << m_commonmode[partition][drawer][mob]
+                                << " / " << m_nemptychan[partition][drawer][mob]
+                                << " nchgood " << m_ngoodchan[partition][drawer][mob]
+                                << " nchmin " << nchmin
+                                << endmsg;
+            } else {
+              msg(MSG::VERBOSE) << "ros " << partition + 1
+                                << " drawer " << std::setw(2) << drawer
+                                << " mb " << mob
+                                << " mean is zero - nothing to correct"
+                                << " nchgood " << m_ngoodchan[partition][drawer][mob]
+                                << " nchmin " << nchmin
+                                << endmsg;
+            }
+          }
+          m_commonmode[partition][drawer][mob] = 0.0;
+        }
+      }
+    }
+  }
+
+  return ncorr;
+}
+
diff --git a/TileCalorimeter/TileRecUtils/src/TileCorrelation.cxx b/TileCalorimeter/TileRecUtils/src/TileCorrelation.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..72084c999242ce8fb78e582884a57d01012ef113
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileCorrelation.cxx
@@ -0,0 +1,880 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+////////////////////////////////////////
+//
+// Filename : TileCorrelation.cxx
+//
+//  Author   : Valencia TileCal group, cristobal.cuenca@cern.ch,
+//  Mantained by Ximo Poveda, jpoveda@cern.ch
+// 
+//  Created  : May, 2004
+//  Moved to TileRecUtils on Jan'05
+//
+////////////////////////////////////////
+
+
+#include "TileRecUtils/TileCorrelation.h"
+#include "PathResolver/PathResolver.h"
+#include "TileConditions/TileInfo.h"
+#include "TileIdentifier/TileHWID.h"
+
+#include "CLHEP/Matrix/Matrix.h"
+//#include "TileConditions/TilePulseShapes.h"
+
+#include <cmath>
+
+using CLHEP::HepMatrix;
+
+
+////////////////////////////////////////
+TileCorrelation::TileCorrelation()
+  : SS()
+  , S()
+  , R()
+  , corr()
+  , corr_sum()
+  , corr_sum_sq()
+  , ncorr(0.0)
+  , N()
+  , jentry(0) 
+  , lag(0)
+  , N_pairs()
+  , N_d(0.0)
+  , S1()
+  , S2()
+  , S11()
+  , S12()
+  , S22()
+{
+}
+
+
+////////////////////////////////////////
+TileCorrelation::~TileCorrelation()
+{
+}
+
+
+////////////////////////////////////////
+void TileCorrelation::SetCorrelationZero(MsgStream & log, int dignum)
+{
+  if (log.level()<=MSG::DEBUG)
+    log<<MSG::DEBUG<<"TileCorrelation::SetCorrelationZero(log)"<<endreq;
+  for (int ros=0;ros<4;ros++)
+    for (int drawer=0;drawer<64;drawer++)
+      for (int channel=0;channel<48;channel++)
+	for (int gain=0;gain<2;gain++){
+	  N[ros][drawer][channel][gain]=0;
+	  //N[ros][drawer][channel][gain]=0;
+	  for (int i=0;i<dignum;i++){
+	    S[ros][drawer][channel][gain][i]=0.;
+	    for (int j=0;j<dignum;j++){
+	      SS[ros][drawer][channel][gain][i][j]=0.;
+	      R[ros][drawer][channel][gain][i][j]=0.;	  
+	    }
+	  }
+	  
+	  for (lag=0;lag<9;lag++){
+		S1[ros][drawer][channel][gain][lag]=0.;
+		S2[ros][drawer][channel][gain][lag]=0.;
+		S11[ros][drawer][channel][gain][lag]=0.;
+		S12[ros][drawer][channel][gain][lag]=0.;
+		S22[ros][drawer][channel][gain][lag]=0.;
+		N_pairs[ros][drawer][channel][gain][lag]=0;
+		corr_sum[ros][drawer][channel][gain][lag]=0.;
+		corr_sum_sq[ros][drawer][channel][gain][lag]=0.;
+	  }
+	}
+
+  for (lag=0;lag<9;lag++){
+    corr[lag]=0.;
+  }
+  
+}
+
+
+////////////////////////////////////////
+void TileCorrelation::SetCorrelationDelta(MsgStream & log, int dignum)
+{
+  if (log.level()<=MSG::DEBUG)
+    log<<MSG::DEBUG<<"TileCorrelation::SetCorrelationDelta(log)"<<endreq;
+
+  for (int ros=0;ros<4;ros++)
+    for (int drawer=0;drawer<64;drawer++)
+      for (int channel=0;channel<48;channel++)
+	for (int gain=0;gain<2;gain++){
+	  N[ros][drawer][channel][gain]=1;
+	  for (int i=0;i<dignum;i++)    
+	    for (int j=0;j<dignum;j++)
+	      if (i==j) R[ros][drawer][channel][gain][i][j]=1.;	  
+		else R[ros][drawer][channel][gain][i][j]=0.;	   
+	}
+}
+
+
+
+////////////////////////////////////////
+void TileCorrelation::Sum(vector<double> &digits, int ros, int drawer, int channel, int gain, MsgStream & log, bool m_debug, int &dignum)
+{
+  if (log.level()<=MSG::VERBOSE)
+    log<<MSG::VERBOSE<<"TileCorrelation::Sum"<<endreq;
+
+  double N_d=0.;
+  dignum=digits.size();
+  
+  N[ros][drawer][channel][gain]++;
+  N_d=double(N[ros][drawer][channel][gain]);
+  for (int i=0;i<dignum;i++){
+    S[ros][drawer][channel][gain][i]+=digits[i];	  
+    for (int j=0;j<dignum;j++) SS[ros][drawer][channel][gain][i][j]+=digits[i]*digits[j];
+    //for (int j=0;j<i+1;j++) SS[ros][drawer][channel][gain][i][j]+=digits[i]*digits[j];
+  }
+  
+  if (m_debug){
+    cout 
+      <<" TileCorrelation::Sum, ros="<<ros
+      <<" drawer="<<drawer
+      <<" channel="<<channel
+      <<" gain="<<gain
+      <<" N="<<N[ros][drawer][channel][gain]
+      <<" Sum[1]="<<S[ros][drawer][channel][gain][1] 
+      <<" Sum[2]="<<S[ros][drawer][channel][gain][2] 
+      <<" Sum[1][2]="<<SS[ros][drawer][channel][gain][1][2] 
+      <<" Sum[1][1]="<<SS[ros][drawer][channel][gain][1][1] 
+      <<" Sum[2][2]="<<SS[ros][drawer][channel][gain][2][2] 
+      <<" B[1][2]="<<SS[ros][drawer][channel][gain][1][2]/N_d-S[ros][drawer][channel][gain][1]/N_d*S[ros][drawer][channel][gain][2]/N_d
+      <<" Correlation[1][2]="<<(N_d*SS[ros][drawer][channel][gain][1][2]-S[ros][drawer][channel][gain][1]*S[ros][drawer][channel][gain][2])/sqrt((N_d*SS[ros][drawer][channel][gain][1][1]-S[ros][drawer][channel][gain][1]*S[ros][drawer][channel][gain][1])*(N_d*SS[ros][drawer][channel][gain][2][2]-S[ros][drawer][channel][gain][2]*S[ros][drawer][channel][gain][2]))
+      <<endl;
+  }
+  
+}
+
+
+
+////////////////////////////////////////
+void TileCorrelation::CalcCorrelation(MsgStream & log, int dignum)
+{
+  if (log.level()<=MSG::DEBUG)
+    log<<MSG::DEBUG<<"TileCorrelation::CalcCorrelation"<<endreq;
+  
+  for (int ros=0;ros<4;ros++)
+    for (int drawer=0;drawer<64;drawer++)
+      for (int channel=0;channel<48;channel++)
+	for (int gain=0;gain<2;gain++){
+	  double N_d=double(N[ros][drawer][channel][gain]);
+	  if (N_d>0.) cout<<" TileCorrelation::CalcCorrelation, ros="<<ros<<" drawer="<<drawer<<" channel="<<channel<<" gain="<<gain<<" N_d="<<N_d<<endl;
+	  for (int i=0;i<dignum;i++)    
+	      //	      for (int j=0;j<i+1;j++)
+	    for (int j=0;j<dignum;j++){
+	      if (N_d>0.){
+		//		      cout<<"b  i="<<i<<" j="<<j<<endl
+		// 			  <<" R[ros][drawer][channel][gain][i][j]="<<R[ros][drawer][channel][gain][i][j]<<endl
+		// 			  <<" N_d="<<N_d<<endl			  
+		// 			  <<" S[ros][drawer][channel][gain][i]="<<S[ros][drawer][channel][gain][i]<<endl
+		// 			  <<" S[ros][drawer][channel][gain][j]="<<S[ros][drawer][channel][gain][j]<<endl
+		// 			  <<" SS[ros][drawer][channel][gain][i][j]="<<SS[ros][drawer][channel][gain][i][j]<<endl
+		// 			  <<" SS[ros][drawer][channel][gain][i][i]="<<SS[ros][drawer][channel][gain][i][i]<<endl
+		// 			  <<" SS[ros][drawer][channel][gain][j][j]="<<SS[ros][drawer][channel][gain][j][j]<<endl
+		//<<" S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][j]="<<S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][j]<<endl
+		//<<" N_d*SS[ros][drawer][channel][gain][i][j]="<<N_d*SS[ros][drawer][channel][gain][i][j]<<endl
+		// 			  <<" N_d*SS[ros][drawer][channel][gain][i][j]-S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][j]="<<N_d*SS[ros][drawer][channel][gain][i][j]-S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][j]<<endl
+		// 			  <<" N_d*SS[ros][drawer][channel][gain][j][j]-S[ros][drawer][channel][gain][j]*S[ros][drawer][channel][gain][j])="<<N_d*SS[ros][drawer][channel][gain][j][j]-S[ros][drawer][channel][gain][j]*S[ros][drawer][channel][gain][j]<<endl
+		// 			  <<" N_d*SS[ros][drawer][channel][gain][i][i]-S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][i]="<<N_d*SS[ros][drawer][channel][gain][i][i]-S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][i]<<endl
+		// 			  <<" sqrt((N_d*SS[ros][drawer][channel][gain][i][i]-S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][i])*(N_d*SS[ros][drawer][channel][gain][j][j]-S[ros][drawer][channel][gain][j]*S[ros][drawer][channel][gain][j]))="<< sqrt((N_d*SS[ros][drawer][channel][gain][i][i]-S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][i])*(N_d*SS[ros][drawer][channel][gain][j][j]-S[ros][drawer][channel][gain][j]*S[ros][drawer][channel][gain][j]))
+		//			  <<endl;																								   
+		R[ros][drawer][channel][gain][i][j]=
+		  (N_d*SS[ros][drawer][channel][gain][i][j]-S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][j])
+		  /sqrt(
+			(N_d*SS[ros][drawer][channel][gain][i][i]-S[ros][drawer][channel][gain][i]*S[ros][drawer][channel][gain][i])*
+			(N_d*SS[ros][drawer][channel][gain][j][j]-S[ros][drawer][channel][gain][j]*S[ros][drawer][channel][gain][j]));
+		//		      cout<<"R[ros][drawer][channel][gain][i][j]="<<R[ros][drawer][channel][gain][i][j]<<endl;
+	      }
+	      else R[ros][drawer][channel][gain][i][j]=-1234.;
+	    }
+	}
+}
+
+
+
+////////////////////////////////////////
+void TileCorrelation::RunningCorrelation(vector<double> &digits, int ros, int drawer, int channel, int gain, MsgStream & log, bool /* m_debug */, int &dignum, int chthres)
+{
+  MSG::Level logLevel = log.level();
+  bool lDebug = (logLevel<=MSG::DEBUG);
+  bool lVerbose = (logLevel<=MSG::VERBOSE);
+  if (lVerbose)
+    log<<MSG::VERBOSE<<"TileCorrelation::RunningCorrelation"<<endreq;
+  dignum=digits.size();
+
+  //chthres=10;
+  //update sums
+  N[ros][drawer][channel][gain]++;
+  jentry=N[ros][drawer][channel][gain];
+  N_d=double(jentry);
+
+  if (ros==1 && drawer==1 && channel==0 && gain==1)
+    log<<MSG::INFO<<"Computing RunningCorrelation for jentry="<<jentry<<endreq;
+
+  for (lag=1;lag<dignum;lag++){
+    for (int i=0; i<dignum-lag; i++){	            
+      S1[ros][drawer][channel][gain][lag-1]+=digits[i];
+      S2[ros][drawer][channel][gain][lag-1]+=digits[i+lag];
+      S12[ros][drawer][channel][gain][lag-1]+=digits[i]*digits[i+lag];
+      S11[ros][drawer][channel][gain][lag-1]+=digits[i]*digits[i];
+      S22[ros][drawer][channel][gain][lag-1]+=digits[i+lag]*digits[i+lag];
+      N_pairs[ros][drawer][channel][gain][lag-1]++;	
+    }
+    if (lag==1 && ros==1 && drawer==1 && channel==0 && gain==1)
+      if (lVerbose)
+        log<<MSG::VERBOSE<<" jentry="<<jentry<<" N="<<N_pairs[ros][drawer][channel][gain][lag-1]<<" S1="<<S1[ros][drawer][channel][gain][lag-1]<<endreq;
+    
+    
+    if (jentry>chthres){
+      ncorr=double(N_pairs[ros][drawer][channel][gain][lag-1]);
+      corr[lag-1]=
+	(ncorr*S12[ros][drawer][channel][gain][lag-1]-
+	 S1[ros][drawer][channel][gain][lag-1]*S2[ros][drawer][channel][gain][lag-1])/
+	sqrt(
+	     (ncorr*S11[ros][drawer][channel][gain][lag-1]-
+	      S1[ros][drawer][channel][gain][lag-1]*S1[ros][drawer][channel][gain][lag-1])*
+	     (ncorr*S22[ros][drawer][channel][gain][lag-1]-
+	      S2[ros][drawer][channel][gain][lag-1]*S2[ros][drawer][channel][gain][lag-1]));
+	  
+      if (lag==1 && ros==1 && drawer==1 && channel==0 && gain==1)
+        if (lDebug)
+          log<<MSG::DEBUG
+             <<" corr="<<corr[lag-1]
+             <<" corr_sum="<<corr_sum[ros][drawer][channel][gain][lag-1]
+             <<" corr_sum_sq="<<corr_sum_sq[ros][drawer][channel][gain][lag-1]
+             <<endreq;
+
+      corr_sum[ros][drawer][channel][gain][lag-1]+=corr[lag-1];
+      corr_sum_sq[ros][drawer][channel][gain][lag-1]+=corr[lag-1]*corr[lag-1];
+      //  	  corr_mean=corr_sum[lag-1]/(chthres-jentry);
+      //  	  corr_RMS=sqrt(corr_sum_sq[lag-1]*(chthres-jentry)-corr_sum[lag-1]*corr_sum[lag-1])/(chthres-jentry)
+      
+      
+      if (lag==1 && ros==1 && drawer==1 && channel==0 && gain==1)
+        if (lDebug)
+          log<<MSG::DEBUG
+             <<" jentry="<<jentry<<" jentry-chthres="<<jentry-chthres<<" lag=1, ros=1, drawer=1, channel=0, gain=1"
+             <<" corr="<<corr[lag-1]
+	  // 	       <<" corr_mean="<<corr_sum[lag-1]/(jentry-chthres)
+             <<" sum corr_mean="<<corr_sum[ros][drawer][channel][gain][lag-1]
+             <<" corr_mean="<<corr_sum[ros][drawer][channel][gain][lag-1]/(jentry-chthres)
+	  // 	       <<" RMS="<<sqrt(corr_sum_sq[lag-1]*(jentry-chthres)-corr_sum[lag-1]*corr_sum[lag-1])/(jentry-chthres)
+             <<" sum RMS="<<corr_sum_sq[ros][drawer][channel][gain][lag-1]
+             <<" RMS="<<sqrt(corr_sum_sq[ros][drawer][channel][gain][lag-1]*(jentry-chthres)
+			   -corr_sum[ros][drawer][channel][gain][lag-1]*corr_sum[ros][drawer][channel][gain][lag-1])/(jentry-chthres)
+             <<endreq;
+    }
+  }
+}
+
+
+
+////////////////////////////////////////
+void TileCorrelation::CalcRunningCorrelation(MsgStream & log, int dignum, int chthres, bool m_7to9)
+{
+  if (log.level()<=MSG::VERBOSE)
+    log<<MSG::VERBOSE<<"TileCorrelation::CalcRunningCorrelation"<<endreq;
+
+  for (int ros=0;ros<4;ros++)
+    for (int drawer=0;drawer<64;drawer++)
+      for (int channel=0;channel<48;channel++)
+	for (int gain=0;gain<2;gain++){
+	  jentry=N[ros][drawer][channel][gain];
+	  ncorr=double(jentry-chthres);
+	  
+	  if (jentry>0){
+	    if (m_7to9 && dignum==7){
+	      for (int i=0;i<9;i++)
+		R[ros][drawer][channel][gain][i][i]=1.;
+	      
+	      for (lag=1;lag<9;lag++)
+		for (int i=0;i<9-lag;i++){
+		  if (lag<7){
+		    R[ros][drawer][channel][gain][i][i+lag]=corr_sum[ros][drawer][channel][gain][lag-1]/ncorr;
+		    R[ros][drawer][channel][gain][i+lag][i]=corr_sum[ros][drawer][channel][gain][lag-1]/ncorr;
+		  }else{
+		    R[ros][drawer][channel][gain][i][i+lag]=0.;
+		    R[ros][drawer][channel][gain][i+lag][i]=0.;
+		  }
+		  
+		  if (-1.>R[ros][drawer][channel][gain][i][i+lag] || R[ros][drawer][channel][gain][i][i+lag]>1.)
+		    R[ros][drawer][channel][gain][i][i+lag]=0.;
+		  if (-1.>R[ros][drawer][channel][gain][i+lag][i] || R[ros][drawer][channel][gain][i+lag][i]>1.)
+		    R[ros][drawer][channel][gain][i+lag][i]=0.;
+		  
+		}
+	    }else{
+	      for (int i=0;i<dignum;i++)
+		R[ros][drawer][channel][gain][i][i]=1.;
+	      
+	      for (lag=1;lag<dignum;lag++)
+		for (int i=0;i<dignum-lag;i++){
+		  R[ros][drawer][channel][gain][i][i+lag]=corr_sum[ros][drawer][channel][gain][lag-1]/ncorr;
+		  R[ros][drawer][channel][gain][i+lag][i]=corr_sum[ros][drawer][channel][gain][lag-1]/ncorr;
+		  if (-1.>R[ros][drawer][channel][gain][i][i+lag] || R[ros][drawer][channel][gain][i][i+lag]>1.)
+		    R[ros][drawer][channel][gain][i][i+lag]=0.;
+		  if (-1.>R[ros][drawer][channel][gain][i+lag][i] || R[ros][drawer][channel][gain][i+lag][i]>1.)
+		    R[ros][drawer][channel][gain][i+lag][i]=0.;
+		}
+	    }
+	  }
+	}
+}
+
+
+
+////////////////////////////////////////
+void TileCorrelation::PrintCorrelation(int dignum)
+{
+
+  cout<<" TileCorrelation::PrintCorrelation()..."<<endl;
+  for (int ros=0;ros<1;ros++){
+      cout<<" ros="<<ros<<endl;
+      for (int drawer=31;drawer<32;drawer++){
+	  cout<<" drawer="<<drawer<<endl;
+	  for (int channel=17;channel<24;channel++){
+	      cout<<" channel="<<channel<<endl;
+	      for (int gain=0;gain<2;gain++){
+		  cout<<" gain="<<gain<<endl;
+		  for (int i=0;i<dignum;i++){		      
+		      for (int j=0;j<dignum;j++){
+			  cout<<" "<<R[ros][drawer][channel][gain][i][j];
+		      }
+		      cout<<endl;
+		  }
+		  cout<<endl;
+	      }
+	  }
+      }
+  }
+  
+}
+
+
+
+
+////////////////////////////////////////
+void TileCorrelation::SaveCorrelationSumm(bool m_deltaCorrelation, 
+					  string m_OptFilterFile_CorrelationSumm,
+					  const TileHWID *m_tileHWID,
+					  MsgStream & log,
+					  int dignum)
+{
+  MSG::Level logLevel = log.level();
+  bool lDebug = (logLevel<=MSG::DEBUG);
+  bool lVerbose = (logLevel<=MSG::VERBOSE);
+  if (lDebug)
+    log<<MSG::DEBUG<<" TileCorrelation::SaveCorrelationSumm"<<endreq;
+  
+  HepMatrix M_correlation(dignum,1,0);
+
+  fstream *f_correlation = new fstream(m_OptFilterFile_CorrelationSumm.c_str(),fstream::out);
+  if (f_correlation->is_open()) log<<MSG::INFO<<m_OptFilterFile_CorrelationSumm<<" file open"<<endreq;
+
+  if (m_deltaCorrelation){
+      //      for (int i=0;i<dignum;i++)
+    for (int j=0;j<dignum;j++){
+      int i=0;
+      if (R[0][0][0][0][i][j]>-100000. &&  R[0][0][0][0][i][j]<100000.)
+	M_correlation[i][j]=R[0][0][0][0][i][j];
+      else
+	M_correlation[i][j]=0.0;
+    }
+    
+    
+    *f_correlation<<M_correlation.T()<<endl;
+  }else{
+      for (int ros=0;ros<4;ros++)    
+	for (int drawer=0;drawer<64;drawer++){
+	  int frag= m_tileHWID->frag(ros+1,drawer);	    
+	  for (int channel=0;channel<48;channel++)
+	    for (int gain=0;gain<2;gain++){
+              if (lVerbose)
+                log<<MSG::VERBOSE
+                   <<"ros "<<ros
+                   <<"  drawer "<<drawer<<MSG::hex
+                   <<"  frag0x "<<frag<<MSG::dec
+                   <<"  channel "<<channel
+                   <<"  gain "<<gain
+                   <<"  N "<<N[ros][drawer][channel][gain]
+                   <<endreq;
+
+	      if (N[ros][drawer][channel][gain]>0){
+		//for (int i=0;i<dignum;i++)
+		for (int j=0;j<dignum;j++){
+		  int i=0;
+		  if (R[ros][drawer][channel][gain][i][j]>-100000. 
+		      &&  R[ros][drawer][channel][gain][i][j]<100000.)
+		    M_correlation[i][j]=R[ros][drawer][channel][gain][i][j];
+		  else
+		    M_correlation[i][j]=0.0;
+		}
+		
+		
+		
+		*f_correlation<<"ros "<<ros
+			      <<"  drawer "<<drawer<<std::hex
+			      <<"  frag0x "<<frag<<std::dec
+			      <<"  channel "<<channel
+			      <<"  gain "<<gain
+			      <<"  N "<<N[ros][drawer][channel][gain]
+			      <<M_correlation.T();
+		//				    <<M_correlation.T()<<endl;
+		// 		for (int i=0;i<dignum;i++)
+		// 		  for (int j=0;j<dignum;j++)				    
+		// 		      *f_correlation<<R[ros][drawer][channel][gain][i][j]<< M_correlation[i][j]<<endl;
+		
+	      }
+	    }	  
+	}    
+  }
+  f_correlation->close();  
+}
+
+
+////////////////////////////////////////
+void TileCorrelation::SaveCorrelationMatrix(bool m_deltaCorrelation, 
+					    string m_OptFilterFile_CorrelationMatrix,
+					    const TileHWID *m_tileHWID,
+					    MsgStream & log,
+					    int dignum)
+{
+  MSG::Level logLevel = log.level();
+  bool lDebug = (logLevel<=MSG::DEBUG);
+  bool lVerbose = (logLevel<=MSG::VERBOSE);
+  if (lDebug)
+    log<<MSG::DEBUG<<" TileCorrelation::SaveCorrelationMatrix"<<endreq;
+  
+  HepMatrix M_correlation(dignum,dignum,0);
+  
+  fstream *f_correlation = new fstream(m_OptFilterFile_CorrelationMatrix.c_str(),fstream::out);
+  if (f_correlation->is_open()) log<<MSG::INFO<<m_OptFilterFile_CorrelationMatrix<<" file open"<<endreq;
+
+  if (m_deltaCorrelation){
+    for (int i=0;i<dignum;i++)
+      for (int j=0;j<dignum;j++){
+	if (R[0][0][0][0][i][j]>-100000. &&  R[0][0][0][0][i][j]<100000.)
+	  M_correlation[i][j]=R[0][0][0][0][i][j];
+	else
+	  M_correlation[i][j]=0.0;
+      }
+    
+    
+    *f_correlation<<M_correlation<<endl;
+  }else{
+    for (int ros=0;ros<4;ros++)    
+      for (int drawer=0;drawer<64;drawer++){
+	int frag= m_tileHWID->frag(ros+1,drawer);	    
+	for (int channel=0;channel<48;channel++)
+	  for (int gain=0;gain<2;gain++){
+            if (lVerbose)
+              log<<MSG::VERBOSE
+                 <<"ros "<<ros
+                 <<"  drawer "<<drawer<<MSG::hex
+                 <<"  frag0x "<<frag<<MSG::dec
+                 <<"  channel "<<channel
+                 <<"  gain "<<gain
+                 <<"  N "<<N[ros][drawer][channel][gain]
+                 <<endreq;
+	    
+	    if (N[ros][drawer][channel][gain]>0){
+	      for (int i=0;i<dignum;i++)
+		for (int j=0;j<dignum;j++){
+		  if (R[ros][drawer][channel][gain][i][j]>-100000.
+		      &&  R[ros][drawer][channel][gain][i][j]<100000.)
+		    M_correlation[i][j]=R[ros][drawer][channel][gain][i][j];
+		  else
+		    M_correlation[i][j]=0.0;
+		}
+	      
+	      
+	      
+	      *f_correlation<<"ros "<<ros
+			    <<"  drawer "<<drawer<<std::hex
+			    <<"  frag0x "<<frag<<std::dec
+			    <<"  channel "<<channel
+			    <<"  gain "<<gain
+			    <<"  N "<<N[ros][drawer][channel][gain]
+			    <<M_correlation<<endl;
+	      // 		for (int i=0;i<dignum;i++)
+	      // 		  for (int j=0;j<dignum;j++)				    
+	      // 		      *f_correlation<<R[ros][drawer][channel][gain][i][j]<< M_correlation[i][j]<<endl;
+	      
+	    }
+	  }	  
+      }    
+  }
+  f_correlation->close();  
+}
+
+
+
+////////////////////////////////////////
+void TileCorrelation::CalcWeights(bool m_deltaCorrelation,
+				  vector<double> m_LshapeForm,
+				  vector<double> m_HshapeForm,
+				  vector<double> m_LdshapeForm,
+				  vector<double> m_HdshapeForm,
+				  string m_OptFilterFile_ai_lo,
+				  string m_OptFilterFile_bi_lo,
+				  string m_OptFilterFile_ai_hi,
+				  string m_OptFilterFile_bi_hi,
+				  const TileHWID *m_tileHWID,
+				  MsgStream & log,
+				  int dignum)
+{
+  MSG::Level logLevel = log.level();
+  bool lDebug = (logLevel<=MSG::DEBUG);
+  bool lVerbose = (logLevel<=MSG::VERBOSE);
+  if (lDebug)
+    log<<MSG::DEBUG<<" TileCorrelation::CalcWeights"<<endreq;
+  
+  HepMatrix Correlation(dignum,dignum,0), Inverse(dignum,dignum,0), Zero(dignum,dignum,0);
+  HepMatrix PulseShape(dignum,1,0), DPulseShape(dignum,1,0);
+  HepMatrix a(dignum,1,0), b(dignum,1,0);
+  
+  fstream *f_ai_lo = new fstream(m_OptFilterFile_ai_lo.c_str(), fstream::out);
+  fstream *f_bi_lo = new fstream(m_OptFilterFile_bi_lo.c_str(), fstream::out);
+  fstream *f_ai_hi = new fstream(m_OptFilterFile_ai_hi.c_str(), fstream::out);
+  fstream *f_bi_hi = new fstream(m_OptFilterFile_bi_hi.c_str(), fstream::out);
+
+
+  //Open Weights files
+  if (f_ai_lo->is_open()&&f_ai_lo->is_open()&&f_ai_lo->is_open()&&f_ai_lo->is_open()) log<<MSG::INFO<<" Weights files open"<<endreq;
+  else log<<MSG::INFO<<" Weights files didn't open succesfully"<<endreq;
+
+
+  //pulse shape
+  //  vector<double> new_shapeForm;
+  //   double max=0.;
+  //   int nmax=0;
+  
+  //   for (int i=0; i<int(m_shapeForm.size());i++)
+  //     if (m_shapeForm[i]>max)
+  //       {
+  // 	max=m_shapeForm[i];
+  // 	nmax=i;
+  //       }
+  //   new_shapeForm.resize(dignum*25,0.);
+
+  //   if (lDebug)
+  //     log<<MSG::DEBUG<<"m_shapeForm.size()="<<m_shapeForm.size()<<"m_shapeForm nmax="<<nmax<<" new_shapeForm.size()="<<new_shapeForm.size()<<" new_shapeForm nmax="<<dignum*25/2<<endreq; 
+  
+  //   for (int i=0;i<dignum*25;i++)
+  //     {
+  //       if (i<(dignum*25/2-nmax)) new_shapeForm[i]=0.;
+  //       if (i>=(dignum*25/2-nmax) && i<(dignum*25/2-nmax+int(m_shapeForm.size()))) new_shapeForm[i]=m_shapeForm[i-dignum*25/2 +nmax];
+  //       if (i>=(dignum*25/2-nmax+int(m_shapeForm.size()))) new_shapeForm[i]=0.;
+  //     }
+  
+  if (lVerbose) {
+    log<<MSG::VERBOSE<<"m_LshapeForm, m_LdshapeForm, m_HshapeForm, m_HdshapeForm"<<endreq;
+    for(int i=0;i<int(m_LshapeForm.size());i++) log<<MSG::VERBOSE<<i<<" "
+                                                   <<std::setw(18)<<std::setprecision(10)<<m_LshapeForm[i]<<" "
+                                                   <<std::setw(18)<<std::setprecision(10)<<m_LdshapeForm[i]<<" "
+                                                   <<std::setw(18)<<std::setprecision(10)<<m_HshapeForm[i]<<" "
+                                                   <<std::setw(18)<<std::setprecision(10)<<m_HdshapeForm[i]<<" "
+                                                   <<endreq;
+  }
+  //if (lDebug) {
+  //   log<<MSG::DEBUG<<"m_HshapeForm"<<endreq;
+  //   for(int i=0;i<int(m_HshapeForm.size());i++) log<<MSG::DEBUG<<"    "<<i<<" "<<m_HshapeForm[i]<<endreq;
+  //   log<<MSG::DEBUG<<"m_LdshapeForm"<<endreq;
+  //   for(int i=0;i<int(m_LdshapeForm.size());i++) log<<MSG::DEBUG<<"    "<<i<<" "<<m_LdshapeForm[i]<<endreq;
+  //   log<<MSG::DEBUG<<"m_HdshapeForm"<<endreq;
+  //   for(int i=0;i<int(m_HdshapeForm.size());i++) log<<MSG::DEBUG<<"    "<<i<<" "<<m_HdshapeForm[i]<<endreq;
+  //}
+
+  double Q1, Q2, Q3, Delta;
+  int ierr=0;
+  
+  if (m_deltaCorrelation){
+    for (int gain=0;gain<2;gain++)	  
+      for (int pha=-12;pha<13;pha++){
+	Correlation=Zero;
+	Inverse=Zero;
+	
+	for (int i=0;i<dignum;i++)    
+	  for (int j=0;j<dignum;j++)
+	    Correlation[i][j]=R[0][0][0][0][i][j];
+	
+	Inverse=Correlation.inverse(ierr);
+	if (ierr==0){		
+	  for (int i=0;i<dignum;i++){
+	    if (gain==0){
+	      PulseShape[i][0]=m_LshapeForm[i*25+12+pha];
+	      DPulseShape[i][0]=m_LdshapeForm[i*25+12+pha];
+	    }else{
+	      PulseShape[i][0]=m_HshapeForm[i*25+12+pha];
+	      DPulseShape[i][0]=m_HdshapeForm[i*25+12+pha];
+	    }
+	  }			
+	  
+	  Q1=((PulseShape.T())*Inverse*PulseShape).determinant();
+	  Q2=((DPulseShape.T())*Inverse*DPulseShape).determinant();
+	  Q3=((DPulseShape.T())*Inverse*PulseShape).determinant();
+	  Delta=Q1*Q2-Q3*Q3;
+	  
+	  a=Q2/Delta*Inverse*PulseShape-Q3/Delta*Inverse*DPulseShape;
+	  b=Q3/Delta*Inverse*PulseShape-Q1/Delta*Inverse*DPulseShape;
+	  
+	  if (gain==0){
+	    *f_ai_lo<<std::setw(6)<<pha;
+	    for (int i=0;i<dignum;i++) *f_ai_lo<<std::setw(18)<<std::setprecision(10)<<a(i+1,1);
+	    *f_ai_lo<<endl;
+	    
+	    *f_bi_lo<<std::setw(6)<<pha;
+	    for (int i=0;i<dignum;i++) *f_bi_lo<<std::setw(18)<<std::setprecision(10)<<b(i+1,1);
+	    *f_bi_lo<<endl;
+	  }else{
+	    *f_ai_hi<<std::setw(6)<<pha;
+	    for (int i=0;i<dignum;i++) *f_ai_hi<<std::setw(18)<<std::setprecision(10)<<a(i+1,1);
+	    *f_ai_hi<<endl;
+	    
+	    *f_bi_hi<<std::setw(6)<<pha;
+	    for (int i=0;i<dignum;i++) *f_bi_hi<<std::setw(18)<<std::setprecision(10)<<b(i+1,1);
+	    *f_bi_hi<<endl;
+	  }
+	}
+      }   
+  }else{
+    for (int ros=0;ros<4;ros++)
+      for (int drawer=0;drawer<64;drawer++){
+	int frag = m_tileHWID->frag(ros+1,drawer);
+	for (int channel=0;channel<48;channel++)
+	  for (int gain=0;gain<2;gain++)	  
+	    if (N[ros][drawer][channel][gain]>0){
+	      if (gain==0){
+		*f_ai_lo<<"ros "<<ros
+			<<"  drawer "<<drawer<<std::hex
+			<<"  frag0x "<<frag<<std::dec
+			<<"  channel "<<channel
+			<<"  N "<<N[ros][drawer][channel][0]
+			<<endl;
+		
+		*f_bi_lo<<"ros "<<ros
+			<<"  drawer "<<drawer<<std::hex
+			<<"  frag0x "<<frag<<std::dec
+			<<"  channel "<<channel
+			<<"  N "<<N[ros][drawer][channel][0]
+			<<endl;
+	      }
+	      if (gain==1){
+		*f_ai_hi<<"ros "<<ros
+			<<"  drawer "<<drawer<<std::hex
+			<<"  frag0x "<<frag<<std::dec
+			<<"  channel "<<channel
+			<<"  N "<<N[ros][drawer][channel][1]
+			<<endl;
+		
+		*f_bi_hi<<"ros "<<ros
+			<<"  drawer "<<drawer<<std::hex
+			<<"  frag0x "<<frag<<std::dec
+			<<"  channel "<<channel
+			<<"  N "<<N[ros][drawer][channel][1]
+			<<endl;
+	      }
+	      
+	      
+	      for (int pha=-12;pha<13;pha++){
+		Correlation=Zero;
+		Inverse=Zero;
+		
+		for (int i=0;i<dignum;i++)    
+		  for (int j=0;j<dignum;j++)
+		    Correlation[i][j]=R[ros][drawer][channel][gain][i][j];
+		
+		Inverse=Correlation.inverse(ierr);
+		if (ierr==0){		
+		  for (int i=0;i<dignum;i++){
+		    if (gain==0){
+		      PulseShape[i][0]=m_LshapeForm[i*25+12+pha];
+		      DPulseShape[i][0]=m_LdshapeForm[i*25+12+pha];
+		    }else{
+		      PulseShape[i][0]=m_HshapeForm[i*25+12+pha];
+		      DPulseShape[i][0]=m_HdshapeForm[i*25+12+pha];
+		    }
+		    
+		    
+		    
+		    // 			      PulseShape[i][0]=new_shapeForm[i*25+12+pha];
+		    // 			      if ((i*25+12+pha)>0 || (i*25+12+pha)<224)
+		    // 				DPulseShape[i][0]=.5*(new_shapeForm[i*25+13+pha]-new_shapeForm[i*25+11+pha]);
+		    // 			      else DPulseShape[i][0]=0.;
+		  }			
+		  
+		  //HepStd::cout<<" correlation "<<Correlation<<HepStd::endl;
+		  //f_weights<<" correlation "<<Correlation<<endl;
+		  //		    HepStd::cout<<" inverse Matrix "<<Correlation.inverse(ierr)<<HepStd::endl;
+		  //HepStd::cout<<" inverse Matrix "<<Inverse<<HepStd::endl;
+		  //f_weights<<" inverse Matrix "<<Inverse<<endl;
+		  //		    HepStd::cout<<" Product "<<Inverse*Correlation<<HepStd::endl;
+		  
+		  //cout<<" Q1 number of columns="<<((PulseShape.T())*Inverse*PulseShape).num_col()
+		  //<<" number of rows="<<((PulseShape.T())*Inverse*PulseShape).num_row()<<endl;
+		  
+		  Q1=((PulseShape.T())*Inverse*PulseShape).determinant();
+		  Q2=((DPulseShape.T())*Inverse*DPulseShape).determinant();
+		  Q3=((DPulseShape.T())*Inverse*PulseShape).determinant();
+		  Delta=Q1*Q2-Q3*Q3;
+		  
+		  //cout<<" Coeffs: Q1="<<Q1<<" Q2="<<Q2<<" Q3="<<Q3<<" Delta="<<Delta<<endl;
+		  a=Q2/Delta*Inverse*PulseShape-Q3/Delta*Inverse*DPulseShape;
+		  b=Q3/Delta*Inverse*PulseShape-Q1/Delta*Inverse*DPulseShape;
+		  
+		  //HepStd::cout<<" a Weights= "<<a<<HepStd::endl;
+		  //HepStd::cout<<" b Weights= "<<b<<HepStd::endl;
+		  
+		  if (gain==0){
+		    *f_ai_lo<<std::setw(6)<<pha;
+		    for (int i=0;i<dignum;i++) *f_ai_lo<<std::setw(18)<<std::setprecision(10)<<a(i+1,1);
+		    *f_ai_lo<<endl;
+				
+		    *f_bi_lo<<std::setw(6)<<pha;
+		    for (int i=0;i<dignum;i++) *f_bi_lo<<std::setw(18)<<std::setprecision(10)<<b(i+1,1);
+		    *f_bi_lo<<endl;
+		  }else{
+		    *f_ai_hi<<std::setw(6)<<pha;
+		    for (int i=0;i<dignum;i++) *f_ai_hi<<std::setw(18)<<std::setprecision(10)<<a(i+1,1);
+		    *f_ai_hi<<endl;
+		    
+		    *f_bi_hi<<std::setw(6)<<pha;
+		    for (int i=0;i<dignum;i++) *f_bi_hi<<std::setw(18)<<std::setprecision(10)<<b(i+1,1);
+		    *f_bi_hi<<endl;
+		  }
+		}
+	      }
+	    }
+      }      
+  }
+  
+  f_ai_lo->close();
+  f_bi_lo->close();
+  f_ai_hi->close();
+  f_bi_hi->close();
+}
+
+
+
+
+////////////////////////////////////////
+void TileCorrelation::BuildPulseShape(vector<double> &m_pulseShape,
+				      vector<double> &m_pulseShapeX,
+				      vector<double> &m_pulseShapeT,
+				      int dignum,
+				      MsgStream &log)
+{ 
+  MSG::Level logLevel = log.level();
+  bool lDebug = (logLevel<=MSG::DEBUG);
+  bool lVerbose = (logLevel<=MSG::VERBOSE);
+  if (lDebug)
+    log<<MSG::DEBUG<<"TileCalorimeter::BuildPulseShape"<<endreq;
+  
+  //1: set m_pulseShape
+  m_pulseShape.resize(dignum*25);
+  if (lDebug)
+    log<<MSG::DEBUG<<"Set dimension of m_pulseShape to dignum*25="<<dignum*25<<endreq;
+
+  //2: scan m_pulseShapeT for: tmin, tmax, nt0 and size: m_pulseShapeX[nt0]=1.0;
+  int nt0=0, size;
+  double tmin=10000., tmax=-10000.;
+  size=m_pulseShapeT.size();
+  for (int i=0; i<size;i++){
+    if (m_pulseShapeT[i]<tmin) tmin=m_pulseShapeT[i];
+    if (m_pulseShapeT[i]>tmax) tmax=m_pulseShapeT[i];
+    if (m_pulseShapeT[i]==0) nt0=i;
+  }
+  if (lDebug)
+    log<<MSG::DEBUG<<"m_pulseShapeX & m_pulseShapeT size ="<<size<<", tmin="<<tmin<<", tmax="<<tmax<<" central point="<<nt0<<" m_pulseShapeT[nt0]="<<m_pulseShapeT[nt0]<<" m_pulseShapeX[nt0]="<<m_pulseShapeX[nt0]<<endreq;
+  
+  //3: fill m_pulseShape
+  bool exact;
+  int nminn, nminp;
+  double minn, minp, tdist;
+  m_pulseShape[dignum*25/2]=m_pulseShapeX[nt0];
+  for (int i=1;i<dignum*25/2+1;i++){
+    // negative times: 0->dignum*25/2    
+    if (-i<tmin) m_pulseShape[dignum*25/2-i]=0.;
+    else{
+      exact=false;
+      minn=-10000.;
+      minp=10000.;
+      nminn=0;
+      nminp=size-1;
+      for (int j=0;j<nt0+1&&!exact;j++){
+	if (m_pulseShapeT[j]==double(-i)){
+	  m_pulseShape[dignum*25/2-i]=m_pulseShapeX[j];
+	  exact=true;
+	}else{
+	  tdist=m_pulseShapeT[j]-double(-i);
+	  if (tdist < 0. && tdist>minn){
+	    minn=tdist;
+	    nminn=j;
+	  }
+	  if (tdist>0. && tdist<minp){
+	    minp=tdist;
+	    nminp=j;
+	  }
+	}
+      }	  
+      
+      if (exact) {
+        if (lVerbose)
+          log<<MSG::VERBOSE<<"exact value found for time="<<-i<<" m_pulseShape="<<m_pulseShape[dignum*25/2-i]<<endreq;
+      } else {
+        if (lVerbose)
+          log<<MSG::VERBOSE<<"exact value NOT found for time="<<-i
+             <<" nminn="<<nminn<<" m_pulseShapeT="<<m_pulseShapeT[nminn]<<" m_pulseShapeX="<<m_pulseShapeX[nminn]<<std::endl 
+             <<" nminp="<<nminp<<" m_pulseShapeT="<<m_pulseShapeT[nminp]<<" m_pulseShapeX="<<m_pulseShapeX[nminp]<<endreq;	  
+	m_pulseShape[dignum*25/2-i]=m_pulseShapeX[nminn]+(m_pulseShapeX[nminp]-m_pulseShapeX[nminn])/(m_pulseShapeT[nminp]-m_pulseShapeT[nminn])*(-i-m_pulseShapeT[nminn]);
+      }
+      
+    }
+    
+    // positive times: dignum*25/2->dignum*25
+    if (i>tmax) m_pulseShape[dignum*25/2+i]=0.;
+    else{
+      exact=false;
+      minn=-10000.;
+      minp=10000.;	  
+      nminn=0;
+      nminp=size;
+      for (int j=nt0;j<size&&!exact;j++){
+	if (m_pulseShapeT[j]==double(i)){
+	  m_pulseShape[dignum*25/2+i]=m_pulseShapeX[j];
+	  exact=true;
+	}else{
+	  tdist=m_pulseShapeT[j]-double(i);
+	  if (tdist<0)
+	    if (tdist>minn)
+	    {
+	      minn=tdist;
+	      nminn=j;
+	    }
+	  if (tdist>0)
+	    if (tdist<minp){
+	      minp=tdist;
+	      nminp=j;
+	    }
+	}
+      }
+      if (exact) {
+        if (lVerbose)
+          log<<MSG::VERBOSE<<"exact value found for time="<<i<<" m_pulseShape="<<m_pulseShape[dignum*25/2+i]<<endreq;
+      } else {
+        if (lVerbose)
+          log<<MSG::VERBOSE<<"exact value NOT found for time="<<i
+	     <<" nminn="<<nminn<<" m_pulseShapeT="<<m_pulseShapeT[nminn]<<" m_pulseShapeX="<<m_pulseShapeX[nminn]<<std::endl 
+	     <<" nminp="<<nminp<<" m_pulseShapeT="<<m_pulseShapeT[nminp]<<" m_pulseShapeX="<<m_pulseShapeX[nminp]<<endreq;	  
+	
+	m_pulseShape[dignum*25/2+i]=m_pulseShapeX[nminn]+(m_pulseShapeX[nminp]-m_pulseShapeX[nminn])/(m_pulseShapeT[nminp]-m_pulseShapeT[nminn])*(i-m_pulseShapeT[nminn]);
+      }
+    }
+  }
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileFilterManager.cxx b/TileCalorimeter/TileRecUtils/src/TileFilterManager.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..01f573912dde4f8db8e3c1b67bb72e6beec982d7
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileFilterManager.cxx
@@ -0,0 +1,631 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// **************************************************************************************
+// Filename : TileFilterManager.cxx
+// Author   : F. Merritt, A. Aurisano
+// Created  : March 2004
+
+//**************************************************************************************
+
+#include "TileRecUtils/TileFilterManager.h"
+#include "TileConditions/TileInfo.h"
+
+#include <algorithm>
+#include <iostream>
+#include "boost/io/ios_state.hpp"
+
+// =======================================================================================
+
+// Constructor
+TileFilterManager::TileFilterManager(int mode, int level, int &npMax, int &nSam, int &inTdig, int &ibSam, 
+				     int &ieSam, int &bCr, int &eCr, int &nShape,
+				     int &inShape, std::vector<double> &Shape, bool lDebug)
+  : map_ind2icr()
+  , map_icr2ind()
+{
+  /* Store information needed for fitting. */
+
+  debug = lDebug;     // Stored as a private data member.
+  Fmode = mode;
+  Flevel = level;
+  NParamMax = npMax;
+  Nshape = nShape;
+  InTshape = inShape;
+  Ndig=nSam;
+  InTdig=inTdig;
+  jBsamp=ibSam;
+  jEsamp=ieSam;
+  jBcross=bCr;
+  jEcross=eCr;
+  InTcross = -jBcross;
+  Ncross=jEcross-jBcross+1;
+  std::cout << " TileFilterManager constructor.  NParamMax =" << NParamMax << ", Ndig =" << Ndig << std::endl;
+
+  // Parameters that should be read in externally.
+  rchisqCut = 4.0;
+  chiCut = 3.5;
+  int NParamMin = 2;
+  std::cout << " FilterMode=" << Fmode << ", NParamMax=" << NParamMax << ", NParamMin="
+       << NParamMin << ", chiCut=" << chiCut << ", rchisqCut=" << rchisqCut << std::endl;
+  //
+  // Create the tables of offsets for calculating Fitter indices.
+
+  MakeFitterOffsetTables();
+
+  //
+  // Create the maps taking us from "digit number" idig to "crossing index" ind.  
+  // "idig" corresponds to the array of digits which are read out, ranging from zero
+  // to Ndig, with the in-time digit defined by InTdig.  It is more convenient for
+  // fitting to re-order these so that the index of the in-time crossing is zero,
+  // and the other are sequentially numbered 1,2,..., Ncr.
+  //
+  std::cout << " Number of digits read =" << Ndig << " .  In-time digit has idig=" 
+       << InTdig << "." << std::endl;
+  int ind=0;
+  for (int icr=0; icr<Ncross; icr++) {
+    if(icr==InTdig)  ind = 0;
+    if(icr<InTdig)   ind = icr - jBcross + 1;
+    if(icr>InTdig)   ind = icr - jBcross;
+    map_icr2ind[icr] = ind;
+    map_ind2icr[ind] = icr;
+  }
+  if(debug) {
+    for(int ind = 0; ind<Ncross; ind++) {
+      int icr = map_ind2icr[ind];
+      int kdisp = icr - InTdig;
+      std::cout << " Index =" << ind << "  crossing #  =" << icr 
+	   << ", displacement from InTdig =" << kdisp << " crossings." << std::endl;
+    }
+  }
+/* Create the shaping matrix.  Row = crossing (of deposit), col = digit. */
+  //  std::vector<double> ShapeMat[Ncross][Ndig];
+  for (int ind = 0; ind<Ncross; ind++) {
+    int icr = map_ind2icr[ind];
+    int OffSet = InTshape;
+    double * Xshape = new double[Ndig];
+    for(int idig=0; idig<Ndig; idig++) {
+      int k=OffSet-icr+idig;
+      if(k<0) k=0;
+      if(k>=Nshape) k=Nshape-1;
+      Xshape[idig]=Shape[k];
+    }
+    CrossShape.push_back(Xshape);
+  }
+  /* Print out the Shape Matrix. */
+  if(debug) {
+    boost::io::ios_base_all_saver coutsave (std::cout);
+    std::cout << " TileFilterManager: ShapingMatrix.   Nshape=" << Nshape << ", InTshape=" << InTshape
+              << ", Ndig=" << Ndig << ", InTdig=" << InTdig 
+              << ", Ncross=" << Ncross << std::endl;
+    for(int ind=0; ind<Ncross; ind++) {
+      double * Xshape = CrossShape[ind];
+      std::cout << " ind=" << ind << " Shape=" ;
+      for (int idig=0; idig<Ndig; idig++) {
+        std::cout << " " << std::setw(6) << std::setprecision(3) << Xshape[idig];
+      }
+      std::cout << std::endl;
+    }
+  }
+  
+  //  vNparam.reserve(NParamMax);
+  //Print the FitterIndex arrays.
+  std::vector<int> Crossings;
+  //int NpileMax = NParamMax - 2;
+  int NampMax = NParamMax - 1;
+  for(int ipile=0; ipile<NampMax; ipile++) {
+    int Nmax = NfitIndex[ipile];
+    if(debug)
+      std::cout << " Crossing configurations for Nparam=" << ipile+2
+                << ", Npileup=" << ipile << ": " << Nmax << " configurations." << std::endl;
+    for(int iconfig=0; iconfig<Nmax; iconfig++) {
+      Crossings.clear();
+      getVcross(ipile, iconfig, Crossings);
+      int nParam = ipile+2;
+      int kFitIndex = getFitIndex(nParam, Crossings);
+      int ncr = Crossings.size();
+      if(debug) {
+	std::cout << "     Npile=" << std::setw(2) << ipile << ", iconfig=" << std::setw(3)
+	     << iconfig << " (kF=" << std::setw(3) << kFitIndex << ")  => Vcross=";
+	for(int icr=0; icr<ncr; icr++) {
+	  std::cout << " " << std::setw(3) << Crossings[icr];
+	}
+	std::cout << std::endl;
+      }
+    }
+  }
+  // Make the Fitter Arrays
+  /* int iok = */ MakeFitterArrays();
+
+    /* Initialization has successfully completed.  */
+  // Set debug = false for execution phase.
+  debug = true;
+  return;
+}
+
+// ===================================================================================
+
+// Destructor
+TileFilterManager::~TileFilterManager() {
+
+  for (unsigned int ind = 0; ind<CrossShape.size(); ++ind) {
+    delete [] CrossShape[ind];
+  }
+
+  for (unsigned int ind = 0; ind<OffsetVector.size(); ++ind) {
+    delete [] OffsetVector[ind];
+  }
+}
+
+// ===================================================================================
+
+int TileFilterManager::FitDigits(TileFilterResult &tResult, bool lDebug) { 
+  int icode = -1;
+
+  //  std::cout << " FitDigits:  Fmode=" << Fmode << ", lDebug=" << lDebug << std::endl;
+  if(Fmode==2) {
+    icode = FitDigits1(tResult, lDebug);
+    return icode;
+  }
+  if(Fmode==3) {
+    icode = FitDigits2(tResult, lDebug);
+    return icode;
+  }
+  std::cout << " ERROR in TileFitDigits !!  Fmode =" << Fmode << std::endl;
+
+  return icode;
+}
+
+// ===================================================================================
+
+int TileFilterManager::FitDigits1(TileFilterResult &tResult, bool lDebug) { 
+  int icode = -1;
+  debug = lDebug;
+  // Get references to the key variable in tResult.
+  HepVector& digits = tResult.getDigRef();
+  //HepVector& fitAmp = tResult.getParamRef();
+  //HepVector& fitErr = tResult.getErrRef();
+  HepVector& residuals = tResult.getResidRef();
+  double& chisqRef = tResult.getChisqRef();
+  int& Npar = tResult.getNparRef();
+  std::vector<int>& vcross = tResult.getVcrossRef();
+  int& iFitIndex = tResult.getFitIndexRef();
+
+  // First find crossing with highest amplitude.
+  int jcross = FindHighestResidual(digits);
+  int jparam = ((jcross<0) ? 0 : map_icr2ind[jcross]);
+  //  if(debug) std::cout << " Highest crossing = " << jcross << "(cind=" << jparam
+  //		 << "), amp=" << digits[jcross] << std::endl;
+
+  // Initialize fitting parameters
+  Npar=1;
+  iFitIndex = -1;
+  chisqRef = 999.;
+  int iret = 0;
+
+  jparam = 0; // Set this to be intime crossing [FSM, 7/30/04]
+  // Include in-time crossing (jparam=0) in fit even if it is not the maximum.
+  //if(jparam != 0) iret = tResult.addCross(0);
+
+  // Start loop over fits, adding a new parameter each time until reduced chisq is OK.
+  double rchisq = 999.;
+  icode = -1;
+  double digSigma = tResult.getSigDig();
+  while(Npar<NParamMax) {
+    if(debug) std::cout << " FilterManager.FitDigits1, while loop. Npar=" << Npar << ", NParamMax=" << NParamMax << std::endl;
+    iret = tResult.addCross(jparam);
+    if(iret != 0) {
+      icode = iret;
+      break;
+    }
+    //    if(debug) tResult.SnapShot(0);
+    iFitIndex = getFitIndex(Npar, vcross);
+    std::vector<TileFitter>& vFitter = vNpFitter[Npar-2];
+    TileFitter& tileFitter = vFitter[iFitIndex];
+    iret = tileFitter.FitAmp(tResult, false);
+    if(debug) tResult.SnapShot(2);
+    rchisq = chisqRef/(Ndig-Npar);
+
+    // Chisq is small enough, so the fit has been successful.
+    if(rchisq<rchisqCut) {
+      icode = 0;
+      break;
+    }
+    // Have hit max param even though chisq is still big (problem!).  
+    if(Npar==NParamMax) {
+      icode = 5;
+      break;
+    }
+    // Find index of the highest residual.
+    jcross = FindHighestResidual(residuals);
+    jparam = ((jcross<0) ? 0 : map_icr2ind[jcross]);
+    // If jparam is already in list, terminate search (problem?).
+    bool ldup=false;
+    int Namp = vcross.size();
+    for(int i=1; i<Namp; i++) {
+      if(vcross[i]==jparam) ldup=true;
+    }
+    if(ldup) {
+      icode = 4;
+      break;
+    }
+
+    double chi = ((jcross<0) ? 0.0 : residuals[jcross]/digSigma);
+    if(chi<chiCut) {
+      icode = 3;
+      break;
+    }
+  }
+  if(debug) {
+    std::cout << " End of loop.  icode =" << icode << ", Npar=" << Npar << std::endl;
+    tResult.SnapShot(1);
+  }
+  return icode;
+}
+
+// ===================================================================================
+
+int TileFilterManager::FitDigits2(TileFilterResult &tResult, bool lDebug) { 
+  //int iret = -1;
+  int icode = 0;
+  debug = lDebug;
+  // Get references to the key variable in tResult.
+  HepVector& digits = tResult.getDigRef();
+  HepVector& fitAmp = tResult.getParamRef();
+  HepVector& fitErr = tResult.getErrRef();
+  //HepVector& residuals = tResult.getResidRef();
+  double& chisqRef = tResult.getChisqRef();
+  int& Npar = tResult.getNparRef();
+  int Namp = Npar - 1;
+  std::vector<int>& vcross = tResult.getVcrossRef();
+  int& iFitIndex = tResult.getFitIndexRef();
+  if(debug) {
+    boost::io::ios_base_all_saver coutsave (std::cout);
+    //    tResult.SnapShot(0);
+    std::cout << " digits=" ;
+    for(int i=0; i<Ndig; i++) {
+      std::cout << " " << std::setw(6) << std::setprecision(2) << digits[i];
+    }
+    std::cout << std::endl;
+  }
+
+  // Make a crossing vector that contains all allowed amplitudes.
+  Namp = Flevel - 1;
+  for(int iamp=0; iamp<Namp; iamp++) {
+    /*iret =*/ tResult.addCross(iamp);
+  }
+
+  //Namp and Npar could have changed. Npar is incremented by addCross.
+  Namp = Npar - 1;
+  if(debug) tResult.SnapShot(0);
+
+  // Initialize fitting parameters
+  iFitIndex = -1;
+  chisqRef = 999.;
+
+  // Start loop over fits, removing one or more amplitudes each time.
+  // double rchisq = 999.;
+  icode = -1;
+
+  int Npass = 0;
+  while(Npar>1) {
+    Npass +=1;
+    if(debug) std::cout << " FilterManager.FitDigits2, while loop. Npar=" 
+                   << Npar << ", NParamMax=" << NParamMax << std::endl;
+    //    if(debug) tResult.SnapShot(0);
+    iFitIndex = getFitIndex(Npar, vcross);
+    if(debug) std::cout << " Npar=" << Npar << ", iFitIndex=" << iFitIndex << std::endl;
+    std::vector<TileFitter>& vFitter = vNpFitter[Npar-2];
+    TileFitter& tileFitter = vFitter[iFitIndex];
+    //    if(debug) std::cout << " Ready to call tileFitter.FitAmp" << std::endl;
+    /*iret =*/ tileFitter.FitAmp(tResult, false);
+    if(debug) tResult.SnapShot(2);
+    // If Npar is down to 2 parameters (ped + inTime), terminate fitting search.
+    if(Npar<=2) {
+      icode = 3;
+      break;
+    }
+
+    // Calculate significance chi of each fit parameter..
+    // double chiCutLow[4] = {chiCut, 1.50, 0.75, 0.};
+    const int Ndim = 12;
+    double chiAmp[Ndim];
+    int iAmp[Ndim];
+    int Npile = 0;
+    for(int i=2; i<Npar; i++) {
+      chiAmp[Npile] = fitAmp[i]/fitErr[i];
+      iAmp[Npile] = vcross[i-1];
+      if(debug) std::cout << " set chiAmp: i=" << i << ", iAmp=" << iAmp[Npile] << ",  chi=" << chiAmp[Npile] << std::endl;  
+      Npile += 1;
+    }
+    int ndrop = 0;
+    int crdrop = 0;
+    int ndropMax = 4;
+    while(ndrop<ndropMax) {
+      if(debug) std::cout << " top of drop loop.  ndrop=" << ndrop << ", Npass=" << Npass << std::endl;
+      double chiMin = chiCut;
+      //      chiMin = chiCutLow[ndrop];
+      int idrop = -1;
+      for (int i=0; i<Npile; i++) {
+	//	if(debug) std::cout << "drop candidate: i=" << i << ", iAmp=" << iAmp[i]
+	//       << ", chiAmp=" << chiAmp[i] <<", chiMin=" << chiMin << std::endl;
+        if(iAmp[i]<0) continue;
+        if(chiAmp[i]>chiMin) continue;
+        chiMin = chiAmp[i];
+        idrop = i;
+	crdrop = iAmp[i];
+      }
+      if(debug) std::cout << " end of Npile loop.  idrop=" << idrop << ", crdrop=" << crdrop << ", ndrop=" << ndrop << std::endl; 
+      if(idrop>-1) {
+        /*iret =*/ tResult.dropCross(crdrop);
+        ndrop += 1;
+	iAmp[idrop] = - iAmp[idrop];
+        icode = 1;
+        if(debug) std::cout << " ndrop=" << ndrop << ", idrop=" << idrop 
+		       << ", crdrop=" << crdrop << ", chiMin=" << chiMin << std::endl;
+      } else {
+        icode = 6;
+        break;
+      }
+      if(debug) std::cout << "FitDig2:  Npass=" << Npass << ", ndrop=" << ndrop << std::endl;
+    } // end of ndrop loop
+    if(debug) std::cout << " have fallen out of drop loop.  ndrop=" << ndrop << ", Npass=" << Npass << std::endl;
+    if(ndrop==0) {
+      icode=0;
+      break;
+    }
+  } // end of while loop
+
+  if(debug) {
+    std::cout << " TileFilterManager:  End of pass loop.  icode =" << icode << ", Npar=" << Npar 
+	 << ", Npass=" << Npass << std::endl;
+    tResult.SnapShot(2);
+  }
+  return icode;
+}
+
+// ===================================================================================
+
+int TileFilterManager::FindHighestResidual(HepVector &digits) const {
+  int icrMax=-1;
+  double ampMax = -999.;
+  for (int icr=0; icr<Ncross; icr++) {
+    if(digits[icr]>ampMax) {
+      icrMax = icr;
+      ampMax = digits[icr];
+    }
+  }
+  return icrMax;
+}
+
+// ===================================================================================
+
+int TileFilterManager::FindLowestCrossing(HepVector &digits) const {
+  int icrMin=-1;
+  double ampMin = +9999.;
+  for (int icr=0; icr<Ncross; icr++) {
+    if(icr==InTdig) continue;
+    if(digits[icr]<ampMin) {
+      icrMin = icr;
+      ampMin = digits[icr];
+    }
+  }
+  return icrMin;
+}
+
+// ===================================================================================
+
+void TileFilterManager::MakeFitterOffsetTables() {
+
+  int NpileupMax = NParamMax - 2;
+  int NampMax = NParamMax - 1;
+  int Npileup = NpileupMax;
+  if (debug)
+    std::cout << " Enter MakeFitterOffsetTables:  Npileup=" << Npileup 
+              << ", NampMax=" << NampMax << ", Ncross=" << Ncross << std::endl;
+//For ipileup=0 (special case), offset = index of crossing.
+  int * Offset0 = new int[Ncross];
+  for(int index=0; index<Ncross; index++) {
+    Offset0[index] = index;
+  }
+  OffsetVector.push_back(Offset0);
+  //
+  //For ipileup=1, number offsets sequentially starting with zero for index=1.
+  int * Offset1 = new int[Ncross];
+  for(int index=0; index<Ncross; index++) {
+    if(index<2) Offset1[index]=0;
+    else Offset1[index]=index-1;
+  }
+  OffsetVector.push_back(Offset1);
+
+  // For Npileup>1, use iterative formula (offsets = coeff of binary expansion)
+  if(NampMax>2) {
+    for(int ipile=2; ipile<NampMax; ipile++) {
+      int * vlast = OffsetVector[ipile-1];
+      int * Offset = new int[Ncross];
+      for(int index = 0; index<Ncross; index++) {
+	if(index<=ipile) {Offset[index]=0;}
+	else {Offset[index] = Offset[index-1] + vlast[index-1];}
+      }
+    OffsetVector.push_back(Offset);
+    }
+  }
+  // Find the number of FitIndex values for each number of parameters.
+  for(int ipile=0; ipile<NampMax; ipile++) {
+    int Nmax;
+    int * Offset = OffsetVector[ipile];
+    if(ipile<=1) Nmax = Offset[Ncross-1] + 1;
+    if(ipile>1) {
+      int * vlast = OffsetVector[ipile-1];
+      Nmax = Offset[Ncross-1]+vlast[Ncross-1];
+    }
+    NfitIndex.push_back(Nmax);
+  }
+
+ // Print out the Offset table.
+  if (debug) {
+    std::cout << " *** TileFilter Offset table for Npileup=" << Npileup 
+              << " and Ncross=" << Ncross << std::endl;
+    for(int ipile=0; ipile<NampMax; ipile++) {
+      int * Offset = OffsetVector[ipile];
+      std::cout << "       ipile=" << std::setw(3) << ipile << ":  Offsets = ";
+      for (int index=0; index<Ncross; index++) {
+        std::cout << " " << std::setw(3) << Offset[index];
+      }
+      std::cout << ";  NfitIndex=" << std::setw(3) << NfitIndex[ipile] << std::endl;
+    }
+  }
+  
+  return;
+}
+
+// ===================================================================================
+
+int TileFilterManager::MakeSPD(bool debugMakeSPD, std::vector<int>& vcross, HepMatrix& SPD) {
+  int iret=-1;
+  int Namp = vcross.size();
+  int Nparam = Namp + 1;
+  // First row of SPD is always for the pedestal.
+  for(int idig=0; idig<Ndig; idig++) {
+    SPD[0][idig] = 1.0;
+  }
+  // The remaining rows correspond to the crossing amplitudes specified by vcross.
+  for(int ipar=1; ipar<Nparam; ipar++) {
+    int jcr = vcross[ipar-1];
+    double * Xshape = CrossShape[jcr];
+    for(int idig=0; idig<Ndig; idig++) {
+      SPD[ipar][idig] = Xshape[idig];
+    }
+  }
+  if(debugMakeSPD) {
+    std::cout << " Make SPD for NP=" << Nparam << ", vcross=";
+    for(int iamp=0; iamp<Namp; iamp++) {
+      std::cout << " " << vcross[iamp];
+    }
+    std::cout << std::endl;
+    for(int ipar=0; ipar<Nparam; ipar++) {
+      std::cout << " ip=" << ipar << " SPD=";
+      for(int idig=0; idig<Ndig; idig++) {
+	std::cout << " " << SPD[ipar][idig];
+      }
+      std::cout << std::endl;
+    }
+  } // end debugMakeSPD printout
+  return iret;
+}
+
+// ===================================================================================
+
+int TileFilterManager::MakeFitterArrays() {
+
+  if(debug) std::cout << " TileFilterManager::MakeFitterArrays.  Will print out first matrix "
+		 << "only for each vFitter vector (one for each value of Nparam)." << std::endl;
+  bool cdebug = false;
+  int NampMax = NParamMax - 1;
+  for(int iamp=0; iamp<NampMax; iamp++) {
+    int Nparam = iamp+2;       // number of parameters in this series of fits.
+    int Nindex = NfitIndex[iamp];  // number of configurations for this Nparam.
+    if(debug) std::cout << " ===>    Nparam=" << Nparam << " => Nindex=" << Nindex
+		   << " TileFitter objects:" << std::endl;
+    std::vector<TileFitter> vFitter(Nindex);
+    for(int index=0; index<Nindex; index++) {
+      //      if(debug) cdebug = (index==0)||(index==Nindex-1);
+      if(debug) cdebug = (index==Nindex-1);
+      std::vector<int> vcross;
+      getVcross(iamp, index, vcross);
+      HepMatrix SPD(Nparam,Ndig);
+      MakeSPD(cdebug, vcross, SPD);            // fill the matrix elements
+      // If constraints are needed, set Icon.
+      int Icon = 0;
+      if(Nparam>Ndig-1) Icon = 1;
+      if(Nparam>Ndig)   Icon = 2;
+      TileFitter * tileFitter = new TileFitter(cdebug, Nparam, Ndig, index, SPD, Icon);
+      vFitter[index] = *tileFitter;
+      delete tileFitter;
+    }
+    vNpFitter.push_back(vFitter);
+  }
+  return 0;
+}
+
+// ===================================================================================
+
+std::vector<int>& TileFilterManager::get_NfitIndex() {
+  return NfitIndex;
+}
+
+// ===================================================================================
+
+void TileFilterManager::getCuts(double& rchisqC, double& chiC) {
+  rchisqC = rchisqCut;
+  chiC = chiCut;
+  return;
+}
+
+// ===================================================================================
+
+void TileFilterManager::getVcross(int nPileup, int iconfig, std::vector<int>& vcross) {
+
+  vcross.clear();
+  int kconfig = iconfig;
+  int icrmax = Ncross-1;
+  for(int ipile=nPileup; ipile>-1; ipile--) {
+    int * Offset = OffsetVector[ipile];
+    for(int icross=icrmax; icross>=0; icross--) {
+      //      std::cout << "icross=" << icross << ", Offset[icross]=" << Offset[icross]
+      //	   << ", kconfig=" << kconfig << ", icrmax=" << icrmax << std::endl;
+      if(Offset[icross]<=kconfig) {
+	int icr = icross;
+	icrmax=icross-1;
+	kconfig = kconfig - Offset[icross];
+	vcross.push_back(icr);
+	break;
+      }
+      if(kconfig<0) {
+	std::cout << " ERROR!! In getVcross, kconfig=" << kconfig << std::endl;
+      }
+    }
+  }
+  sort(vcross.begin(), vcross.end());
+
+  return;
+}
+
+// ===================================================================================
+
+int TileFilterManager::getFitIndex(int Nparam, std::vector<int>& vcross) {
+
+  int Index=0;
+  int Namp = Nparam-1;
+  if(Namp<=0) {
+    std::cout << " TileFilterManager.getFitIndex called when Nparam=" << Nparam << std::endl;}
+  for(int ipar=0; ipar<Namp; ipar++) {
+    int * Offset = OffsetVector[ipar];
+    int jcr = vcross[ipar];
+    Index += Offset[jcr];
+  }
+
+  //  if(debug) {
+  //    std::cout << " getFitIndex: Nparam=" << Nparam << ", Index=" << Index
+  //	 << ", Vcross=";
+  //    for(int ipar=0; ipar<Namp; ipar++) {
+  //      std::cout << " " << vcross[ipar];
+  //    }
+  //    std::cout << std::endl;
+  //  }
+
+  return Index;
+}
+
+// ===================================================================================
+
+std::vector<double>& TileFilterManager::getFitterErr(int Nparam, int iconfig) {
+  int ipile = Nparam - 2;
+  std::vector<TileFitter>& vFitter = vNpFitter[ipile];
+  TileFitter& tileFitter = vFitter[iconfig];
+  std::vector<double>& fitErr = tileFitter.getErrRef();
+  return fitErr;
+}
+
+// ===================================================================================
diff --git a/TileCalorimeter/TileRecUtils/src/TileFilterResult.cxx b/TileCalorimeter/TileRecUtils/src/TileFilterResult.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..9d8398d9c782646cdda49c0b7aaa146e1721a5f9
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileFilterResult.cxx
@@ -0,0 +1,226 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//****************************************************************************
+// Filename : TileFilterResult.cxx
+// Authors  : F. Merritt, A. Aurisano
+// Created  : March 2004
+//
+//****************************************************************************
+#include "TileRecUtils/TileFilterResult.h"
+#include <algorithm>
+#include <iostream>
+#include <iomanip>
+#include "boost/io/ios_state.hpp"
+
+// Constructor
+TileFilterResult::TileFilterResult(std::vector<float> &dig, double sig) {
+  debug = false;
+  int ND = dig.size();
+  HepVector digtem(ND);
+  digits = digtem;
+  for(int id=0; id<ND; id++) {
+    digits[id] = dig[id];
+  }
+  //int nrow = digits.num_row();
+  //int nrowtem = digtem.num_row();
+  sigDigit = sig;
+  // Initialize some of the variables that will be set later.
+  Nparam = 1;
+  Npileup = 0;
+  Vcross.clear();
+  chisq = 999.;
+  iFitIndex = -1;
+}
+//============================================================================= 
+TileFilterResult::~TileFilterResult() {
+  return;
+}
+//============================================================================= 
+double TileFilterResult::getSigDig() const {
+  return sigDigit;
+}
+//============================================================================= 
+HepVector* TileFilterResult::getDigPt() {
+  HepVector* pdigits = &digits;
+  return pdigits;
+}
+//============================================================================= 
+HepVector& TileFilterResult::getDigRef() {
+  return digits;
+}
+//============================================================================= 
+std::vector<int>& TileFilterResult::getVcrossRef() {
+  return Vcross;
+}
+//============================================================================= 
+int& TileFilterResult::getFitIndexRef() {
+  return iFitIndex;
+}
+//============================================================================= 
+int& TileFilterResult::getNparRef() {
+  return Nparam;
+}
+//============================================================================= 
+HepVector& TileFilterResult::getParamRef() {
+  return fitParam;
+}
+//============================================================================= 
+HepVector& TileFilterResult::getErrRef() {
+  return fitErr;
+}
+//============================================================================= 
+HepVector& TileFilterResult::getResidRef() {
+  return residuals;
+}
+//============================================================================= 
+double& TileFilterResult::getChisqRef() {
+  return chisq;
+}
+//============================================================================= 
+void TileFilterResult::PrintFitParam() {
+  boost::io::ios_base_all_saver coutsave (std::cout);
+  std::cout << " Print fitted param from TileFilterResult:  Nparam=" << Nparam 
+       << ", chisq=" << chisq << std::endl;
+  for(int ipar=0; ipar<Nparam; ipar++) {
+    if(ipar==0) {
+      std::cout << " i=" << ipar << ", kcr=P" << ", A=" << std::setw(5) << std::setprecision(2) 
+	 << fitParam[ipar] << " +-" << fitErr[ipar] << std::endl;
+    } else {
+    std::cout << " i=" << ipar << ", kcr=" << Vcross[ipar-1] << ", A=" 
+	 << fitParam[ipar] << " +-" << fitErr[ipar] << std::endl;
+    }
+  }
+  return;
+}
+//============================================================================= 
+double TileFilterResult::getInTime(double &amp, double &err, double &ped,
+				   double &chi2, double &t) {
+  amp = fitParam[1];
+  err = fitErr[1];
+  ped = fitParam[0];
+  chi2 = chisq;
+  t = 0.;
+
+  return chisq;
+}
+//============================================================================= 
+void TileFilterResult::SnapShot(int imode) {
+  boost::io::ios_base_all_saver coutsave (std::cout);
+  // This print a short snapshot of the FilterResult state.
+  std::cout << " SnapShot: imode=" << imode << ".  Nparam=" << Nparam 
+       << ", chisq=" << chisq << ", iFitIndex" << iFitIndex << ", Vcross=";
+  int Namp=Nparam-1;
+  for(int jamp=0; jamp<Namp; jamp++) {
+    std::cout << " " << Vcross[jamp];
+  }
+  std::cout << std::endl;
+  if(iFitIndex<0) return;
+
+  if(imode>0) {
+    std::cout << "   FitParam=";
+    for(int ipar=0; ipar<Nparam; ipar++) {
+      std::cout << std::setw(5) << std::setprecision(1) << fitParam[ipar] << "+-" << fitErr[ipar];
+      if(ipar<Nparam-1) std::cout << ", ";
+    }
+    std::cout << std::endl;
+  }
+  if(imode>1) {
+    int Ndig = digits.num_row();
+    std::cout << "   Residuals=";
+    for(int idig=0; idig<Ndig; idig++) {
+      std::cout << " " << std::setw(4) << std::setprecision(3) << residuals[idig];
+    }
+    std::cout << std::endl;
+  }
+}
+//============================================================================= 
+int TileFilterResult::addCross(int kcrIndex) {
+
+  int iret = -1;
+
+  //Check that kcrIndex is not already in list.
+  bool ldup = false;
+  if(Nparam>1) {
+    int Namp = Nparam-1;
+    for(int icr=0; icr<Namp; icr++) {
+      if(kcrIndex==Vcross[icr]) {
+	ldup = true;
+	if(debug) {
+	  std::cout << " TileFilterResult.addCross: kcrIndex=" << kcrIndex 
+	       << " is already in crossing list: Kcross =";
+	  for (int j=0; j<Namp; j++) {
+	    std::cout << " " << Vcross[j];
+	  }
+	  std::cout << std::endl;
+	}
+	if(ldup) break;
+      }
+    } // end for loop
+  } // end "if(Nparam>1)"
+  if(ldup) {
+    iret=1;
+    return iret;
+  }
+
+  // Add the new crossing.
+  iret = 0;
+  Vcross.push_back(kcrIndex);
+  std::sort(Vcross.begin(), Vcross.end() );
+  Nparam = Nparam + 1;
+
+  // Since we have a new Vcross configuration, iFitIndex is not yet defined for it.
+  iFitIndex = -1;
+
+  if(debug) {
+    int Namp = Nparam-1;
+    std::cout << " TileFilterResult.addCross.  Exit with Nparam=" << Nparam 
+	 << ", Vcross=";
+    for(int icr=0; icr<Namp; icr++) {
+      std::cout << " " << std::setw(3) << Vcross[icr];
+    }
+    std::cout << std::endl;
+  }
+
+  return iret;
+}
+//============================================================================= 
+int TileFilterResult::dropCross(int idrop) {
+  // Drop the crossing stored in amplitude # iamp.
+  int iret = -1;
+  int Namp = Nparam - 1;
+  for(int iamp=1; iamp<Namp; iamp++) {
+    if(Vcross[iamp] == idrop) {
+      // Erase the crossing.
+      iret = 0;
+      Vcross.erase(Vcross.begin()+iamp); 
+      //  std::sort(Vcross.begin(), Vcross.end() );
+      Nparam = Nparam - 1;
+    }
+  } 
+  if(iret != 0) {
+    std::cout << "error in TileFilterResult.dropCross:  idrop=" << idrop << "but vcross =";
+    for(int iamp=0; iamp<Namp; iamp++) {
+      std::cout << " " << Vcross[iamp];
+    }
+    std::cout << std::endl;
+    return iret;
+  }
+
+
+  // Since we have a new Vcross configuration, iFitIndex is not yet defined for it.
+  iFitIndex = -1;
+
+  if(debug) {
+    int Namp = Nparam-1;
+    std::cout << " TileFilterResult.dropCross.  Exit with Nparam=" << Nparam 
+	 << ", Vcross=";
+    for(int icr=0; icr<Namp; icr++) {
+      std::cout << " " << std::setw(3) << Vcross[icr];
+    }
+    std::cout << std::endl;
+  }
+
+  return iret;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileFilterTester.cxx b/TileCalorimeter/TileRecUtils/src/TileFilterTester.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..049b52db480c224da4a4fe84ccc3156c6b6daa71
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileFilterTester.cxx
@@ -0,0 +1,373 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// **************************************************************************************************
+// Filename: TileFilterTester.cxx
+// Author:   F. Merritt
+// Created:  April 2004
+
+// DESCRIPTION
+// This class is to be called from the TileHitToRawChannel initialization section,
+// after TileFilterManager has been instantiated.  A reference to TileFilterManager 
+// is passed in the call, so it is possible to thoroughly test the program outside 
+// of the event loop.
+//
+// *************************************************************************************************
+#include "GaudiKernel/Chrono.h"
+//#include "GaudiKernel/ISvcLocator.h"
+
+#include "TileRecUtils/TileFilterTester.h"
+#include "TileRecUtils/TileFilterManager.h"
+#include "TileRecUtils/TileFitter.h"
+#include "TileRecUtils/TileFilterResult.h"
+#include "TileConditions/TileInfo.h"
+
+#include <CLHEP/Random/Randomize.h>
+#include <CLHEP/Matrix/Matrix.h>
+#include <CLHEP/Matrix/Vector.h>
+
+using namespace CLHEP;
+
+#include <iostream>
+#include <iomanip>
+
+// ==============================================================================================
+
+// Constructor
+TileFilterTester::TileFilterTester(TileFilterManager * tFilterManager, int mode, int test, bool lDebug)
+  : m_tileFilterManager(0)
+{
+  Fmode = mode;
+  Ftest = test;
+  std::cout << " Enter TileFilterTester constructor.  April" << std::endl;
+  m_tileFilterManager = tFilterManager;
+  // Get TileInfo from detStore
+  /* ********* Unable to retrieve detStor.  Needs to be fixed.
+  StatusCode sc = service("DetectorStore", m_detStore);
+  if(sc.isFailure()) {
+    std::cout << "TileFilterTester: Unable to retrieve pointer to Detector Store" << std::endl;
+    return sc;
+  }
+  sc = m_detStore->retrieve(m_tileInfo, m_infoName);
+  if(sc.isFailure()) {
+    std::cout << "TileFilterTester: Unableto retrieve TileInfo from DetectorStore" << std::endl;
+    return sc;
+  }
+  */ // ********** Unable to get preceding part working.  Must be fixed.
+  //
+  debug = lDebug;
+  Cmode = 2;
+  Ncross = 9;
+  Npileup = Ftest-2;
+  if(Npileup<0) Npileup=0;
+  if(Npileup>Ncross) Npileup=Ncross;
+  Namp=Npileup+1;
+  iAmpVec.reserve(Namp);
+  AmpVec.reserve(Namp);
+  Nconfig = m_tileFilterManager->get_NfitIndex(); 
+  if(debug) {
+    std::cout << " TileFilterTester:  Cmode=" << Cmode << ", Npileup=" << Npileup << std::endl;
+    for(int i=0; i<Namp; i++) {
+      std::cout << "    Namp=" << i << " => Nconfig=" << Nconfig[i] << std::endl;
+    }
+  }
+
+  return;
+}
+
+// Destructor
+TileFilterTester::~TileFilterTester() {
+}
+
+//
+// **********************************************************************************************
+//
+// Generate events for testing TileFilterManager and related classes.
+void TileFilterTester::GenEvents(int Nevent) {
+  //
+  // Set up the vector of Hit amplitudes that will be used to generate fake events.
+  // This are indexed according to the "crossing index", 0=intime and 1 to Ncross-1
+  // for the pileup amplitudes.  All amps are in RC units.
+  //
+  // Define the range of amplitudes to be generated.  
+  double ApileMin = 30.;
+  double ApileMax = 400.;
+  double AinTime = 100.;
+  double digSigma = 1.6;
+  // Define the pileup configuration that will be used to generate events.
+  if(Cmode==1) {     // Fixed configuration. Must define iAmpVec for first Namp elements.
+    iAmpVec[0]=0;
+    iAmpVec[1]=2;
+    iAmpVec[2]=5;
+  }
+  bool FSM=true;
+  if(FSM) {
+    Nevent = 1000000;
+  }
+  // Initialization for sums.
+    int Ngen = 0;
+    int Nrec = 0;
+    const int ncodeSize = 8;
+    int ncode[ncodeSize];
+    int ncodeg[ncodeSize];
+    int ncodeb[ncodeSize];
+    for(int i=0; i<ncodeSize; i++) {
+      ncode[i] = 0;
+      ncodeg[i] = 0;
+      ncodeb[i] = 0;
+    }
+    double Dsum = 0.;
+    double D2sum = 0.;
+    double Esum = 0.;
+    double E2sum = 0.;
+    int Nrecg = 0;
+    double Dsumg = 0.;
+    double D2sumg = 0.;
+    double Esumg = 0.;
+    double E2sumg = 0.;
+    int Nrecb = 0;
+    double Dsumb = 0.;
+    double D2sumb = 0.;
+    double Esumb = 0.;
+    double E2sumb = 0.;
+    int Nover = 0;
+    int Nunder = 0;
+    int Nmixed = 0;
+    int NampFinal[12];
+     
+  bool lPrint = debug;
+  lPrint = true;
+  bool lPrint2 = true;
+  int nPrint = 0;
+  int nPrint2= 0;
+  for(int i=0;i<12; i++) {
+    NampFinal[i]=0;
+  }
+  //
+  // ***** Start event loop. *****
+  //  std::cout << " Start event loop in TileFilterTester:  Nevent =" << Nevent << std::endl;
+  for(int ievent = 0; ievent<Nevent; ievent++) {
+    if(ievent>5) debug=false;
+    lPrint = debug;
+    if(ievent==9)  lPrint=true;
+    if(ievent==23)  lPrint=true;
+    if(ievent==235)  lPrint=true;
+    if(ievent==372)  lPrint=true;
+    if(ievent==460)  lPrint=true;
+    if(ievent==483)  lPrint=true;
+    if(ievent==501)  lPrint=true;
+    if(ievent==823)  lPrint=true;
+    if(ievent==959)  lPrint=true;
+    if(ievent==1220)  lPrint=true;
+    iAmpVec.clear();
+    iAmpVec.reserve(Namp);
+    int iConfig=0;
+
+    if(lPrint) std::cout << " TileFilterTester:  Start new event; ievent=" << ievent 
+		    << ", Fmode=" << Fmode << ", Cmode=" << Cmode << std::endl;
+    // Generate the pileup configuration.
+    if(Cmode==2) {
+      int Nindex = Nconfig[Npileup];
+      double Ranflat = RandFlat::shoot();
+      iConfig = (int)(Ranflat*Nindex);
+      if(Npileup==0) iConfig = 0; // Special case!
+      m_tileFilterManager->getVcross(Npileup, iConfig, iAmpVec);
+    }
+    //Generate the amplitudes.
+    AmpVec[0]=AinTime;
+    if(Npileup>0) {
+      for(int ipileup=0; ipileup<Npileup; ipileup++) {
+	double Ranflat = RandFlat::shoot();
+	AmpVec[ipileup+1] = ApileMin + Ranflat*(ApileMax-ApileMin);
+      }
+    }
+
+    if(lPrint) {
+      for(int iAmp=0; iAmp<Namp; iAmp++) {
+	std::cout << " i=" << iAmp << ", ipar=" << iAmpVec[iAmp] << ", Amp=" << AmpVec[iAmp] << std::endl;
+      }
+    }
+    //Generate the TileDigits amplitudes.
+    int Nparam = Npileup +2;
+    const int Ndig = 9;
+    HepVector digitsHep;
+    std::vector<float> digits(Ndig);
+    HepVector Param(Nparam);
+    HepMatrix SPD(Nparam,Ndig);
+    Param[0] = 50.;
+    double chisqGen = 0.;
+    for(int i=1; i<Nparam; i++) {
+      Param[i] = AmpVec[i-1];
+    }
+    //int iret;
+    /*iret =*/ m_tileFilterManager->MakeSPD(false, iAmpVec, SPD);
+    HepMatrix SDP = SPD.T();
+
+    digitsHep = SDP*Param;
+
+    //    if(lPrint) std::cout << " digits=";
+    // Convert to digits and add noise
+    double sigmaGen = 1.6;
+    for(int idig=0; idig<Ndig; idig++) {
+      double rang = RandGauss::shoot();
+      double noise = rang*sigmaGen;
+      chisqGen += rang*rang;
+      digits[idig] = digitsHep[idig]+noise;
+      //      if(lPrint) std::cout << " " << std::setw(6) << std::setprecision(2) << digitsHep[idig];
+    }
+    //    if(lPrint) std::cout << std::endl;
+
+    // ***************  Now test the fitting code!  ********************
+    TileFilterResult tResult(digits,digSigma);
+    int icode = m_tileFilterManager->FitDigits(tResult, lPrint);
+    if(lPrint) std::cout << "TileFilterTester.GenEvents: ievent=" << ievent << ", icode=" << icode << std::endl;
+
+    // Compare reconstruction to generation   
+    std::vector<int>& vcross = tResult.getVcrossRef();
+    int ncrgen = iAmpVec.size();
+    int ncrrec = vcross.size();
+    NampFinal[ncrrec] += 1;
+    int& iFitIndex = tResult.getFitIndexRef(); 
+    // Get the theoretical error on the in-time amplitude.
+    std::vector<double>& fitterErr = m_tileFilterManager->getFitterErr(Nparam, iConfig);
+    double Qerr = sigmaGen*fitterErr[1];
+
+    // Define lconfigOK if reconstructed configuration = generated configuration.
+    bool lconfigOK = (ncrrec == ncrgen);
+    if(iConfig!=iFitIndex) lconfigOK = false;
+    if(lconfigOK) {
+      for(int icr = 0; icr<ncrgen; icr++) {
+	if(iAmpVec[icr]!=vcross[icr]) lconfigOK = false;
+      }
+    }
+    lPrint2 = false;
+    if(!lconfigOK) {
+      lPrint2 = true;
+      nPrint2 +=1;
+      if(nPrint2>100) lPrint2=false;
+      if(lPrint2) {
+	std::cout << std::endl;
+	std::cout << " Discrepancy: ievent=" << ievent << ",  ncrgen=" << ncrgen << ", ncrrec=" << ncrrec 
+	     << ", icode=" << icode << "    iConfig=" << iConfig << ", iFitIndex=" << iFitIndex << std::endl;
+	std::cout << "        icrGen=";
+	for(int i=0; i<ncrgen; i++) {
+	  std::cout << " " << iAmpVec[i];
+	}
+	std::cout << "        icrRec=";
+	for(int i=0; i<ncrrec; i++) {
+	  std::cout << " " << vcross[i];
+	}
+	double& chisq = tResult.getChisqRef();
+	std::cout << "  (chi2=" << chisq << ", chi2G=" << chisqGen << ")" << std::endl;
+
+	for(int iAmp=0; iAmp<Namp; iAmp++) {
+	  std::cout << " i=" << iAmp << ", ipar=" << iAmpVec[iAmp] << ", Amp=" << AmpVec[iAmp] << std::endl;
+	}
+
+	nPrint += 1;
+	std::cout << "  digits=" ;
+	for(int idig=0; idig<Ndig; idig++) {
+	  std::cout << " " << std::setw(6) << std::setprecision(2) <<digits[idig];
+	}
+	std::cout << std::endl;
+	tResult.PrintFitParam();
+	tResult.SnapShot(2);
+	std::cout << std::endl; 
+	if(nPrint>10) lPrint=false;
+      }
+    }
+    // Now print out the results for comparison with generated amplitudes.
+    if(lPrint) {
+
+      tResult.PrintFitParam();
+      if(nPrint2>20) lPrint=false;
+    }
+    double& chisq = tResult.getChisqRef();
+    //    std::vector<int>& vcross = tResult.getVcrossRef();
+    HepVector& fitParam = tResult.getParamRef();
+    HepVector& fitErr = tResult.getErrRef();
+    double diff_ch = fitParam[1] - AmpVec[0];
+    int Npar = fitParam.num_row();
+    Ngen = Ngen + 1;
+    //    double err = fitErr[1];
+    double err = Qerr;
+    Nrec += 1;
+    if (icode>=0) ncode[icode] += 1;
+    Dsum += diff_ch;
+    D2sum += diff_ch*diff_ch;
+    Esum += err;
+    E2sum += err*err;
+
+    if(lconfigOK) {
+      Nrecg +=1;
+      if (icode>=0) ncodeg[icode] += 1;
+      Dsumg += diff_ch;
+      D2sumg += diff_ch*diff_ch;
+      Esumg += err;
+      E2sumg += err*err;
+    } else {
+      Nrecb +=1;
+      if(ncrrec>ncrgen) Nover += 1;
+      if(ncrrec<ncrgen) Nunder += 1;
+      if(ncrrec==ncrgen) Nmixed += 1;
+      if (icode>=0) ncodeb[icode] += 1;
+      Dsumb += diff_ch;
+      D2sumb += diff_ch*diff_ch;
+      Esumb += err;
+      E2sumb += err*err;
+    } // end of ievent loop.
+
+    if(lPrint) std::cout << "TileFilterTester event: Npar=" << Npar << ",  diff_ch =" 
+	 << std::setw(6) << std::setprecision(2) << diff_ch << " +-" << fitErr[1] 
+		    << ", chi2=" << chisq << ", chi2Gen=" << chisqGen << std::endl << std::endl;
+ }
+  // Calculate the mean displacement and sigma of the reconstructed events.
+  std::cout << std::endl;
+  std::cout << " *** TileFilterTester Summary:  Fmode=" << Fmode << ", Ftest=" << Ftest 
+       << ", NparamGen =" << Npileup+2 << ", Cmode=" << Cmode << ", Nevent=" << Nevent << std::endl;
+  double rchisqCut;
+  double chiCut;
+  m_tileFilterManager->getCuts(rchisqCut, chiCut);
+  std::cout << " Cuts applied:  rchisqCut=" << rchisqCut << ", chiCut=" << chiCut << std::endl;
+  std::cout << "     ApileMin=" << ApileMin << ", ApileMax=" << ApileMax << ", AmpInTime=" << AinTime << std::endl;
+  std::cout << " Compare difference (rec-gen) for:   Ngen=" << Ngen << ", Nrec=" << Nrec << std::endl;
+  double rm = Dsum/Nrec;
+  //double errm = Esum/Nrec;
+  double errsig = pow(E2sum/Nrec, 0.5);
+  double rsig = pow(D2sum/Nrec, 0.5);
+
+  double rmg = Dsumg/Nrecg;
+  //double errmg = Esumg/Nrecg;
+  double errsigg = pow(E2sumg/Nrecg, 0.5);
+  double rsigg = pow(D2sumg/Nrecg, 0.5);
+
+  double rmb = Dsumg/Nrecb;
+  //double errmb = Esumb/Nrecb;
+  double errsigb = pow(E2sumb/Nrecb, 0.5);
+  double rsigb = pow(D2sumb/Nrecb, 0.5);
+
+  std::cout << " All  configurations:  N=" << std::setw(7) <<Nrec << ",  Diff =" << std::setw(6) << std::setprecision(3)
+       << rm << ",  sig =" << rsig << "  (errsig=" << errsig << ")" << std::endl; 
+  std::cout << " Good configurations:  N=" << std::setw(7) << Nrecg << ",  Diff =" << std::setw(6) << std::setprecision(3)
+       << rmg << ",  sig =" << rsigg << "  (errsig=" << errsigg << ")" << std::endl; 
+  if(Nrecb>3) {
+  std::cout << " Bad  configurations:  N=" << std::setw(7) << Nrecb << ",  Diff =" << std::setw(6) << std::setprecision(3)
+       << rmb << ",  sig =" << rsigb << "  (errsig=" << errsigb << ")" << std::endl; 
+  std::cout << "     Nover=" << Nover << ",  Nunder=" << Nunder << ",  Nmixed=" << Nmixed << std::endl;
+  }
+  std::cout << std::endl;
+  // Print out summary of icode counts:
+  for(int i=0; i<ncodeSize; i++) {
+    std::cout << "     icode=" << i << " ==> ncnt=" << std::setw(7) << ncode[i] << ",   ncntg=" << ncodeg[i]
+	 << ",   ncntb=" << ncodeb[i] << std::endl;
+  }
+  std::cout << std::endl;
+  // Print out number of final amplitudes.
+  std::cout << " Number of final amplitudes:" << std::endl;
+  for(int i=0; i<12; i++) {
+    if(NampFinal[i] != 0) std::cout << "     Namp=" << i << "  =>" << NampFinal[i] << " events" << std::endl;
+  }
+
+ return;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileFitter.cxx b/TileCalorimeter/TileRecUtils/src/TileFitter.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..6fd7ecaba92a002b462c55c9b6c1a68fde4772f2
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileFitter.cxx
@@ -0,0 +1,221 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//************************************************************************************
+// Filename : TileFitter.cxx
+// Author   : F. Merritt, A. Aurisano
+// Created  : March 2004
+//
+// DESCRIPTION
+//
+//*************************************************************************************
+
+#include "TileRecUtils/TileFitter.h"
+#include "TileRecUtils/TileFilterResult.h"
+#include <iostream>
+#include <iomanip>
+#include "boost/io/ios_state.hpp"
+
+//std::vector<double> Amp;
+
+// Constructors
+TileFitter::TileFitter()
+  : Iconstraint(0)
+  , Camp()
+  , Ped_const(0.0)
+  , F2_sigPed(0.0)
+  , F2_sigPile(0.0)
+  , ND(0)
+  , NP(0)
+  , iconfig(0)
+  , vconfig()
+  , SPD()
+  , MISPD()
+  , ErrDiag()
+{
+}
+
+TileFitter::TileFitter(bool debug, int nrow, int ncol, int index, HepMatrix& S, int Icon)
+  : Iconstraint(0)
+  , Camp()
+  , Ped_const(0.0)
+  , F2_sigPed(0.0)
+  , F2_sigPile(0.0)
+  , ND(0)
+  , NP(0)
+  , iconfig(0)
+  , vconfig()
+  , SPD()
+  , MISPD()
+  , ErrDiag()
+{
+
+  // Set P_const.
+  Iconstraint = Icon;
+  NP = nrow;
+  ND = ncol;
+  iconfig = index;
+
+  //  if(nrow>ncol) debug=true;
+  Ped_const = 50.0;
+  F2_sigPed  = pow(1.6/10.,2.);
+  F2_sigPile = pow(1.6/50.,2.);
+
+  //  std::cout << "TileFitter constructor called for NP=" << NP << ", iconfig=" << iconfig << std::endl;
+  SPD = S;
+
+  //Calculate the matrices needed for fitting.  Save SPD and MISPD.
+  HepMatrix SPDT = SPD.T();
+  HepMatrix M    = SPD*SPDT;
+
+  // Add constraint terms to chisquare if Iconstraint>0.
+  if(Iconstraint>0) {
+    if(debug) {
+      std::cout << " add f*p:  F2_sigPed=" << F2_sigPed << ", Ped_const=" << Ped_const 
+	   << ", F2_sigPile=" << F2_sigPile << std::endl;
+    }
+    M[0][0] = M[0][0] + F2_sigPed;
+    if(Iconstraint>1) {
+      for(int i=2; i<NP; i++) {
+	M[i][i] = M[i][i] + F2_sigPile;
+      }
+    }
+  }
+
+  int err;
+  HepMatrix MI   = M.inverse(err);
+  MISPD = MI*SPD;
+
+  if(Iconstraint>0) {
+    HepVector temp(NP);
+    for(int i=0; i<10; i++) {
+      temp[i]=MI[0][i];
+    }
+    Camp = Ped_const*F2_sigPed*temp;
+    if(debug) {
+      std::cout << " Camp: ";
+      PrintVec(Camp);
+    }
+  }
+
+  for(int i=0; i<NP; i++) {
+    double err = sqrt(MI[i][i]);
+    ErrDiag.push_back(err);
+  }
+
+  if(debug) { 
+    std::cout << " SPD: ";
+    PrintMat(SPD);
+    std::cout << " SPDT: ";
+    PrintMat(SPDT);
+    HepMatrix Ident = M*MI;
+    std::cout << " M*MI:  ";
+    PrintMat(Ident);
+    std::cout << " MISPD: ";
+    PrintMat(MISPD);
+    std::cout << " errDiag=";
+    for(int i=0; i<NP; i++) {
+      std::cout  << ErrDiag[i] << " ";
+    }
+  std::cout << std::endl;
+  }
+}
+// ============================================================================== 
+// Destructor.
+TileFitter::~TileFitter() {
+  //  std::cout << " TileFitter destructor called: NP=" << NP << ", iconfig=" << iconfig << std::endl;
+  return;
+}
+// ============================================================================== 
+int TileFitter::FitAmp(TileFilterResult &tResult, bool lDebug) {
+  int iret = -1;
+  bool debugFitAmp = lDebug;
+  // Get reference to variables which will store results.
+  double sigDig = tResult.getSigDig();
+  HepVector& digits = tResult.getDigRef();
+  // int nrow = digits.num_row();
+  HepVector& Amp = tResult.getParamRef();
+  HepVector& Err = tResult.getErrRef();
+  HepVector& residuals = tResult.getResidRef();
+  double& chisq = tResult.getChisqRef();
+  if(debugFitAmp) {
+    std::cout << " Enter FitAmp. NP=" << NP << ", ND=" << ND << std::endl;
+    std::cout << " In FitAmp:  MISPD=" << std::endl;
+    PrintMat(MISPD);
+  }
+
+  // Multiply MISPD into digits to get fitted parameters. 
+
+  Amp = MISPD*digits;
+  if(Iconstraint>0) Amp = Amp + Camp;
+
+  // Right-multiply Fit into SPD to get predicted digits.
+  HepVector predict(ND);
+  predict = SPD.T()*Amp;
+
+  // Subtract digits from predict to get residuals, sum to get chisq*sig.
+  residuals = digits - predict;
+  chisq = residuals.normsq()/(sigDig*sigDig);
+  if(Iconstraint>0) chisq += pow(Amp[0]-Ped_const,2.)*F2_sigPed;
+  if(Iconstraint>1) {
+    for(int iamp=2; iamp<NP; iamp++) {
+      chisq = chisq + Amp[iamp]*Amp[iamp]*F2_sigPile;
+    }
+  }
+
+  // Get error on each parameter from ErrDiag.
+  HepVector newErr(NP);
+  for(int ip=0; ip<NP; ip++) {
+    newErr[ip]=sigDig*ErrDiag[ip];
+  }
+  Err = newErr;
+
+  if(debugFitAmp) {
+    std::cout << " predict: ";
+    PrintVec(predict);
+    std::cout << " residuals: ";
+    PrintVec(residuals);
+    //Check chisq.
+    //    double dig2 = digits.normsq();
+    //    double chisq2 = dig2 - dot(M*Amp,Amp);
+    //    std::cout << " chisq=" << chisq << ", chisq2=" << chisq2 << std::endl;
+  }
+  iret = 0;
+  return iret;
+}
+
+// ============================================================================== 
+int TileFitter::getIndex() {
+  return iconfig;
+}
+// ============================================================================== 
+std::vector<double>& TileFitter::getErrRef() {
+  return ErrDiag;
+}
+// ============================================================================== 
+void TileFitter::PrintMat(HepMatrix &mat) {
+  boost::io::ios_base_all_saver coutsave (std::cout);
+  int nrow = mat.num_row();
+  int ncol = mat.num_col();
+  std::cout << "  nrow=" << nrow << ", ncol=" << ncol << std::endl;
+  std::streamsize oldprec = std::cout.precision(4);
+  for (int irow=0; irow<nrow; irow++) {
+    std::cout << " irow=" << irow << ": Mat=";
+    for (int icol=0; icol<ncol; icol++) {
+      std::cout << std::setw(7) /* << std::setprecision(4) */ << mat[irow][icol];
+    }
+    std::cout << std::endl;
+  }
+  std::cout.precision(oldprec);
+}
+// ============================================================================== 
+void TileFitter::PrintVec(HepVector &vec) {
+  int nr = vec.num_row();
+  int nc = vec.num_col();
+  std::cout << " nr=" << nr << ", nc=" << nc << ",  vec=";
+  for(int i=0; i<nr; i++) {
+    std::cout << vec[i] << " ";
+  }
+  std::cout << std::endl;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilder.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilder.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..33ed2705baa3cb82702336b698ac5479b75f2a85
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilder.cxx
@@ -0,0 +1,736 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#define private public
+#include "TileEvent/TileRawChannel.h"
+#undef private
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+
+// Atlas includes
+#include "AthenaKernel/errorcheck.h"
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilder.h"
+#include "TileRecUtils/TileBeamInfoProvider.h"
+#include "TileRecUtils/ITileRawChannelTool.h"
+#include "TileEvent/TileDigits.h"
+#include "TileEvent/TileRawChannel.h"
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileConditions/TileInfo.h"
+
+
+static const InterfaceID IID_ITileRawChannelBuilder("TileRawChannelBuilder", 1, 0);
+
+const InterfaceID& TileRawChannelBuilder::interfaceID() {
+  return IID_ITileRawChannelBuilder;
+}
+
+static const int nChMax = 48;
+static const int nDmuMax = 16;
+static int m_last_drawer;
+static bool m_bad_drawer;
+static int m_error[nChMax];
+static int m_dmuerr[nDmuMax];
+
+void TileRawChannelBuilder::resetDrawer() {
+  m_last_drawer = -1;
+  m_bad_drawer = false;
+}
+
+void TileRawChannelBuilder::resetOverflows() {
+  m_overflows.clear();
+}
+
+Overflows_t& TileRawChannelBuilder::getOverflowedChannels() {
+  return m_overflows;
+}
+
+std::string TileRawChannelBuilder::getTileRawChannelContainerID() {
+  return m_TileRawChannelContainerID;
+}
+
+/**
+ * Constructor
+ */
+TileRawChannelBuilder::TileRawChannelBuilder(const std::string& type
+    , const std::string& name, const IInterface* parent)
+  : AthAlgTool(type, name, parent)
+  , m_TileRawChannelContainerID("TileRawChannelFiltered")
+  , m_rawChannelCnt(0)
+  , m_rChType(TileFragHash::Default)
+  , m_rChUnit(TileRawChannelUnit::ADCcounts)
+  , m_bsflags(0)
+  , m_tileID(0)
+  , m_tileHWID(0) 
+  , m_tileInfo(0)
+  , m_beamInfo("TileBeamInfoProvider/TileBeamInfoProvider")
+  , m_noiseFilterTools() // "TileRawChannelNoiseFilter/TileRawChannelNoiseFilter")
+  , m_trigType(0)
+  , m_idophys(false)
+  , m_idolas(false)
+  , m_idoped(false)
+  , m_idocis(false)
+  , m_cischan(-1)
+  , m_capdaq(100)
+  , m_evtCounter(0)
+  , m_chCounter(0)
+  , m_nChL(0)
+  , m_nChH(0)
+  , m_RChSumL(0.0)
+  , m_RChSumH(0.0)
+{
+  resetDrawer();
+  memset(m_error, 0, sizeof(m_error));
+  memset(m_dmuerr, 0, sizeof(m_dmuerr));
+
+  declareProperty("TileRawChannelContainer",m_TileRawChannelContainerID);
+  declareProperty("calibrateEnergy",m_calibrateEnergy = false);
+  declareProperty("correctTime",m_correctTime = false);
+  declareProperty("AmpMinForAmpCorrection",m_ampMinThresh = 15.0);
+  declareProperty("TimeMinForAmpCorrection",m_timeMinThresh = -25.0);
+  declareProperty("TimeMaxForAmpCorrection",m_timeMaxThresh =  25.0);
+  declareProperty("RunType",m_runType = 0);
+  declareProperty("BeamInfo", m_beamInfo);
+  declareProperty("NoiseFilterTools", m_noiseFilterTools);
+  declareProperty("DataPoolSize", m_dataPoollSize = -1);
+  declareProperty("UseDSPCorrection", m_useDSP = true);
+
+}
+
+/**
+ * Destructor
+ */
+TileRawChannelBuilder::~TileRawChannelBuilder() {
+}
+
+/**
+ * Initializer
+ */
+StatusCode TileRawChannelBuilder::initialize() {
+  m_trigType = m_runType;
+  m_idophys = ((m_trigType == 0) || (m_trigType == 1));
+  m_idolas = ((m_trigType == 2) || (m_trigType == 3));
+  m_idoped = ((m_trigType == 4) || (m_trigType == 5));
+  m_idocis = ((m_trigType == 8) || (m_trigType == 9));
+  m_cischan = -1;
+  m_capdaq = 0;
+  m_chCounter = 0;
+  m_evtCounter = 0;
+  m_rawChannelCnt = NULL;
+  m_nChL = m_nChH = 0;
+  m_RChSumL = m_RChSumH = 0.0;
+
+  ATH_MSG_INFO( "TileRawChannelBuilder::initialize()" );
+
+  // retrieve TileID helpers and TileIfno from det store
+  CHECK( detStore()->retrieve(m_tileID, "TileID") );
+  CHECK( detStore()->retrieve(m_tileHWID, "TileHWID") );
+
+  CHECK( detStore()->retrieve(m_tileInfo, "TileInfo") );
+
+  CHECK( m_beamInfo.retrieve() );
+
+  // access tools and store them
+  CHECK( m_noiseFilterTools.retrieve() );
+  ATH_MSG_DEBUG( "Successfully retrieve  NoiseFilterTools: " << m_noiseFilterTools );
+
+  if(m_calibrateEnergy){
+    ATH_MSG_DEBUG( "Obsolete calibrateEnergy flag is set to True in jobOptions - disabling it" );
+    m_calibrateEnergy = false;
+  }
+  
+  // check if we want to keep ADC counts or convert them to pCb
+  m_rChUnit = (m_calibrateEnergy) ? TileRawChannelUnit::PicoCoulombs
+                                  : TileRawChannelUnit::ADCcounts;
+
+  // if unit is not pCb, but already MeV one can use method TileRawChannelContainer::set_unit()
+  // later to declare that
+
+  // 8 upper bits of bsflags:
+  // UUPPSTTT
+  // 31,30 - units
+  // 29,28 - pulse type = 3 for offline reco
+  // 27    - 7(=0) or 9(=1) samples
+  // 24,25,26 - TileFragHash::TYPE - OF algorithm type
+  int nsamp = (m_tileInfo->NdigitSamples() > 7) ? 1 : 0;
+  m_bsflags = (m_rChUnit << 30) | (3 << 28) | (nsamp << 27) | (m_rChType << 24);
+
+  // bits 12-15 - various options
+  if (m_correctTime)  m_bsflags |= 0x1000;
+
+  if (msgLvl(MSG::DEBUG)) {
+    msg(MSG::DEBUG) << "TileRawChannelBuilder created, storing rc in '"
+                    << m_TileRawChannelContainerID << "'" << endmsg;
+    msg(MSG::DEBUG) << " calibrate energy = " << m_calibrateEnergy << endmsg;
+    msg(MSG::DEBUG) << " correct time = " << m_correctTime << endmsg;
+    msg(MSG::DEBUG) << " run type = " << m_runType << endmsg;
+  }
+
+  if (m_dataPoollSize < 0) m_dataPoollSize = m_tileHWID->channel_hash_max();
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TileRawChannelBuilder::finalize() {
+  ATH_MSG_INFO( "Finalizing" );
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TileRawChannelBuilder::createContainer() {
+  initLog();
+
+  // create TRC container and record in TES
+  m_rawChannelCnt = new TileRawChannelContainer(true, m_rChType, m_rChUnit, SG::VIEW_ELEMENTS);
+  m_rawChannelCnt->set_bsflags(m_bsflags);
+  StatusCode sc = evtStore()->record(m_rawChannelCnt, m_TileRawChannelContainerID);
+  if (sc.isFailure()) {
+    ATH_MSG_ERROR( "Error registering TileRawChannelCnt" );
+    delete m_rawChannelCnt;
+    m_rawChannelCnt = NULL;
+    return sc;
+  }
+  ATH_MSG_DEBUG( "Created TileRawChannelContainer '" << m_TileRawChannelContainerID << "'" );
+
+  return StatusCode::SUCCESS;
+}
+
+void TileRawChannelBuilder::initLog() {
+
+  // update only if there is new event
+  if (m_evtCounter != m_beamInfo->eventCounter()) {
+
+    m_evtCounter = m_beamInfo->eventCounter();
+    if (m_runType != 0) m_trigType = m_runType;
+    else m_trigType = m_beamInfo->trigType();
+
+    if (0 == m_trigType) {
+      m_idophys = (m_beamInfo->calibMode() == 0);
+      m_idolas = false;
+      m_idoped = false;
+      m_idocis = (m_beamInfo->calibMode() == 1);
+    } else {
+      m_idophys = (m_trigType <= 1);
+      m_idolas = ((m_trigType == 2) || (m_trigType == 3));
+      m_idoped = ((m_trigType == 4) || (m_trigType == 5));
+      m_idocis = ((m_trigType == 8) || (m_trigType == 9));
+    }
+
+    const unsigned int *cispar = m_beamInfo->cispar();
+    if (0 == cispar[7]) { // if capdaq not set, it can't be CIS event
+      if (m_idocis) { // cis flag was set incorrectly, change to ped
+        m_idoped = true;
+        m_idocis = false;
+      }
+      m_capdaq = 0.0;
+    } else {
+      m_capdaq = (cispar[7] < 10) ? 5.2 : 100.0;
+    }
+    m_cischan = cispar[8] - 1; // channel where CIS is fired (-1 = all channels)
+
+    ATH_MSG_DEBUG( "Trig type is " << m_trigType
+                  << "; dophys is " << ((m_idophys) ? "true" : "false")
+                  << "; dolas is " << ((m_idolas) ? "true" : "false")
+                  << "; doped is " << ((m_idoped) ? "true" : "false")
+                  << "; docis is " << ((m_idocis) ? "true" : "false")
+                  << "; capacitor is " << m_capdaq
+                  << "; cis chan is " << m_cischan );
+  }
+}
+
+TileRawChannel* TileRawChannelBuilder::rawChannel(const TileDigits* digits) {
+  ++m_chCounter;
+  ATH_MSG_WARNING( "Default constructor for rawChannel!" );
+  TileRawChannel *rawCh = new TileRawChannel(digits->adc_HWID(), 0.0, 0.0, 0.0);
+  return rawCh;
+}
+
+void TileRawChannelBuilder::fill_drawer_errors(const TileDigitsCollection* coll) {
+  const TileDQstatus* DQstatus = m_beamInfo->getDQstatus();
+
+  int frag = coll->identify();
+  int ros = (frag >> 8);
+  int drawer = (frag & 0xff);
+
+  m_last_drawer = frag;
+
+  memset(m_error, 0, sizeof(m_error));
+  memset(m_dmuerr, 0, sizeof(m_dmuerr));
+  int nch = 0;
+  bool bigain = DQstatus->isBiGain();
+  if (!bigain) { // in bigain runs we don't have DQ status fragment
+    for (int ch = 0; ch < nChMax; ch += 3) {
+      if (!DQstatus->isAdcDQgood(ros, drawer, ch, 0)) {
+        m_error[ch + 2] = m_error[ch + 1] = m_error[ch] = -3;
+        m_dmuerr[ch / 3] = 3;
+        nch += 3;
+      }
+    }
+  }
+  if (nch == nChMax) { // all bad - nothing to do
+    m_bad_drawer = true;
+    ATH_MSG_VERBOSE( "Drawer 0x" << MSG::hex << frag << MSG::dec
+                    << " is bad - skipping bad patterns check " );
+    return;
+  } else {
+    m_bad_drawer = false;
+    ATH_MSG_VERBOSE(  "Drawer 0x" << MSG::hex << frag << MSG::dec
+                    << " looking for bad patterns in digits" );
+  }
+
+  float mindig, maxdig;
+  int nchbad[2] = { 0, 0 };
+
+  // Iterate over all digits in this collection
+  TileDigitsCollection::const_iterator digitItr = coll->begin();
+  TileDigitsCollection::const_iterator lastDigit = coll->end();
+
+  for (; digitItr != lastDigit; ++digitItr) {
+    const TileDigits * pDigits = (*digitItr);
+    HWIdentifier adcId = pDigits->adc_HWID();
+    int channel = m_tileHWID->channel(adcId);
+    int gain = m_tileHWID->adc(adcId);
+
+    if (m_error[channel]) {
+      ATH_MSG_VERBOSE( "BadCh " << ros
+                        << "/" << drawer
+                        << "/" << channel
+                        << "/" << gain << " BAD DQ STATUS ");
+
+    } else {
+
+      int err = CorruptedData(ros, drawer, channel, gain, pDigits->samples(), mindig, maxdig);
+
+      if (err) {
+
+        m_error[channel] = err;
+        if (err > -5) {
+          ++m_dmuerr[channel / 3];
+          ++nchbad[channel / 24];
+        }
+
+        if (msgLvl(MSG::VERBOSE)) {
+
+          msg(MSG::VERBOSE) << "BadCh " << ros
+                            << "/" << drawer
+                            << "/" << channel
+                            << "/" << gain;
+          if (err) {
+            if (err < -5) msg(MSG::VERBOSE) << " Warning " << err;
+            else msg(MSG::VERBOSE) << " Error " << err;
+          }
+          if (mindig > 2046.99) msg(MSG::VERBOSE) << " BADDQ";
+          if (maxdig > 1022.9) msg(MSG::VERBOSE) << " Overflow";
+          if (mindig < 0.1) msg(MSG::VERBOSE) << " Underflow";
+          if (err < 0) msg(MSG::VERBOSE) << " Const";
+
+          msg(MSG::VERBOSE) << " samp=";
+          std::vector<float> digits = pDigits->samples();
+          for (unsigned int i = 0; i < digits.size(); ++i) {
+            msg(MSG::VERBOSE) << " " << digits[i];
+          }
+          msg(MSG::VERBOSE) << endmsg;
+        }
+
+      } else {
+        if (mindig < 0.01) err += 1;
+        if (maxdig > 1022.99) err += 2;
+        if (err) m_error[channel] = err - 10;
+      }
+    }
+  }
+
+  // check if we want to mask half a drawer 
+  // in this case set error = -4 for channels which were good before
+
+  int ndmubad[2] = { 0, 0 };
+  int dmu = 0;
+  for (; dmu < nDmuMax / 2; ++dmu) { // first half
+    if (m_dmuerr[dmu] > 1)
+      ++ndmubad[0]; // count DMUs with at least two bad channels
+  }
+  for (; dmu < nDmuMax; ++dmu) { // second half
+    if (m_dmuerr[dmu] > 1)
+      ++ndmubad[1]; // count DMUs with at least two bad channels
+  }
+
+  int ndmulimit[2] = { 3, 3 }; // max number of bad DMUs when half-drawer is not yet masked
+                               // if 4 DMUs will be bad - mask whole half-drawer
+  if (frag > 0x2ff) { // if extended barrel
+    if (frag == 0x30e || frag == 0x411)
+      ndmulimit[0] = 4; // in EB special one DMU is always bad (missing)
+    ndmulimit[1] = 5; // in second half of EB 4 DMUs ara always bad (missing)
+                      // only if 7 DMUs are bad, mask whole half-drawer
+  }
+
+  bool printall = true;
+  for (int p = 0; p < 2; ++p) {
+    if (ndmubad[p] > ndmulimit[p] && nchbad[p] > 0) {
+      if (msgLvl(MSG::VERBOSE)) {
+        msg(MSG::VERBOSE) << "Drawer 0x" << MSG::hex << frag << MSG::dec
+                          << " masking whole " << ((p) ? "second" : "first")
+                          << " half" << endmsg;
+        if (printall) {
+          msg(MSG::VERBOSE) << "nDMuErr ";
+          for (int d = 0; d < nDmuMax; ++d) {
+            msg(MSG::VERBOSE) << " " << m_dmuerr[d];
+          }
+          msg(MSG::VERBOSE) << " total " << ndmubad[p] << " errors" << endmsg;
+
+          msg(MSG::VERBOSE) << "ChErr ";
+          int ch = 0;
+          while (ch < nChMax) {
+            msg(MSG::VERBOSE) << " " << m_error[ch++];
+            msg(MSG::VERBOSE) << " " << m_error[ch++];
+            msg(MSG::VERBOSE) << " " << m_error[ch++];
+            msg(MSG::VERBOSE) << "  ";
+          }
+
+          msg(MSG::VERBOSE) << " total " << nchbad[p]
+                            << " bad patterns" << endmsg;
+          printall = false;
+        }
+      }
+      int ch = (p) ? nChMax / 2 : 0;
+      int chmax = (p) ? nChMax : nChMax / 2;
+      for (; ch < chmax; ++ch) {
+        if (m_error[ch] == 0 || m_error[ch] < -5) { // channel was good before
+          m_error[ch] = -4;
+        }
+      }
+    }
+  }
+
+}
+
+const char * TileRawChannelBuilder::BadPatternName(float ped) {
+  static const char * errname[25] = {
+      "-10 - good signal",
+      "-9 - underflow",
+      "-8 - overflow",
+      "-7 - underflow and overflow",
+      "-6 - constant signal",
+      "-5 - disconnected channel",
+      "-4 - half a drawer masked",
+      "-3 - bad DQ status",
+      "-2 - underflow in all samples",  
+      "-1 - overflow in all samples",
+      "0 - unknown error",
+      "1 - jump from zero to saturation",
+      "2 - samples with zeros",
+      "3 - at least two saturated. others - close to pedestal",
+      "4 - two distinct levels with at least 2 samples each",
+      "5 - pedestal with jump up in one sample",
+      "6 - pedestal with jump down in one sample",
+      "7 - signal with jump up in one sample",
+      "8 - signal with jump down in one sample",
+      "9 - base line above threshold in low gain",
+      "10 - jump down in first sample in low gain",
+      "11 - jump down in last sample in low gain",
+      "12 - jump up in one sample above const",
+      "13 - jump down in one sample below const",
+      "14 - unknown error"
+  };
+  
+  return errname[std::min(24, std::max(0, int((ped + 500) / 10000.)))];
+}
+
+    
+void TileRawChannelBuilder::build(const TileDigitsCollection* coll) {
+  int frag = coll->identify();
+
+  // make sure that errror array is up-to-date
+  if (frag != m_last_drawer) {
+    fill_drawer_errors(coll);
+  }
+
+  // Iterate over all digits in this collection
+  TileDigitsCollection::const_iterator digitItr = coll->begin();
+  TileDigitsCollection::const_iterator lastDigit = coll->end();
+
+  for (; digitItr != lastDigit; ++digitItr) {
+
+    TileRawChannel* rch = rawChannel((*digitItr));
+    int err = m_error[m_tileHWID->channel(rch->adc_HWID())];
+
+    if (err) {
+      if (err == -8 || err == -7) m_overflows.push_back(std::make_pair(rch, (*digitItr)));
+      float ped = rch->pedestal() + 100000 + 10000 * err;
+      rch->setPedestal(ped);
+      if (msgLvl(MSG::VERBOSE) && !m_bad_drawer) {
+        if (err < -5) {
+          msg(MSG::VERBOSE) << "BadCh " << m_tileHWID->to_string(rch->adc_HWID())
+                            << " warning = " << BadPatternName(ped) << endmsg;
+        } else {
+          msg(MSG::VERBOSE) << "BadCh " << m_tileHWID->to_string(rch->adc_HWID())
+                            << " error = " << BadPatternName(ped) << endmsg;
+        }
+      }
+    }
+    m_rawChannelCnt->push_back(rch);
+  }
+}
+
+StatusCode TileRawChannelBuilder::commitContainer() {
+  ToolHandleArray<ITileRawChannelTool>::iterator itrTool = m_noiseFilterTools.begin();
+  ToolHandleArray<ITileRawChannelTool>::iterator endTool = m_noiseFilterTools.end();
+
+  const TileRawChannelContainer * dspCnt = m_beamInfo->dspContainer();
+
+  if ( m_useDSP && (m_beamInfo->incompleteDigits() || m_chCounter<12288) && dspCnt && itrTool!=endTool) {
+    ATH_MSG_DEBUG( "Incomplete container - use noise filter corrections from DSP container" );
+    if (m_beamInfo->noiseFilterApplied()) {
+      ATH_MSG_DEBUG( "Noise filter was already applied to DSP container before, use it as it is" );
+    } else {
+      for (;itrTool!=endTool;++itrTool){
+        if ((*itrTool)->process(dspCnt).isFailure()) {
+          ATH_MSG_ERROR( " Error status returned from noise filter " );
+        } else {
+          ATH_MSG_DEBUG( "Noise filter applied to DSP container" );
+        }
+      }
+      m_beamInfo->setNoiseFilterApplied(true);
+    }
+
+    TileRawChannelContainer::const_iterator itColl = m_rawChannelCnt->begin();
+    TileRawChannelContainer::const_iterator itCollEnd = m_rawChannelCnt->end();
+
+    TileRawChannelContainer::const_iterator dtColl = dspCnt->begin();
+    TileRawChannelContainer::const_iterator dtCollEnd = dspCnt->end();
+
+    // Go through all TileRawChannelCollections
+    for (; itColl != itCollEnd && dtColl !=dtCollEnd; ++itColl, ++dtColl) {
+      const TileRawChannelCollection *coll = (*itColl);
+      const TileRawChannelCollection *dcoll = (*dtColl);
+
+      if (coll->identify() != dcoll->identify()) {
+
+        ATH_MSG_ERROR( " Error in applying noise corrections " << MSG::hex 
+                       << " collection IDs 0x" << coll->identify() <<  " and 0x" << dcoll->identify() 
+                       << " do not match " << MSG::dec );
+        break;
+      }
+      
+      // iterate over all channels in a collection
+      TileRawChannelCollection::const_iterator rchItr=coll->begin();
+      TileRawChannelCollection::const_iterator lastRch=coll->end();
+
+      TileRawChannelCollection::const_iterator dspItr=dcoll->begin();
+      TileRawChannelCollection::const_iterator dspLast=dcoll->end();
+
+      for(; rchItr!=lastRch; ++rchItr) {
+        TileRawChannel* rch = (*rchItr);
+        HWIdentifier adc_id = rch->adc_HWID();
+        while (adc_id != (*dspItr)->adc_HWID() && dspItr != dspLast) {
+          ++dspItr;
+        }
+        if (dspItr != dspLast) {
+          float corr = (*dspItr)->m_pedestal;
+          ATH_MSG_VERBOSE( "Ch "<<m_tileHWID->to_string(adc_id)
+                           <<" amp " << rch->m_amplitude[0] << " ped " << rch->m_pedestal 
+                           << " corr " << corr );
+          rch->m_amplitude[0] -= corr;
+          rch->m_pedestal += corr;
+        } else {
+          ATH_MSG_ERROR(" Error in applying noise corrections " 
+                        << " can not find channel in DSP container with HWID "
+                        << m_tileHWID->to_string(adc_id) );
+          break;
+        }
+      }
+    }
+    
+  } else {
+
+    for (; itrTool != endTool; ++itrTool) {
+      if ((*itrTool)->process(m_rawChannelCnt).isFailure()) {
+        ATH_MSG_ERROR( " Error status returned from noise filter " );
+      } else {
+        ATH_MSG_DEBUG( "Noise filter applied to the container" );
+      }
+    }
+  }
+  
+  ATH_MSG_DEBUG( " nCh=" << m_chCounter
+                << " nChH/L=" << m_nChH << "/" << m_nChL
+                << " RChSumH/L=" << m_RChSumH << "/" << m_RChSumL );
+
+    // ATH_MSG_DEBUG( m_chCounter << " channels stored in '"
+    //                  << m_TileRawChannelContainerID << "' " );
+
+
+  //lock RawChannel container
+  StatusCode sc = evtStore()->setConst(m_rawChannelCnt);
+  if (sc.isFailure()) {
+    ATH_MSG_ERROR( " Could not lock TileRawChannelContainer '"
+                  << m_TileRawChannelContainerID << "'" );
+  }
+  m_rawChannelCnt = NULL;
+
+  endLog();
+
+  return sc;
+}
+
+void TileRawChannelBuilder::endLog() {
+  m_chCounter = 0;
+  m_nChL = m_nChH = 0;
+  m_RChSumL = m_RChSumH = 0.0;
+
+}
+
+double TileRawChannelBuilder::correctAmp(double phase) {
+// estimation from Belen for rel 14.0.0
+  /*double a,b,c;
+   if(fabs(phase)<5.){
+   a=0.137; b=0.0877; c=0.0865;
+   }else{
+   a=0.565; b=0.116; c=0.0751;
+   }
+   double corr=(1+(a+b*phase+c*phase*phase)/100.);
+   */
+
+// estimation from Vakhtang for rel 14.4.0
+  double k = (phase < 0.0 ? 0.0009400 : 0.0010160);
+  double corr = (1.0 + k * phase * phase);
+
+  return corr;
+}
+
+int TileRawChannelBuilder::CorruptedData(int ros, int drawer, int channel, int gain,
+    const std::vector<float> & digits, float &dmin, float &dmax) {
+  bool eb = (ros > 2);
+  bool ebsp = ((ros == 3 && drawer == 14) || (ros == 4 && drawer == 17));
+  bool empty = ((eb && ((channel > 23 && channel < 30) || channel > 41)) || (ebsp && channel < 3));
+  bool not_gap = !(empty || (eb && (channel == 0 || channel == 1 || channel == 12 || channel == 13))
+      || (ebsp && (channel == 18 || channel == 19)));
+
+  const float epsilon = 4.1; // allow +/- 2 counts fluctuations around const value
+  const float delta[4] = { 29.9, 29.9, 49.9, 99.9 }; // jump levels between constLG, constHG, non-constLG, non-constHG
+  const float level1 = 99.9; // jump from this level to 1023 is bad 
+  const float level2 = 149.9; // base line at this level in low gain is bad
+  const float narrowLevel[2] = { 29.9, 49.9 }; // minimal amplitude for narrow pulses
+  const float delt = std::min(std::min(std::min(delta[0], delta[1]), std::min(delta[2], delta[3])),
+      std::min(narrowLevel[0], narrowLevel[1]));
+  const float secondMaxLevel = 0.3;
+
+  int error = 0;
+
+  unsigned int nSamp = digits.size();
+  if (nSamp) {
+    dmin = dmax = digits[0];
+    unsigned int pmin = 0;
+    unsigned int pmax = 0;
+    unsigned int nzero = (dmin < 0.01) ? 1 : 0;
+    unsigned int nover = (dmax > 1022.99) ? 1 : 0;
+
+    for (unsigned int i = 1; i < nSamp; ++i) {
+      float dig = digits[i];
+      if (dig > dmax) {
+        dmax = dig;
+        pmax = i;
+      } else if (dig < dmin) {
+        dmin = dig;
+        pmin = i;
+      }
+      if (dig < 0.01) ++nzero;
+      else if (dig > 1022.99) ++nover;
+    }
+
+    float dmaxmin = dmax - dmin;
+    //std::cout << " ros " << ros << " drawer " << drawer << " channel " << channel << " not_gap " << not_gap << " nzero " << nzero << " nover " << nover << std::endl;
+
+    if (dmin > 1022.99) { // overflow in all samples
+      error = (dmin > 2046.99) ? -3 : -1; // dmin=2047 - masking in overlay job (set in TileDigitsMaker)
+
+    } else if (dmax < 0.01) { // underflow in all samples
+      error = (empty) ? -5 : -2; // set different type of errors for exsiting and non-existing channels
+
+    } else if (dmaxmin < 0.01) { // constant value in all samples
+      error = -6;
+
+    } else if (nzero && nover) { // jump from zero to saturation
+      error = 1;
+
+    } else if ((nzero && (not_gap || empty)) || nzero > 1) { // one sample at zero in normal channel
+      error = 2;                                           // or 2 samples at zero in gap/crack/MBTS
+
+    } else if (gain == 0 && dmin > level2) { // baseline above threshold in low gain is bad
+      error = 9;
+
+    } else if (dmaxmin > delt) { // check that max-min is above minimal allowed jump
+
+      float abovemin = dmax;
+      float belowmax = dmin;
+      unsigned int nmin = 0;
+      unsigned int nmax = 0;
+      for (unsigned int i = 0; i < nSamp; ++i) {
+        float smp = digits[i];
+        if (smp - dmin < epsilon) {
+          ++nmin;
+        }
+        if (dmax - smp < epsilon) {
+          ++nmax;
+        }
+        if (smp < abovemin && smp > dmin) {
+          abovemin = smp;
+        }
+        if (smp > belowmax && smp < dmax) {
+          belowmax = smp;
+        }
+      }
+      // more than two different values - shift index by 2, i.e. use thresholds for non-const levels
+      int gainInd = (abovemin != dmax || belowmax != dmin) ? gain + 2 : gain;
+      bool big_jump = (dmaxmin > delta[gainInd]);
+      bool max_in_middle = (pmax > 0 && pmax < nSamp - 1);
+      bool min_in_middle = (pmin > 0 && pmin < nSamp - 1);
+
+      if (nover > 1 && belowmax < level1) {  // at least two saturated. others - close to pedestal
+        error = 3;
+      } else if (nmax + nmin == nSamp && big_jump) {
+        if (nmax > 1 && nmin > 1) { // at least 2 samples at two distinct levels
+          error = 4;
+        } else if (nmax == 1) {
+          if (max_in_middle) { // jump up in one sample, but not at the edge
+            error = 5;
+          }
+        } else if (nmin == 1) { // jump down in one sample
+          error = 6;
+        }
+      }
+      if (error == 0 && dmaxmin > narrowLevel[gain]) {
+        float secondMax = dmaxmin * secondMaxLevel;
+        float dminPlus = dmin + secondMax;
+        float dmaxMinus = dmax - secondMax;
+        if (not_gap) { // jumps above two (or one) neighbour samples
+          if (max_in_middle && std::max(digits[pmax - 1], digits[pmax + 1]) < dminPlus) {
+            error = 7; // jump up in one sample in the middle, which is much higher than two neighbours
+          } else if (min_in_middle && std::min(digits[pmin - 1], digits[pmin + 1]) > dmaxMinus) {
+            error = 8; // jump down in one sample, which is much lower than two neighbours
+          } else if (big_jump && gain == 0) { // check first and last sample only in low gain
+            if (pmin == 0 && digits[1] > dmax - secondMax) {
+              error = 10; // jump down in first sample. which is much lower than next one
+            } else if (pmin == nSamp - 1 && digits[pmin - 1] > dmax - secondMax) {
+              error = 11; // jump down in last sample. which is much lower than previous one
+            }
+          }
+        }
+        if (!error && big_jump) { // jumps above all samples 
+          if ((max_in_middle || gain == 0) && nmax == 1 && belowmax < dminPlus) {
+            error = 12; // jump up in one sample in the middle, which is much higher than all others
+          } else if ((min_in_middle || gain == 0) && nmin == 1 && abovemin > dmaxMinus) {
+            error = 13; // jump down in one sample, which is much lower than all others (
+          }
+        }
+      }
+    }
+
+  } else {
+    dmin = dmax = 0.0;
+  }
+
+  return error;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderFitFilter.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderFitFilter.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..35a9d95c92f53d64b705264a53b443e2099cf78e
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderFitFilter.cxx
@@ -0,0 +1,1536 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// small hack to enable datapool usage
+#define private public
+#define protected public
+#include "TileEvent/TileRawChannel.h"
+#undef private
+#undef protected
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+#include "AthenaKernel/errorcheck.h"
+
+// Atlas includes
+#include "DataModel/DataPool.h"
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilderFitFilter.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileDigitsContainer.h"
+#include "TileEvent/TileDigits.h"
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileIdentifier/TileTrigType.h"
+#include "TileConditions/TileInfo.h"
+#include "TileConditions/TileCablingService.h"
+
+// lang include
+#include <algorithm>
+#include <cmath>
+
+
+static const InterfaceID IID_ITileRawChannelBuilderFitFilter
+       ("TileRawChannelBuilderFitFilter", 1, 0);
+
+const InterfaceID& TileRawChannelBuilderFitFilter::interfaceID( ) { 
+  return IID_ITileRawChannelBuilderFitFilter;
+  //return TileRawChannelBuilder::interfaceID();
+}
+
+/**
+ * Constructor
+ */
+TileRawChannelBuilderFitFilter::TileRawChannelBuilderFitFilter(const std::string& type
+   , const std::string& name, const IInterface *parent)
+  : TileRawChannelBuilder(type, name, parent)
+  , m_t0fit(0.0)
+  , m_ipeak0(0)
+  , m_min_time(0.0)
+  , m_max_time(0.0)
+  , m_min_tau(0)
+  , m_max_tau(0.0)
+  , m_pulsevar(0)
+{  
+  //declare interfaces
+  declareInterface< TileRawChannelBuilder >( this );
+  declareInterface< TileRawChannelBuilderFitFilter >(this);
+
+  m_TileRawChannelContainerID = "TileRawChannelFit";
+
+  //declare properties
+  declareProperty("FrameLength",m_frameLength = 9);
+  declareProperty("MaxIterate",m_maxIterate = 9);
+  declareProperty("NoiseLowGain",m_noiseLow = 0.6);
+  declareProperty("NoiseHighGain",m_noiseHigh = 1.5);
+  declareProperty("RMSChannelNoise",m_RMSChannelNoise = 3);
+  declareProperty("ExtraSamplesLeft",m_extraSamplesLeft=0);   // increase window on left side
+  declareProperty("ExtraSamplesRight",m_extraSamplesRight=0); // increase window on right side
+  declareProperty("SaturatedSampleError",m_SaturatedSampleError = 6.0);
+  declareProperty("ZeroSampleError",m_ZeroSampleError = 100.0);
+  declareProperty("NoiseThresholdRMS",m_NoiseThresholdRMS = 3.0);
+  declareProperty("MaxTimeFromPeak",m_MaxTimeFromPeak = 250.0);
+}
+
+/**
+ * Destructor
+ */
+TileRawChannelBuilderFitFilter::~TileRawChannelBuilderFitFilter(){ 
+}
+
+/**
+ * Initializer
+ */
+StatusCode TileRawChannelBuilderFitFilter::initialize() {
+
+  ATH_MSG_INFO( "TileRawChannelBuilderFitFilter::initialize()" );
+
+  m_rChType = TileFragHash::FitFilter;
+
+  // init in superclass
+  CHECK( TileRawChannelBuilder::initialize() );
+
+  // Get number of samples from TileInfo - ignore jobOptions settings
+  m_frameLength = m_tileInfo->NdigitSamples();
+  ATH_MSG_DEBUG( "NSamples = " << m_frameLength );
+  
+  // Get pulse shapes from TileInfo
+  m_pulsevar = m_tileInfo->getPulseShapes();
+
+  // Determine peak sample position 
+  // peak sample position defines t=0 
+  m_ipeak0 = (int) (m_frameLength) / 2 + (m_frameLength % 2) - 1;
+
+  // Min and max time are now calculated based on m_framelength - i.e. on the
+  // number of 25 ns samples read out. Don't forget t=0 corresponds to 
+  // m_ipeak0-th sample
+  m_min_time = DTIME * (0 - m_ipeak0 - m_extraSamplesLeft);
+  m_max_time = DTIME * (m_frameLength - 1 - m_ipeak0 + m_extraSamplesRight);
+  // maximal jump during one iteration
+  m_max_tau = (m_max_time - m_min_time);
+  m_min_tau = -m_max_tau;
+
+  ATH_MSG_DEBUG( " ipeak0=" << m_ipeak0
+                << " min_time=" << m_min_time
+                << " max_time=" << m_max_time
+                << " min_tau=" << m_min_tau
+                << " max_tau=" << m_max_tau );
+
+  // Speedup for physics processing (max_iter=1):
+  //  read initial pulse shapes into arrays
+  m_fnpar[0] = 0.0;
+  m_fnpar[1] = 0.0;
+  m_fnpar[2] = 1.0;
+  m_t0fit = 0.0;
+
+  m_gphyslo.reserve(m_frameLength);
+  m_dgphyslo.reserve(m_frameLength);
+  m_gphyshi.reserve(m_frameLength);
+  m_dgphyshi.reserve(m_frameLength);
+
+  double rsamp;
+  for (int isamp = 0; isamp < m_frameLength; ++isamp) {
+    rsamp = (isamp) * 1.0;
+    m_gphyslo.push_back(scaledpulse(rsamp, &(m_pulsevar->m_tlphys), &(m_pulsevar->m_ylphys)));
+    m_dgphyslo.push_back(pulse(rsamp, &(m_pulsevar->m_tdlphys), &(m_pulsevar->m_ydlphys)));
+    m_gphyshi.push_back(scaledpulse(rsamp, &(m_pulsevar->m_thphys), &(m_pulsevar->m_yhphys)));
+    m_dgphyshi.push_back(pulse(rsamp, &(m_pulsevar->m_tdhphys), &(m_pulsevar->m_ydhphys)));
+  }
+  
+  if (m_RMSChannelNoise < 0 || m_RMSChannelNoise > 3) {
+    m_RMSChannelNoise = 3;
+  }
+  
+  if (msgLvl(MSG::DEBUG)) {
+    switch (m_RMSChannelNoise) {
+      case 1:
+        msg(MSG::DEBUG) << " noise for all channels from noiseOrig array (OBSOLETE!!!) " << endmsg;
+        break;
+      case 2:
+        msg(MSG::DEBUG) << " noise for all channels from noiseNk array (OBSOLETE!!!)  " << endmsg;
+        break;
+      case 3:
+        msg(MSG::DEBUG) << " noise for all channels from Conditions DB ";
+        if (TileCablingService::getInstance()->getTestBeam()) {
+          msg(MSG::DEBUG) << " rmsLow(LBA01/0) = " << m_tileInfo->DigitsPedSigma(TileID::LOWGAIN, 0, 20)
+                          << " rmsHi(LBA01/0) = " << m_tileInfo->DigitsPedSigma(TileID::HIGHGAIN, 0, 20)
+                          << endmsg;
+        } else {
+          msg(MSG::DEBUG) << endmsg;
+        }
+        break;
+      default:
+        msg(MSG::DEBUG) << " common noise for all channels (OBSOLETE): " << endmsg;
+        msg(MSG::DEBUG) << " rmsLow = " << m_noiseLow
+                        << " rmsHi = " << m_noiseHigh << endmsg;
+        break;
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TileRawChannelBuilderFitFilter::finalize() {
+
+  ATH_MSG_DEBUG( "Finalizing" );
+  return StatusCode::SUCCESS;
+}
+
+TileRawChannel* TileRawChannelBuilderFitFilter::rawChannel(const TileDigits* digits) {
+
+  ++m_chCounter;
+
+  const HWIdentifier adcId = digits->adc_HWID();
+  
+  ATH_MSG_VERBOSE( "Running FitFilter for TileRawChannel with HWID "
+                  << m_tileHWID->to_string(adcId) );
+
+  // tmp variables for filtering
+  double amplitude = 0.0;
+  double chi2 = 0.0;
+  double time = 0.0;
+  double pedestal = 0.0;
+
+  // use fit filter
+  pulseFit(digits, amplitude, time, pedestal, chi2);
+  
+  // fit filter calib
+  // note that when called from TileROD_Decoder, m_calibrateEnergy is set
+  // from TileROD_Decoder...
+  if (m_calibrateEnergy) {
+    amplitude = m_tileInfo->CisCalib(adcId, amplitude);
+  }
+
+  ATH_MSG_VERBOSE( "Creating RawChannel"
+                  << " a=" << amplitude
+                  << " t=" << time
+                  << " q=" << chi2
+                  << " ped=" << pedestal );
+
+
+  // return new TileRawChannel
+  //  TileRawChannel *rawCh = new TileRawChannel(adcId,amplitude,time,chi2,pedestal);
+
+  static DataPool<TileRawChannel> tileRchPool(m_dataPoollSize);
+  TileRawChannel *rawCh = tileRchPool.nextElementPtr();
+  rawCh->m_adc_hwid = adcId;
+  rawCh->m_amplitude.resize(1);
+  rawCh->m_amplitude[0] = amplitude;
+  rawCh->m_time.resize(1);
+  rawCh->m_time[0] = time;
+  rawCh->m_quality.resize(1);
+  rawCh->m_quality[0] = chi2;
+  rawCh->m_pedestal = pedestal;
+
+  if (m_correctTime && chi2 > 0) {
+    rawCh->insertTime(m_tileInfo->TimeCalib(adcId, time));
+    ATH_MSG_VERBOSE( "Correcting time, new time=" << rawCh->time() );
+  }
+  
+  int gain = m_tileHWID->adc(adcId);
+  if (TileID::HIGHGAIN == gain) {
+    ++m_nChH;
+    m_RChSumH += amplitude;
+  } else {
+    ++m_nChL;
+    m_RChSumL += amplitude;
+  }
+
+  return rawCh;
+}
+
+/**
+ * Calculate energy, time and chi2 for one channel
+ * using fitted pulse shape. Method uses HFITV.
+ * @param samples TileDigits
+ */
+void TileRawChannelBuilderFitFilter::pulseFit(const TileDigits *digit
+    , double &p_amplitude, double &p_time, double &p_pedestal, double &p_chi2) {
+
+  p_amplitude = 0.0;
+  p_time = 0.0;
+  p_pedestal = 0.0;
+  p_chi2 = MAX_CHI2;
+
+  const HWIdentifier adcId = digit->adc_HWID();
+  int ros = m_tileHWID->ros(adcId);
+  int channel = m_tileHWID->channel(adcId);
+  int igain = m_tileHWID->adc(adcId);
+
+  // Estimate channel noise
+  double rms = 0.0;
+  int noise_channel = (ros < 3) ? channel : channel + 48;
+
+  if (igain == 0) {
+    switch (m_RMSChannelNoise) {
+      case 3:
+        rms = m_tileInfo->DigitsPedSigma(adcId);
+        if (rms > 0.0) break;
+      case 2:
+        rms = m_pulsevar->m_noiseNkLo[noise_channel];
+        if (rms > 0.0) break;
+      case 1:
+        rms = m_pulsevar->m_noiseOrigLo[noise_channel];
+        if (rms > 0.0) break;
+      default:
+        rms = m_noiseLow;
+    }
+  } else if (igain == 1) {
+    switch (m_RMSChannelNoise) {
+      case 3:
+        rms = m_tileInfo->DigitsPedSigma(adcId);
+        if (rms > 0.0) break;
+      case 2:
+        rms = m_pulsevar->m_noiseNkHi[noise_channel];
+        if (rms > 0.0) break;
+      case 1:
+        rms = m_pulsevar->m_noiseOrigHi[noise_channel];
+        if (rms > 0.0) break;
+      default:
+        rms = m_noiseHigh;
+    }
+  } else {
+    // neither low nor hi-gain, (e.g. adder data)
+    p_chi2 = -1.0;
+    return;
+  }
+
+  ATH_MSG_VERBOSE( "PulseFit:"
+                  << " ROS=" << ros
+                  << " Channel=" << channel
+                  << " gain=" << igain
+                  << " RMS=" << rms
+                  << " RMSChNoise=" << m_RMSChannelNoise );
+
+  // First sample to fit, number of samples to fit
+  int ifit1 = 0;
+  int nfit = std::min(m_frameLength, digit->NtimeSamples());
+
+  int ipeakMax = m_ipeak0;
+  int ipeakMin = m_ipeak0;
+
+  ATH_MSG_VERBOSE( "ipeak0=" << m_ipeak0
+                  << " ifit1=" << ifit1
+                  << " ifit2=" << ifit1 + nfit - 1
+                  << " nfit=" << nfit
+                  << " idolas=" << m_idolas
+                  << " idocis=" << m_idocis
+                  << " CISchan=" << m_cischan
+                  << " capdaq=" << m_capdaq );
+
+  std::vector<float> samples = digit->samples();
+  double maxsamp = 0.0;
+  double minsamp = SATURATED_ADC_VALUE;
+
+  double xvec[MAX_SAMPLES], yvec0[MAX_SAMPLES];
+  double yvec[MAX_SAMPLES], eyvec[MAX_SAMPLES];
+
+  bool no_signal = true;
+  p_pedestal = samples[0];
+  const double delta = 1.e-6;
+ 
+  for (int isamp = 0; isamp < nfit; ++isamp) {
+    int j = isamp + ifit1;
+    xvec[isamp] = j;
+    yvec0[isamp] = samples[j];
+    if (no_signal) no_signal = (fabs(samples[j] - p_pedestal) < delta);
+
+    // Check for saturated or zero samples and de-weight accordingly
+    if ((yvec0[isamp] < SATURATED_ADC_VALUE) && (yvec0[isamp] > 0)) {
+      eyvec[isamp] = rms;
+    } else {
+      if (yvec0[isamp] >= SATURATED_ADC_VALUE) {
+        eyvec[isamp] = m_SaturatedSampleError * rms;
+        ATH_MSG_VERBOSE( "Saturated ADC value yvec0[" << isamp << "]=" << yvec0[isamp]
+                        << " (MAX=" << SATURATED_ADC_VALUE << " ) RMS=" << eyvec[isamp] );
+
+      } else { // must be yvec0[isamp]==0
+        eyvec[isamp] = m_ZeroSampleError * rms;
+        ATH_MSG_VERBOSE( "Zero ADC value yvec0[" << isamp << "]=" << yvec0[isamp]
+                        << " RMS=" << eyvec[isamp] );
+      }
+    }
+    
+    if (yvec0[isamp] > maxsamp) {
+      // initial time guess based on
+      // sample with maximum value
+      maxsamp = yvec0[isamp];
+      ipeakMax = j;
+    }
+    if (yvec0[isamp] < minsamp) {
+      minsamp = yvec0[isamp];
+      ipeakMin = j;
+    }
+    ATH_MSG_VERBOSE( "isamp=" << isamp
+                    << ", xvec=" << xvec[isamp]
+                    << ", yvec0=" << yvec0[isamp]
+                    << ", eyvec=" << eyvec[isamp] );
+  }
+
+  if (no_signal) {
+    ATH_MSG_VERBOSE( "No signal detected" );
+    return;
+  }
+
+  // Make an initial guess about pedestal
+  double pedg = (yvec0[0] + yvec0[nfit - 1]) / 2.0;
+
+  // Position of max sample compared to nominal peak position
+  int delta_peak = 0;
+  // Time offset in pulse functions
+  m_t0fit = 0.0;
+  // Flag for fixed time in fit
+  bool fixedTime = (m_maxIterate < 0);
+  
+  if (!fixedTime) {
+    if (maxsamp - pedg > m_NoiseThresholdRMS * rms) {
+      delta_peak = ipeakMax - m_ipeak0;  // Adjust initial phase guess,
+      m_t0fit = (delta_peak) * DTIME;       // positive amplitude
+    } else if (pedg - minsamp > m_NoiseThresholdRMS * rms) {
+      delta_peak = ipeakMin - m_ipeak0;       // Adjust initial phase guess,
+      m_t0fit = (delta_peak) * DTIME;     // negative amplitude
+    } else {
+      fixedTime = true; // no signal above noise
+      m_t0fit = 0.0;    // fit with fixed time
+    }
+  }
+  
+  ATH_MSG_VERBOSE ( " initial value of"
+                   << " t0fit=" << m_t0fit
+                   << " ipeakMax=" << ipeakMax
+                   << " ipeakMin=" << ipeakMin
+                   << " fixedTime=" << ((fixedTime) ? "true" : "false") );
+
+  std::vector<double> *tpulse = &m_dummy, *ypulse = &m_dummy, *tdpulse = &m_dummy, *dpulse = &m_dummy;
+  std::vector<double> *tleak = &m_dummy, *yleak = &m_dummy, *tdleak = &m_dummy, *dleak = &m_dummy;
+  
+  if (m_idocis && ((m_cischan == -1) || (channel == m_cischan))) { // CIS pulse
+    if (igain == 0) { // low gain
+      if (m_capdaq > 10) { // 100 pF capacitor
+        tpulse = &(m_pulsevar->m_tlcis);
+        ypulse = &(m_pulsevar->m_ylcis);
+        tdpulse = &(m_pulsevar->m_tdlcis);
+        dpulse = &(m_pulsevar->m_ydlcis);
+        tleak = &(m_pulsevar->m_tleaklo);
+        yleak = &(m_pulsevar->m_leaklo);
+        tdleak = &(m_pulsevar->m_tdleaklo);
+        dleak = &(m_pulsevar->m_dleaklo);
+      } else { // 5.2 pF capacitor
+        tpulse = &(m_pulsevar->m_tslcis);
+        ypulse = &(m_pulsevar->m_yslcis);
+        tdpulse = &(m_pulsevar->m_tdslcis);
+        dpulse = &(m_pulsevar->m_ydslcis);
+        tleak = &(m_pulsevar->m_tsleaklo);
+        yleak = &(m_pulsevar->m_sleaklo);
+        tdleak = &(m_pulsevar->m_tdsleaklo);
+        dleak = &(m_pulsevar->m_dsleaklo);
+      }
+    } else { // igain==1 => high-gain
+      if (m_capdaq > 10) { // 100 pF capacitor
+        tpulse = &(m_pulsevar->m_thcis);
+        ypulse = &(m_pulsevar->m_yhcis);
+        tdpulse = &(m_pulsevar->m_tdhcis);
+        dpulse = &(m_pulsevar->m_ydhcis);
+        tleak = &(m_pulsevar->m_tleakhi);
+        yleak = &(m_pulsevar->m_leakhi);
+        tdleak = &(m_pulsevar->m_tdleakhi);
+        dleak = &(m_pulsevar->m_dleakhi);
+      } else { // 5.2 pF capacitor
+        tpulse = &(m_pulsevar->m_tshcis);
+        ypulse = &(m_pulsevar->m_yshcis);
+        tdpulse = &(m_pulsevar->m_tdshcis);
+        dpulse = &(m_pulsevar->m_ydshcis);
+        tleak = &(m_pulsevar->m_tsleakhi);
+        yleak = &(m_pulsevar->m_sleakhi);
+        tdleak = &(m_pulsevar->m_tdsleakhi);
+        dleak = &(m_pulsevar->m_dsleakhi);
+      }
+    }
+  } else {
+    if (m_idolas) { // laser pulse
+      if (igain == 0) { // low gain
+        tpulse = &(m_pulsevar->m_tllas);
+        ypulse = &(m_pulsevar->m_yllas);
+        tdpulse = &(m_pulsevar->m_tdllas);
+        dpulse = &(m_pulsevar->m_ydllas);
+      } else { // igain==1 => high-gain
+        tpulse = &(m_pulsevar->m_thlas);
+        ypulse = &(m_pulsevar->m_yhlas);
+        tdpulse = &(m_pulsevar->m_tdhlas);
+        dpulse = &(m_pulsevar->m_ydhlas);
+      }
+    } else { // physics pulse
+      if (igain == 0) { // low gain
+        tpulse = &(m_pulsevar->m_tlphys);
+        ypulse = &(m_pulsevar->m_ylphys);
+        tdpulse = &(m_pulsevar->m_tdlphys);
+        dpulse = &(m_pulsevar->m_ydlphys);
+      } else { // igain==1 => high-gain
+        tpulse = &(m_pulsevar->m_thphys);
+        ypulse = &(m_pulsevar->m_yhphys);
+        tdpulse = &(m_pulsevar->m_tdhphys);
+        dpulse = &(m_pulsevar->m_ydhphys);
+      }
+    }
+  }
+  
+  // Variables used for iterative fitting
+  double gval, gpval, sy, syg, sygp, sg, sgp, sgg, sgpgp, sggp, serr, err2;
+  double dgg0, dgg, dggp, dgpgp, dyg, dygp, dg, dc, xd;
+  double sllp, sylp, slplp, dleakage, leakage;
+  double fixtau = 0.0, fixped = 0.0, fixampl = 0.0, fixchi2 = MAX_CHI2;
+  double leaktau = 0.0, leakped = 0.0, leakampl = 0.0, leakchi2 = MAX_CHI2;
+  double cistau = 0.0, cisped = 0.0, cisampl = 0.0, cisatau = 0.0, cischi2 = MAX_CHI2;
+  double tau, ped, ampl, atau = 0.0, chi2 = MAX_CHI2, oldchi2 = MAX_CHI2 / 2;
+
+  // number of iterations
+  int niter = 0;
+  do {
+    ++niter;
+    ATH_MSG_VERBOSE ( "niter=" << niter << " maxIterate=" << m_maxIterate );
+
+    if (chi2 < oldchi2) oldchi2 = chi2; // best chi2 up to now
+
+    // parameters for pulse shape functions
+    // 0. phase
+    m_fnpar[0] = 0.0;
+    // 1. pedestal 
+    m_fnpar[1] = 0.0;
+    // 2. amplitude
+    m_fnpar[2] = 1.0;
+
+    // CIS events linear fit
+    if (m_idocis && ((m_cischan == -1) || (channel == m_cischan))) {
+      ATH_MSG_VERBOSE ( "Fit time with leakage" );
+      // CIS Part (A): fit for time using leakage pulse
+      sllp = 0.0;
+      sylp = 0.0;
+      slplp = 0.0;
+      for (int isamp = 0; isamp < nfit; ++isamp) {
+        ATH_MSG_VERBOSE ( "Lo gain leakage xvec[" << isamp << "]=" << xvec[isamp] );
+
+        leakage = pulse(xvec[isamp], tleak, yleak);
+        dleakage = deriv(xvec[isamp], tdleak, dleak);
+
+        // Samples with pedestal subtracted
+        yvec[isamp] = yvec0[isamp] - pedg;
+
+	      ATH_MSG_VERBOSE( " yvec[" << isamp << "]=" << yvec[isamp]
+	                      << " yvec0[" << isamp << "]=" << yvec0[isamp]
+	                      << " pedg=" << pedg);
+
+        sllp += leakage * dleakage;
+        sylp += yvec[isamp] * dleakage;
+        slplp += dleakage * dleakage;
+      }
+      // Also allow for fixed-time fit to CIS events
+      if (fabs(slplp) > EPS_DG && !fixedTime) {
+        leaktau = (sllp - sylp) / slplp;
+        // Also have to check the range for leaktau
+        if (leaktau > m_max_tau)
+          leaktau = m_max_tau;
+        else if (leaktau < m_min_tau) leaktau = m_min_tau;
+      } else {
+        leaktau = 0.0;
+      }
+      
+      ATH_MSG_VERBOSE( " sllp=" << sllp
+                      << " sylp=" << sylp
+                      << " slplp=" << slplp
+                      << " leaktau=" << leaktau);
+
+      // CIS Part (B): using phase determined in part (A), 
+      // subtract leakage pedestal and fit for amplitude, pedestal
+      m_fnpar[0] = leaktau;
+      sy = 0.0;
+      sg = 0.0;
+      syg = 0.0;
+      sgg = 0.0;
+      serr = 0.0;
+      for (int isamp = 0; isamp < nfit; ++isamp) {
+        leakage = pulse(xvec[isamp], tleak, yleak);
+        gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+        yvec[isamp] = yvec0[isamp] - leakage;
+
+        ATH_MSG_VERBOSE ( " yvec[" << isamp << "]=" << yvec[isamp]
+                         << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                         << " leakage=" << leakage );
+
+        err2 = eyvec[isamp] * eyvec[isamp];
+        sy += yvec[isamp] / err2;
+        sg += gval / err2;
+        syg += yvec[isamp] * gval / err2;
+        sgg += gval * gval / err2;
+        serr += 1.0 / err2;
+      }
+
+      dgg0 = sg * sg - serr * sgg;
+      if (fabs(dgg0) > EPS_DG) {
+        leakampl = (sy * sg - serr * syg) / dgg0;
+        leakped = (syg * sg - sy * sgg) / dgg0;
+      } else {
+        leakampl = 0.0;
+        leakped = sy / serr;
+      }      
+
+      // Determine Chi2 for corresponding function  for CIS leakage + pulse
+      ATH_MSG_VERBOSE ( " Determine Chi2 for CIS leakage + pulse" );
+      
+      leakchi2 = 0.0;
+      m_fnpar[0] = leaktau;
+      m_fnpar[1] = leakped;
+      m_fnpar[2] = leakampl;
+
+      for (int isamp = 0; isamp < nfit; ++isamp) {
+        gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+        leakage = pulse(xvec[isamp], tleak, yleak);
+        xd = yvec0[isamp] - (gval + leakage);
+        leakchi2 = leakchi2 + (xd * xd) / (eyvec[isamp] * eyvec[isamp]);
+
+        ATH_MSG_VERBOSE ( " isamp=" << isamp
+                        << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                        << " gval=" << gval
+                        << ", leakage=" << leakage
+                        << ", xd=" << xd );
+      }
+
+      leakchi2 = leakchi2/(nfit-3.0);
+
+      ATH_MSG_VERBOSE ( " leaktau=" << leaktau
+                      << " leakped=" << leakped
+                      << " leakampl=" << leakampl
+                      << " leakchi2=" << leakchi2 );
+      
+      // CIS Part C: Least-squares fit with 3 parameters for pulse+leakage
+      if (!fixedTime) {
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 0.0;
+
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          leakage = pulse(xvec[isamp], tleak, yleak);
+
+          // Subtract leakage from samples
+          yvec[isamp] = yvec0[isamp] - leakage;
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                          << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                          << " leakage=" << leakage
+                          << " yvec[" << isamp << "]=" << yvec[isamp] );
+        }
+
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 1.0;
+        sy = 0.0;
+        sg = 0.0;
+        sgp = 0.0;
+        syg = 0.0;
+        sygp = 0.0;
+        sgg = 0.0;
+        sggp = 0.0;
+        sgpgp = 0.0;
+        serr = 0.0;
+
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          gpval = deriv(xvec[isamp], tdpulse, dpulse);
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec[isamp] / err2;
+          sg += gval / err2;
+          sgp += gpval / err2;
+          syg += yvec[isamp] * gval / err2;
+          sygp += yvec[isamp] * gpval / err2;
+          sgg += gval * gval / err2;
+          sggp += gval * gpval / err2;
+          sgpgp += gpval * gpval / err2;
+          serr += 1.0 / err2;
+        }
+
+        dgg = sgg - sg * sg / serr;
+        dggp = sggp - sg * sgp / serr;
+        dgpgp = sgpgp - sgp * sgp / serr;
+        dyg = syg - sy * sg / serr;
+        dygp = sygp - sy * sgp / serr;
+        dg = dgg * dgpgp - dggp * dggp;
+
+        if (fabs(dg) > EPS_DG) {
+          cisampl = (dyg * dgpgp - dygp * dggp) / dg;
+          cisatau = (dyg * dggp - dygp * dgg) / dg;
+          cisped = (sy
+              - (dyg * dgpgp * sg - dygp * dggp * sg + dyg * dggp * sgp - dygp * dgg * sgp) / dg)
+              / serr;
+
+          if (fabs(cisampl) > EPS_DG) {
+            cistau = cisatau / cisampl;
+            if (cistau > m_max_tau)
+              cistau = m_max_tau;
+            else if (cistau < m_min_tau) cistau = m_min_tau;
+          } else {
+            cistau = 0.0;
+          }
+        } else {
+          cisampl = 0.0;
+          cisatau = 0.0;
+          cistau = 0.0;
+          cisped = sy / serr;
+        }
+
+	      if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " sy=" << sy
+                            << " sg=" << sg
+                            << " sgp=" << sgp
+                            << " syg=" << syg
+                            << " sygp=" << sygp
+                            << " sgg="  << sgg
+                            << " sggp=" << sggp
+                            << " sgpgp=" << sgpgp << endmsg;
+
+          msg(MSG::VERBOSE) << " dgg=" << dgg
+                            << " dggp=" << dggp
+                            << " sgpgp=" << sgpgp
+                            << " dyg=" << dyg
+                            << " dygp=" << dygp
+                            << " dg=" << dg << endmsg;
+
+          msg(MSG::VERBOSE) << " cistau=" << cistau
+                            << " cisped=" << cisped
+                            << " cisampl=" << cisampl << endmsg;
+        }
+        
+        // Determine Chi2 for pulse shape + leakage fit CIS Part C
+        cischi2 = 0.0;
+        m_fnpar[0] = cistau;
+        m_fnpar[1] = cisped;
+        m_fnpar[2] = cisampl;
+
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          // Subtract leakage from samples
+          yvec[isamp] = yvec0[isamp] - leakage;
+          xd = yvec[isamp] - gval;
+          cischi2 = cischi2 + (xd * xd) / (eyvec[isamp] * eyvec[isamp]);
+
+          ATH_MSG_VERBOSE ( " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " yvec[" << isamp << "]=" << yvec[isamp]
+                           << " leakage=" << leakage
+                           << " gval=" << gval
+                           << " xd=" << xd );
+        }
+
+        cischi2 = cischi2 / (nfit - 3.0);
+
+        ATH_MSG_VERBOSE ( " cischi2=" << cischi2 );
+      }
+
+      // Determine which set of parameters to use from CIS fit methods based on minimum chi2
+      if ((cischi2 < leakchi2) && !fixedTime) {
+        tau = cistau;
+        ped = cisped;
+        ampl = cisampl;
+        chi2 = cischi2;
+      } else {
+        tau = leaktau;
+        ped = leakped;
+        ampl = leakampl;
+        chi2 = leakchi2;
+      }
+      // End of fit for CIS events
+    } else {
+      // Physics and laser events
+
+      if (niter == 1) {
+        /* For first iteration, also calculate 2-Parameter Fit for pedestal and amplitude
+        */
+        double t0fit_old = m_t0fit;
+        m_t0fit = 0.0;
+
+        sy = 0.0;
+        sg = 0.0;
+        sgg = 0.0;
+        syg = 0.0;
+        serr = 0.0;
+
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          if (!m_idolas) {
+            // Use initial values for speeding up the physics events
+            int jsamp = (int) xvec[isamp] - delta_peak;
+            if (jsamp < 0)
+              jsamp = 0;
+            else if (jsamp >= nfit) jsamp = nfit - 1;
+
+            if (igain == 0) {
+              gval = m_gphyslo[jsamp];
+            } else {
+              gval = m_gphyshi[jsamp];
+            }
+          } else {
+            gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          }
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec0[isamp] / err2;
+          sg += gval / err2;
+          syg += yvec0[isamp] * gval / err2;
+          sgg += gval * gval / err2;
+          serr += 1.0 / err2;
+        }
+
+        fixtau = 0.0;
+        dgg0 = sg * sg - serr * sgg;
+        if (fabs(dgg0) > EPS_DG) {
+          fixampl = (sy * sg - serr * syg) / dgg0;
+          fixped = (syg * sg - sy * sgg) / dgg0;
+          ATH_MSG_VERBOSE ( "  2-par fit: fixampl = " << fixampl
+                          << " fixped = " << fixped );
+        } else {
+          fixampl = 0.0;
+          fixped = sy / serr;
+          ATH_MSG_VERBOSE ( "  2-par fit: small dgg0 = " << dgg0
+                          << ", fixampl = " << fixampl
+                          << " fixped = " << fixped );
+        }
+
+        m_fnpar[0] = fixtau; /* 2-Par fit Calculate chi2 for physics events */
+        m_fnpar[1] = fixped;
+        m_fnpar[2] = fixampl;
+        fixchi2 = 0.0;
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          dc = yvec0[isamp] - scaledpulse(xvec[isamp], tpulse, ypulse);
+          fixchi2 = fixchi2 + (dc * dc) / (eyvec[isamp] * eyvec[isamp]);
+          ATH_MSG_VERBOSE ( "   isamp= " << isamp
+                          << " yvec0[" << isamp << "]= " << yvec0[isamp]
+                          << " eyvec[" << isamp << "]= " << eyvec[isamp]
+                          << " fixchi2= " << fixchi2 );
+        }
+        fixchi2 = fixchi2 / (nfit - 2.0);
+        ATH_MSG_VERBOSE ( "  fixchi2/(nfit-2.0)=" << fixchi2
+                        << " nfit=" << nfit );
+
+        m_t0fit = t0fit_old;
+
+        // restore initial parameters for pulse shape functions - to be used in 3-par fit
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 1.0;
+      }                        /* end of 2-par fit in first iteration */
+
+      if (fixedTime) {
+        m_t0fit = 0.0;
+        tau = fixtau;
+        ped = fixped;
+        ampl = fixampl;
+        chi2 = oldchi2 = -fabs(fixchi2);
+      } else {
+
+        sy = 0.0;
+        sg = 0.0;
+        sgp = 0.0;
+        syg = 0.0;
+        sygp = 0.0;
+        sgg = 0.0;
+        sggp = 0.0;
+        sgpgp = 0.0;
+        serr = 0.0;
+
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          if ((niter == 1) && (!m_idolas)) {
+            // Use initial function values stored in array for niter=1 physics
+            // XXX: double->int
+            int jsamp = (int) xvec[isamp] - delta_peak;
+            if (jsamp < 0)
+              jsamp = 0;
+            else if (jsamp >= nfit) jsamp = nfit - 1;
+
+            if (igain == 0) {       // igain ==0 => low-gain
+              gval = m_gphyslo[jsamp];
+              gpval = m_dgphyslo[jsamp];
+            } else { 	          // must be igain==1 => high-gain
+              gval = m_gphyshi[jsamp];
+              gpval = m_dgphyshi[jsamp];
+            }
+          } else {
+            // Use the respective function values
+            gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+            gpval = deriv(xvec[isamp], tdpulse, dpulse);
+          }
+
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec0[isamp] / err2;
+          sg += gval / err2;
+          sgp += gpval / err2;
+          syg += yvec0[isamp] * gval / err2;
+          sygp += yvec0[isamp] * gpval / err2;
+          sgg += gval * gval / err2;
+          sggp += gval * gpval / err2;
+          sgpgp += gpval * gpval / err2;
+          serr += 1.0 / err2;
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                          << " gval=" << gval
+                          << " sg=" << sg
+                          << " gpval=" << gpval
+                          << " sgp=" << sgp );
+        }
+      
+        dgg = sgg - sg * sg / serr;
+        dggp = sggp - sg * sgp / serr;
+        dgpgp = sgpgp - sgp * sgp / serr;
+        dyg = syg - sy * sg / serr;
+        dygp = sygp - sy * sgp / serr;
+        dg = dgg * dgpgp - dggp * dggp;
+
+	// Fit for time, pedestal, and amplitude
+        if (fabs(dg) > EPS_DG) {
+          // Amplitude           : ampl
+          ampl = (dyg * dgpgp - dygp * dggp) / dg;
+          // and Amplitude * time: atau
+          atau = (dyg * dggp - dygp * dgg) / dg;
+          // Pedestal
+          ped = (sy - ((dyg * dgpgp - dygp * dggp) * sg + (dyg * dggp - dygp * dgg) * sgp) / dg)
+              / serr;
+
+          if (fabs(ampl) > EPS_DG) {
+            // Time
+            tau = atau / ampl;
+            if (tau > m_max_tau)
+              tau = m_max_tau;
+            else if (tau < m_min_tau) tau = m_min_tau;
+          } else {
+            tau = 0.0;
+          }
+        } else {
+          ampl = 0.0;
+          atau = 0.0;
+          tau = 0.0;
+          ped = sy / serr;
+        }
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " ped=" << ped << endmsg;
+          msg(MSG::VERBOSE) << " sy=" << sy
+                            << " sg=" << sg
+                            << " sgp=" << sgp << endmsg;
+
+          msg(MSG::VERBOSE) << " syg=" << syg
+                            << " sygp=" << sygp
+                            << " sgg=" << sgg << endmsg;
+
+          msg(MSG::VERBOSE) << " sggp=" << sggp
+                            << " sgpgp=" << sgpgp << endmsg;
+
+          msg(MSG::VERBOSE) << " ampl = (dyg*dgpgp - dygp*dggp)= " << ampl << endmsg;
+
+          msg(MSG::VERBOSE) << " dyg=" << dyg
+                            << " dgpgp=" << dgpgp
+                            << " dyg*dgpgp=" << (dyg * dgpgp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dygp=" << dygp
+                            << " dggp=" << dggp
+                            << " dygp*dggp=" << (dygp * dggp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dyg=" << dyg
+                            << " dggp=" << dggp
+                            << " dyg*dggp=" << (dyg * dggp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dygp=" << dygp
+                            << " dgg=" << dgg
+                            << " dygp*dgg=" << (dygp * dgg) << endmsg;
+
+          msg(MSG::VERBOSE) << " dg=" << dg
+                            << " atau=" << atau
+                            << " tau=" << tau << endmsg;
+        }
+      
+        m_fnpar[0] = tau;
+        m_fnpar[1] = ped;
+        m_fnpar[2] = ampl;
+
+        chi2 = 0;
+        // Calculate chi2 for physics and laser events
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          dc = yvec0[isamp] - scaledpulse(xvec[isamp], tpulse, ypulse);
+          chi2 = chi2 + (dc * dc) / (eyvec[isamp] * eyvec[isamp]);
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                          << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                          << " eyvec[" << isamp << "]=" << eyvec[isamp]
+                          << " dc=" << dc
+                          << " chi2=" << chi2 );
+        }
+
+        chi2 = chi2 / (nfit - 3.0);
+        ATH_MSG_VERBOSE ( " chi2/(nfit-3.0)=" << chi2
+                        << " nfit=" << nfit );
+      } // end if fixedTime
+    } // end of physics and laser specific part
+
+    if (msgLvl(MSG::VERBOSE))
+      msg(MSG::VERBOSE) << " t0fit: " << m_t0fit << ((tau < 0.0) ? " - " : " + ") << fabs(tau);
+
+    // avoid infinite loop at the boundary
+    if (fabs(m_t0fit - m_max_time) < 0.001 && tau >= 0.0) { // trying to go outside max boundary second time
+      m_t0fit = fixtau + (m_min_time - fixtau) * niter / m_maxIterate; // jump to negative time
+      if (msgLvl(MSG::VERBOSE))
+        msg(MSG::VERBOSE) << " jumping to " << m_t0fit << endmsg;
+      chi2 = MAX_CHI2;
+    } else if (fabs(m_t0fit - m_min_time) < 0.001 && tau <= 0.0) { // trying to go outside min boundary second time
+      m_t0fit = fixtau + (m_max_time - fixtau) * niter / m_maxIterate; // jump to positive time
+      if (msgLvl(MSG::VERBOSE)) msg(MSG::VERBOSE) << " jumping to " << m_t0fit << endmsg;
+      chi2 = MAX_CHI2;
+    } else {
+
+      // Iteration with parameter for time 
+      m_t0fit += tau;
+      if (msgLvl(MSG::VERBOSE)) msg(MSG::VERBOSE) << " = " << m_t0fit << endmsg;
+
+      // Check if total time does not exceed the limits:
+      if (m_t0fit > m_max_time) {
+        m_t0fit = m_max_time;
+        chi2 = MAX_CHI2;
+      } else if (m_t0fit < m_min_time) {
+        m_t0fit = m_min_time;
+        chi2 = MAX_CHI2;
+      }
+    }
+    
+    // save values of the best iteration
+    if (chi2 < p_chi2) {
+      p_time = m_t0fit;
+      p_pedestal = ped;
+      p_amplitude = ampl;
+      p_chi2 = chi2;
+    }
+
+    if (!fixedTime && chi2 < MAX_CHI2 && fabs(tau) < EPS_DG) { // too small tau
+      if (m_t0fit > fixtau)
+        m_t0fit = fixtau + (m_min_time - fixtau) * niter / m_maxIterate; // jump to negative time
+      else
+        m_t0fit = fixtau + (m_max_time - fixtau) * niter / m_maxIterate; // jump to positive time
+
+      ATH_MSG_VERBOSE( " too small tau - jump to " << m_t0fit);
+    }
+    
+    ATH_MSG_VERBOSE ( " iter=" << niter
+                     << " t0fit=" << m_t0fit
+                     << " phase=" << tau
+                     << " ped=" << ped
+                     << " ampl=" << ampl
+                     << " chi2=" << chi2 );
+
+  } while (fabs(chi2 - oldchi2) > DELTA_CHI2 && (niter < m_maxIterate));
+  
+//  NGO never use the 2par fit result if non-pedestal event was detected!
+//  if ((fabs(fixchi2) <= fabs(p_chi2))
+//      && !(m_idocis && ((m_cischan == -1) || (channel == m_cischan)))) {
+//    /* results from 2-par fit */
+//    p_time = fixtau;
+//    p_pedestal = fixped;
+//    p_amplitude = fixampl;
+//    p_chi2 = -fabs(fixchi2);
+//  }
+
+// TD: fit converged, now one extra iteration, leaving out the samples that
+// are more then m_MaxTimeFromPeak ns from the peak. We want to avoid to 
+// extrapolate the pulse shape beyond the region where it is known.
+
+// Do it only in case when at least last sample is beyond m_MaxTimeFromPeak:
+  if ((nfit - 1 - m_ipeak0) * DTIME > p_time + m_MaxTimeFromPeak) {
+
+    ATH_MSG_VERBOSE ( "Result before last iteration:"
+                     << " Time=" << p_time
+                     << " Ped=" << p_pedestal
+                     << " Amplitude=" << p_amplitude
+                     << " Chi2=" << p_chi2 );
+
+    m_t0fit = p_time;
+    int nfit_real;
+
+    // parameters for pulse shape functions
+    // 0. phase
+    m_fnpar[0] = 0.0;
+    // 1. pedestal 
+    m_fnpar[1] = 0.0;
+    // 2. amplitude
+    m_fnpar[2] = 1.0;
+
+    // CIS events linear fit
+    if (m_idocis && ((m_cischan == -1) || (channel == m_cischan))) {
+      if (!fixedTime) {
+        ATH_MSG_VERBOSE ( "Fit time with leakage" );
+        // CIS Part (A): fit for time using leakage pulse
+        sllp = 0.0;
+        sylp = 0.0;
+        slplp = 0.0;
+        for (int isamp = 0; (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+
+          ATH_MSG_VERBOSE ( "Lo gain leakage xvec[" << isamp << "]=" << xvec[isamp] );
+
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          dleakage = deriv(xvec[isamp], tdleak, dleak);
+
+          // Samples with pedestal subtracted
+          yvec[isamp] = yvec0[isamp] - pedg;
+
+          ATH_MSG_VERBOSE ( " yvec[" << isamp << "]=" << yvec[isamp]
+                           << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " pedg=" << pedg );
+
+          sllp += leakage * dleakage;
+          sylp += yvec[isamp] * dleakage;
+          slplp += dleakage * dleakage;
+        }
+        // Also allow for fixed-time fit to CIS events
+        if (fabs(slplp) > EPS_DG && !fixedTime) {
+          leaktau = (sllp - sylp) / slplp;
+          // Also have to check the range for leaktau
+          if (leaktau > m_max_tau)
+            leaktau = m_max_tau;
+          else if (leaktau < m_min_tau) leaktau = m_min_tau;
+        } else {
+          leaktau = 0.0;
+        }
+      
+        ATH_MSG_VERBOSE ( " sllp=" << sllp
+                         << " sylp=" << sylp
+                         << " slplp=" << slplp
+                         << " leaktau=" << leaktau );
+
+        // CIS Part (B): using phase determined in part (A), 
+        // subtract leakage pedestal and fit for amplitude, pedestal
+        m_fnpar[0] = leaktau;
+        sy = 0.0;
+        sg = 0.0;
+        syg = 0.0;
+        sgg = 0.0;
+        serr = 0.0;
+
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          yvec[isamp] = yvec0[isamp] - leakage;
+
+          ATH_MSG_VERBOSE ( " yvec[" << isamp << "]=" << yvec[isamp]
+                            << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                            << " leakage=" << leakage );
+
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec[isamp] / err2;
+          sg += gval / err2;
+          syg += yvec[isamp] * gval / err2;
+          sgg += gval * gval / err2;
+          serr += 1.0 / err2;
+        }
+
+        dgg0 = sg * sg - serr * sgg;
+        if (fabs(dgg0) > EPS_DG) {
+          leakampl = (sy * sg - serr * syg) / dgg0;
+          leakped = (syg * sg - sy * sgg) / dgg0;
+        } else {
+          leakampl = 0.0;
+          leakped = sy / serr;
+        }      
+
+        // Determine Chi2 for corresponding function  for CIS leakage + pulse
+        ATH_MSG_VERBOSE ( " Determine Chi2 for CIS leakage + pulse" );
+      
+        leakchi2 = 0.0;
+        nfit_real = 0;
+        m_fnpar[0] = leaktau;
+        m_fnpar[1] = leakped;
+        m_fnpar[2] = leakampl;
+
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+
+          ++nfit_real;
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          xd = yvec0[isamp] - (gval + leakage);
+          leakchi2 = leakchi2 + (xd * xd) / (eyvec[isamp] * eyvec[isamp]);
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                           << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " gval=" << gval
+                           << ", leakage=" << leakage
+                           << ", xd=" << xd );
+        }
+
+        leakchi2 = leakchi2 / (nfit_real - 3.0);
+
+        ATH_MSG_VERBOSE ( " leaktau=" << leaktau
+                         << " leakped=" << leakped
+                         << " leakampl=" << leakampl
+                         << " leakchi2=" << leakchi2 );
+      
+        // CIS Part C: Least-squares fit with 3 parameters for pulse+leakage
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 0.0;
+
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+
+          leakage = pulse(xvec[isamp], tleak, yleak);
+
+          // Subtract leakage from samples
+          yvec[isamp] = yvec0[isamp] - leakage;
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                          << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                          << " leakage=" << leakage
+                          << " yvec[" << isamp << "]=" << yvec[isamp] );
+        }
+
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 1.0;
+        sy = 0.0;
+        sg = 0.0;
+        sgp = 0.0;
+        syg = 0.0;
+        sygp = 0.0;
+        sgg = 0.0;
+        sggp = 0.0;
+        sgpgp = 0.0;
+        serr = 0.0;
+
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          gpval = deriv(xvec[isamp], tdpulse, dpulse);
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec[isamp] / err2;
+          sg += gval / err2;
+          sgp += gpval / err2;
+          syg += yvec[isamp] * gval / err2;
+          sygp += yvec[isamp] * gpval / err2;
+          sgg += gval * gval / err2;
+          sggp += gval * gpval / err2;
+          sgpgp += gpval * gpval / err2;
+          serr += 1.0 / err2;
+        }
+
+        dgg = sgg - sg * sg / serr;
+        dggp = sggp - sg * sgp / serr;
+        dgpgp = sgpgp - sgp * sgp / serr;
+        dyg = syg - sy * sg / serr;
+        dygp = sygp - sy * sgp / serr;
+        dg = dgg * dgpgp - dggp * dggp;
+      
+        if (fabs(dg) > EPS_DG) {
+          cisampl = (dyg * dgpgp - dygp * dggp) / dg;
+          cisatau = (dyg * dggp - dygp * dgg) / dg;
+          cisped = (sy
+              - (dyg * dgpgp * sg - dygp * dggp * sg + dyg * dggp * sgp - dygp * dgg * sgp) / dg)
+              / serr;
+
+          if (fabs(cisampl) > EPS_DG) {
+            cistau = cisatau / cisampl;
+            if (cistau > m_max_tau)
+              cistau = m_max_tau;
+            else if (cistau < m_min_tau) cistau = m_min_tau;
+          } else {
+            cistau = 0.0;
+          }
+        } else {
+          cisampl = 0.0;
+          cisatau = 0.0;
+          cistau = 0.0;
+          cisped = sy / serr;
+        }
+      
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " sy=" << sy
+                            << " sg=" << sg
+                            << " sgp=" << sgp
+                            << " syg=" << syg
+                            << " sygp=" << sygp
+                            << " sgg=" << sgg
+                            << " sggp=" << sggp
+                            << " sgpgp=" << sgpgp << endmsg;
+
+          msg(MSG::VERBOSE) << " dgg=" << dgg
+                            << " dggp=" << dggp
+                            << " sgpgp=" << sgpgp
+                            << " dyg=" << dyg
+                            << " dygp=" << dygp
+                            << " dg=" << dg << endmsg;
+
+          msg(MSG::VERBOSE) << " cistau=" << cistau
+                            << " cisped=" << cisped
+                            << " cisampl=" << cisampl << endmsg;
+        }
+      
+        // Determine Chi2 for pulse shape + leakage fit CIS 
+        cischi2 = 0.0;
+        nfit_real = 0;
+        m_fnpar[0] = cistau;
+        m_fnpar[1] = cisped;
+        m_fnpar[2] = cisampl;
+
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+
+          ++nfit_real;
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          // Subtract leakage from samples
+          yvec[isamp] = yvec0[isamp] - leakage;
+          xd = yvec[isamp] - gval;
+          cischi2 = cischi2 + (xd * xd) / (eyvec[isamp] * eyvec[isamp]);
+
+          ATH_MSG_VERBOSE ( " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " yvec[" << isamp << "]=" << yvec[isamp]
+                           << " leakage=" << leakage
+                           << " gval=" << gval
+                           << " xd=" << xd );
+        }
+
+        cischi2=cischi2/(nfit_real-3.0);
+      
+        ATH_MSG_VERBOSE ( " cischi2=" << cischi2 );
+
+        // Determine which set of parameters to use from CIS fit methods based on minimum chi2
+        if ((cischi2 < leakchi2) && !fixedTime) {
+          tau = cistau;
+          ped = cisped;
+          ampl = cisampl;
+          chi2 = cischi2;
+        } else {
+          tau = leaktau;
+          ped = leakped;
+          ampl = leakampl;
+          chi2 = leakchi2;
+        }
+        // End of fit for CIS events
+      }
+    } else {    // Physics and laser events
+      if (!fixedTime) {
+
+        // restore initial parameters for pulse shape functions - to be used in 3-par fit
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 1.0;
+
+        sy = 0.0;
+        sg = 0.0;
+        sgp = 0.0;
+        syg = 0.0;
+        sygp = 0.0;
+        sgg = 0.0;
+        sggp = 0.0;
+        sgpgp = 0.0;
+        serr = 0.0;
+
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+          // Use the respective function values
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          gpval = deriv(xvec[isamp], tdpulse, dpulse);
+
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec0[isamp] / err2;
+          sg += gval / err2;
+          sgp += gpval / err2;
+          syg += yvec0[isamp] * gval / err2;
+          sygp += yvec0[isamp] * gpval / err2;
+          sgg += gval * gval / err2;
+          sggp += gval * gpval / err2;
+          sgpgp += gpval * gpval / err2;
+          serr += 1.0 / err2;
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                          << " gval=" << gval
+                          << " sg=" << sg
+                          << " gpval=" << gpval
+                          << " sgp=" << sgp );
+        }
+    
+        dgg = sgg - sg * sg / serr;
+        dggp = sggp - sg * sgp / serr;
+        dgpgp = sgpgp - sgp * sgp / serr;
+        dyg = syg - sy * sg / serr;
+        dygp = sygp - sy * sgp / serr;
+        dg = dgg * dgpgp - dggp * dggp;
+      
+        // Fit for time, pedestal, and amplitude
+        if (fabs(dg) > EPS_DG) {
+          // Amplitude           : ampl 
+          ampl = (dyg * dgpgp - dygp * dggp) / dg;
+          // and Amplitude * time: atau
+          atau = (dyg * dggp - dygp * dgg) / dg;
+          // Pedestal
+          ped = (sy - ((dyg * dgpgp - dygp * dggp) * sg + (dyg * dggp - dygp * dgg) * sgp) / dg)
+              / serr;
+
+          if (fabs(ampl) > EPS_DG) {
+            // Time
+            tau = atau / ampl;
+            if (tau > m_max_tau)
+              tau = m_max_tau;
+            else if (tau < m_min_tau) tau = m_min_tau;
+          } else {
+            tau = 0.0;
+          }
+        } else {
+          ampl = 0.0;
+          atau = 0.0;
+          tau = 0.0;
+          ped = sy / serr;
+        }
+      
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " ped=" << ped << endmsg;
+          msg(MSG::VERBOSE) << " sy=" << sy
+                            << " sg=" << sg
+                            << " sgp=" << sgp << endmsg;
+
+          msg(MSG::VERBOSE) << " syg=" << syg
+                            << " sygp=" << sygp
+                            << " sgg=" << sgg << endmsg;
+
+          msg(MSG::VERBOSE) << " sggp=" << sggp
+                            << " sgpgp=" << sgpgp << endmsg;
+
+          msg(MSG::VERBOSE) << " ampl = (dyg*dgpgp - dygp*dggp)= " << ampl << endmsg;
+          msg(MSG::VERBOSE) << " dyg=" << dyg
+                            << " dgpgp=" << dgpgp
+                            << " dyg*dgpgp=" << (dyg * dgpgp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dygp=" << dygp
+                            << " dggp=" << dggp
+                            << " dygp*dggp=" << (dygp * dggp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dyg=" << dyg
+                            << " dggp=" << dggp
+                            << " dyg*dggp=" << (dyg * dggp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dygp=" << dygp
+                            << " dgg=" << dgg
+                            << " dygp*dgg=" << (dygp * dgg) << endmsg;
+
+          msg(MSG::VERBOSE) << " dg=" << dg
+                            << " atau=" << atau
+                            << " tau=" << tau << endmsg;
+        }
+      
+        m_fnpar[0] = tau;
+        m_fnpar[1] = ped;
+        m_fnpar[2] = ampl;
+
+        chi2 = 0;
+        nfit_real = 0;
+        // Calculate chi2 for physics and laser events
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+
+          ++nfit_real;
+          dc = yvec0[isamp] - scaledpulse(xvec[isamp], tpulse, ypulse);
+          chi2 = chi2 + (dc * dc) / (eyvec[isamp] * eyvec[isamp]);
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                           << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " eyvec[" << isamp << "]=" << eyvec[isamp]
+                           << " dc=" << dc
+                           << " chi2=" << chi2 );
+        }
+
+        chi2 = chi2 / (nfit_real - 3.0);
+        ATH_MSG_VERBOSE ( " chi2/(nfit_real-3.0)=" << chi2
+                         << " nfit_real=" << nfit_real );
+      } // end if fixedTime
+    } // end of physics and laser specific part
+
+    if (msgLvl(MSG::VERBOSE))
+      msg(MSG::VERBOSE) << " t0fit: " << m_t0fit << ((tau < 0.0) ? " - " : " + ") << fabs(tau);
+
+    // Iteration with parameter for time 
+    m_t0fit += tau;
+    if ( msgLvl(MSG::VERBOSE) )
+      msg(MSG::VERBOSE) << " = " << m_t0fit << endmsg;
+  
+    // Check if total time does not exceed the limits:
+    if (m_t0fit > m_max_time) {
+      m_t0fit = m_max_time;
+      chi2 = MAX_CHI2;
+    } else if (m_t0fit < m_min_time) {
+      m_t0fit = m_min_time;
+      chi2 = MAX_CHI2;
+    }
+
+    if (chi2 < MAX_CHI2) {
+      p_time = m_t0fit;
+      p_pedestal = ped;
+      p_amplitude = ampl;
+      p_chi2 = chi2;
+    } // otherwise using the previous iteration
+    
+  } // end if to use extra iteration
+  
+  ATH_MSG_VERBOSE ( "Result: Time=" << p_time
+                  << " Ped=" << p_pedestal
+                  << " Amplitude=" << p_amplitude
+                  << " Chi2=" << p_chi2 );
+}
+
+
+/**
+ * pulse interpolation
+ */
+double TileRawChannelBuilderFitFilter::pulse(double x, const std::vector<double> * xvec
+    , const std::vector<double> * yvec, bool zeroOutside) const {
+
+  int size1 = xvec->size() - 1;
+  if (size1 < 1) return 0.0;
+
+  const double delta = 1.e-6;
+  
+  double xpmin = xvec->at(0);
+  double xpmax = xvec->at(size1);
+
+  double xp = (x - m_ipeak0) * DTIME - m_t0fit - m_fnpar[0];
+
+  double val = 0.0;
+  double tdiv = (xpmax - xpmin) / size1;
+
+  if (xp < xpmin + delta) {
+    if (zeroOutside && xp < xpmin - delta)
+      val = 0.0;
+    else
+      val = yvec->at(0);
+#ifdef EXTRAPOLATE_TO_ZERO
+    if (xp < xpmin - delta && val != 0.0) {
+      double newval = val + ((yvec->at(1) - val) / tdiv) * (xp - xpmin);
+      if (val * newval < 0.0) {
+        val = 0.0;
+      } else if (fabs(newval) < fabs(val)) {
+        val = newval;
+      }
+    }
+#endif
+  } else if (xp > xpmax - delta) {
+    if (zeroOutside && xp > xpmax + delta)
+      val = 0.0;
+    else
+      val = yvec->at(size1);
+#ifdef EXTRAPOLATE_TO_ZERO
+    if (xp > xpmax + delta && val != 0.0) {
+      double newval = val + ((yvec->at(size1 - 1) - val) / tdiv) * (xp - xpmax);
+      if (val * newval < 0.0) {
+        val = 0.0;
+      } else if (fabs(newval) < fabs(val)) {
+        val = newval;
+      }
+    }
+#endif
+  } else {
+    int j = (int) ((xp - xpmin) / tdiv);
+    val = yvec->at(j) + ((yvec->at(j + 1) - yvec->at(j)) / tdiv) * (xp - xvec->at(j));
+  }
+  
+  return val;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderFitFilterCool.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderFitFilterCool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..7528ebe156fa65fbb479efdcd5d20ba51b73c374
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderFitFilterCool.cxx
@@ -0,0 +1,1863 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// small hack to enable datapool usage
+#define private public
+#define protected public
+#include "TileEvent/TileRawChannel.h"
+#undef private
+#undef protected
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+#include "GaudiKernel/IIncidentSvc.h"
+
+// Atlas includes
+#include "DataModel/DataPool.h"
+#include "AthenaKernel/errorcheck.h"
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilderFitFilterCool.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileDigitsContainer.h"
+#include "TileEvent/TileDigits.h"
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileIdentifier/TileTrigType.h"
+#include "TileConditions/TileInfo.h"
+#include "TileConditions/TileCablingService.h"
+
+// lang include
+#include <algorithm>
+#include <cmath>
+
+
+static const InterfaceID IID_ITileRawChannelBuilderFitFilterCool
+       ("TileRawChannelBuilderFitFilterCool", 1, 0);
+
+const InterfaceID& TileRawChannelBuilderFitFilterCool::interfaceID( ) { 
+  return IID_ITileRawChannelBuilderFitFilterCool;
+  //return TileRawChannelBuilderCool::interfaceID();
+}
+
+/**
+ * Constructor
+ */
+TileRawChannelBuilderFitFilterCool::TileRawChannelBuilderFitFilterCool(const std::string& type,
+    const std::string& name, const IInterface *parent)
+  : TileRawChannelBuilder(type, name, parent)
+  , m_t0fit(0.0)
+  , m_ipeak0(0)
+  , m_min_time(0.0)
+  , m_max_time(0.0)
+  , m_min_tau(0)
+  , m_max_tau(0.0)
+  , m_pulsevar(0)
+  , m_tileToolPulseShape("TileCondToolPulseShape")
+  , m_tileToolLeak100Shape("TileCondToolLeak100Shape")
+  , m_tileToolLeak5p2Shape("TileCondToolLeak5p2Shape")
+  , m_tileToolPulse5p2Shape("TileCondToolPulse5p2Shape")
+  , m_tileToolNoiseSample("TileCondToolNoiseSample")
+  , m_shapes(NULL)
+{  
+  //declare interfaces
+  declareInterface< TileRawChannelBuilder >( this );
+  declareInterface< TileRawChannelBuilderFitFilterCool >(this);
+
+  m_TileRawChannelContainerID = "TileRawChannelFitCool";
+
+  //declare properties
+  declareProperty("FrameLength",m_frameLength = 9);
+  declareProperty("MaxIterate",m_maxIterate = 9);
+  declareProperty("NoiseLowGain",m_noiseLow = 0.6);
+  declareProperty("NoiseHighGain",m_noiseHigh = 1.5);
+  declareProperty("RMSChannelNoise",m_RMSChannelNoise = 3);
+  declareProperty("ExtraSamplesLeft",m_extraSamplesLeft=0);   // increase window on left side
+  declareProperty("ExtraSamplesRight",m_extraSamplesRight=0); // increase window on right side
+  declareProperty("SaturatedSampleError",m_SaturatedSampleError = 6.0);
+  declareProperty("ZeroSampleError",m_ZeroSampleError = 100.0);
+  declareProperty("NoiseThresholdRMS",m_NoiseThresholdRMS = 3.0);
+  declareProperty("MaxTimeFromPeak",m_MaxTimeFromPeak = 250.0);
+  declareProperty("TileCondToolPulseShape" , m_tileToolPulseShape);
+  declareProperty("TileCondToolLeak100Shape" , m_tileToolLeak100Shape);
+  declareProperty("TileCondToolLeak5p2Shape" , m_tileToolLeak5p2Shape);
+  declareProperty("TileCondToolPulse5p2Shape" , m_tileToolPulse5p2Shape);
+  declareProperty("TileCondToolNoiseSample", m_tileToolNoiseSample);
+}
+
+/**
+ * Destructor
+ */
+TileRawChannelBuilderFitFilterCool::~TileRawChannelBuilderFitFilterCool() {
+  if (m_shapes) delete m_shapes;
+}
+
+/**
+ * Initializer
+ */
+StatusCode TileRawChannelBuilderFitFilterCool::initialize() {
+
+  ATH_MSG_INFO( "TileRawChannelBuilderFitFilterCool::initialize()" );
+
+  m_rChType = TileFragHash::FitFilterCool;
+
+  // init in superclass
+  CHECK( TileRawChannelBuilder::initialize() );
+  
+  // Get pulse shapes from TileInfo
+  m_pulsevar = m_tileInfo->getPulseShapes();
+
+  // Determine peak sample position 
+  // peak sample position defines t=0 
+  m_ipeak0 = (int) (m_frameLength) / 2 + (m_frameLength % 2) - 1;
+
+  // Min and max time are now calculated based on m_framelength - i.e. on the
+  // number of 25 ns samples read out. Don't forget t=0 corresponds to 
+  // m_ipeak0-th sample
+  m_min_time = DTIME * (0 - m_ipeak0 - m_extraSamplesLeft);
+  m_max_time = DTIME * (m_frameLength - 1 - m_ipeak0 + m_extraSamplesRight);
+  // maximal jump during one iteration
+  m_max_tau = (m_max_time - m_min_time);
+  m_min_tau = -m_max_tau;
+
+  ATH_MSG_DEBUG( " ipeak0=" << m_ipeak0
+                << " min_time=" << m_min_time
+                << " max_time=" << m_max_time
+                << " min_tau=" << m_min_tau
+                << " max_tau=" << m_max_tau
+                << " physize=" << m_pulsevar->m_ylphys.size()
+                << " cissize=" << m_pulsevar->m_ylcis.size()
+                << " xpmin=" << m_pulsevar->m_tlphys[0]
+                << " xpmax=" << m_pulsevar->m_tlphys[MAX_LO_PULSE_PHYS - 1]
+                << " ypmin=" << m_pulsevar->m_ylphys[0]
+                << " ypmax=" << m_pulsevar->m_ylphys[MAX_LO_PULSE_PHYS - 1] );
+  
+  //=== get TileCondToolPulseShape
+  CHECK( m_tileToolPulseShape.retrieve() );
+
+  if (m_idocis) {
+    CHECK( m_tileToolLeak100Shape.retrieve() );
+    CHECK(  m_tileToolLeak5p2Shape.retrieve() );
+    CHECK( m_tileToolPulse5p2Shape.retrieve() );
+  }
+
+  //=== TileCondToolNoiseSample
+  CHECK( m_tileToolNoiseSample.retrieve() );
+
+  // Incident Service:
+  ServiceHandle<IIncidentSvc> incSvc("IncidentSvc", this->name());
+  CHECK(incSvc.retrieve());
+  //start listening to "BeginRun"
+  incSvc->addListener(this, "BeginRun");
+
+  if (msgLvl(MSG::DEBUG)) {
+    if (m_RMSChannelNoise) {
+      msg(MSG::DEBUG) << " noise for all channels from Conditions DB ";
+      if (TileCablingService::getInstance()->getTestBeam())
+        msg(MSG::DEBUG) << " rmsLow(LBA01/0) = " << m_tileInfo->DigitsPedSigma(TileID::LOWGAIN, 0, 20)
+                        << " rmsHi(LBA01/0) = " << m_tileInfo->DigitsPedSigma(TileID::HIGHGAIN, 0, 20)
+                        << endmsg;
+      else
+        msg(MSG::DEBUG) << endmsg;
+    } else {
+      msg(MSG::DEBUG) << " common noise for all channels (OBSOLETE): " << endmsg;
+      msg(MSG::DEBUG) << " rmsLow = " << m_noiseLow
+                      << " rmsHi = " << m_noiseHigh << endmsg;
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+void TileRawChannelBuilderFitFilterCool::handle(const Incident& inc) {
+
+  // -------- get pulse shapes from COOL
+
+  ATH_MSG_INFO( "TileRawChannelBuilderFitFilterCool handle" << inc.type() );
+
+  if (inc.type() == "BeginRun") {
+    // Do run initializations here...
+
+    ATH_MSG_DEBUG( "m_trigType " << m_trigType
+                  << " m_runType " << m_runType
+                  << " m_idophys " << m_idophys
+                  << " m_idolas " << m_idolas
+                  << " m_idoped " << m_idoped
+                  << " m_idocis " << m_idocis );
+
+    if (m_shapes) delete m_shapes;
+
+    m_shapes = new TilePulseShapesStruct;
+    float y, dy;
+    unsigned int drawerIdx = TileCalibUtils::getDrawerIdx(0, 0);
+
+    float phase = 0;
+    if (m_idolas) {
+      // --- low gain
+      m_shapes->m_yllas.reserve(MAX_LO_PULSE_LAS);
+      m_shapes->m_tllas.reserve(MAX_LO_PULSE_LAS);
+      m_shapes->m_ydllas.reserve(MAX_LO_PULSE_LAS);
+      m_shapes->m_tdllas.reserve(MAX_LO_PULSE_LAS);
+      phase = LAS_START_T_LO;
+      for (int i = 0; i < MAX_LO_PULSE_LAS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolPulseShape->getPulseShapeYDY(drawerIdx, 0, 0, phase, y, dy);
+        m_shapes->m_tllas.push_back(phase);
+        m_shapes->m_yllas.push_back(y);
+        m_shapes->m_tdllas.push_back(phase);
+        m_shapes->m_ydllas.push_back(dy);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << "new  tllas= " << m_shapes->m_tllas[i]
+                          << " yllas= " << m_shapes->m_yllas[i]
+                          << " ydllas " << m_shapes->m_ydllas[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  tllas= " << m_pulsevar->m_tllas[i]
+                          << " yllas= " << m_pulsevar->m_yllas[i]
+                          << " ydllas " << m_pulsevar->m_ydllas[i] << endmsg;
+        }
+        phase += LAS_DT_LO;
+      }
+      // --- high gain
+      m_shapes->m_yhlas.reserve(MAX_HI_PULSE_LAS);
+      m_shapes->m_thlas.reserve(MAX_HI_PULSE_LAS);
+      m_shapes->m_ydhlas.reserve(MAX_HI_PULSE_LAS);
+      m_shapes->m_tdhlas.reserve(MAX_HI_PULSE_LAS);
+      phase = LAS_START_T_HI;
+      for (int i = 0; i < MAX_HI_PULSE_LAS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolPulseShape->getPulseShapeYDY(drawerIdx, 0, 1, phase, y, dy);
+        m_shapes->m_thlas.push_back(phase);
+        m_shapes->m_yhlas.push_back(y);
+        m_shapes->m_tdhlas.push_back(phase);
+        m_shapes->m_ydhlas.push_back(dy);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << "n ew  thlas= " << m_shapes->m_thlas[i]
+                          << " yhlas= " << m_shapes->m_yhlas[i]
+                          << " ydhlas " << m_shapes->m_ydhlas[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  thlas= " << m_pulsevar->m_thlas[i]
+                          << " yhlas= " << m_pulsevar->m_yhlas[i]
+                          << " ydhlas " << m_pulsevar->m_ydhlas[i] << endmsg;
+        }
+        phase += LAS_DT_HI;
+      }
+    } else if (m_idocis || m_idophys || m_idoped) {
+      // --- low gain
+      m_shapes->m_ylphys.reserve(MAX_LO_PULSE_PHYS);
+      m_shapes->m_tlphys.reserve(MAX_LO_PULSE_PHYS);
+      m_shapes->m_ydlphys.reserve(MAX_LO_PULSE_PHYS);
+      m_shapes->m_tdlphys.reserve(MAX_LO_PULSE_PHYS);
+      phase = PHYS_START_T_LO;
+      for (int i = 0; i < MAX_LO_PULSE_PHYS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolPulseShape->getPulseShapeYDY(drawerIdx, 0, 0, phase, y, dy);
+        m_shapes->m_tlphys.push_back(phase);
+        m_shapes->m_ylphys.push_back(y);
+        m_shapes->m_tdlphys.push_back(phase);
+        m_shapes->m_ydlphys.push_back(dy);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new  tlphys= " << m_shapes->m_tlphys[i]
+                          << " ylphys= " << m_shapes->m_ylphys[i]
+                          << " ydlphys " << m_shapes->m_ydlphys[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  tlphys= " << m_pulsevar->m_tlphys[i]
+                          << " ylphys= " << m_pulsevar->m_ylphys[i]
+                          << " ydlphys " << m_pulsevar->m_ydlphys[i] << endmsg;
+        }
+        phase += PHYS_DT_LO;
+      }
+      // --- high gain
+      m_shapes->m_yhphys.reserve(MAX_HI_PULSE_PHYS);
+      m_shapes->m_thphys.reserve(MAX_HI_PULSE_PHYS);
+      m_shapes->m_ydhphys.reserve(MAX_HI_PULSE_PHYS);
+      m_shapes->m_tdhphys.reserve(MAX_HI_PULSE_PHYS);
+      phase = PHYS_START_T_HI;
+      for (int i = 0; i < MAX_HI_PULSE_PHYS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolPulseShape->getPulseShapeYDY(drawerIdx, 0, 1, phase, y, dy);
+        m_shapes->m_thphys.push_back(phase);
+        m_shapes->m_yhphys.push_back(y);
+        m_shapes->m_tdhphys.push_back(phase);
+        m_shapes->m_ydhphys.push_back(dy);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new  thphys= " << m_shapes->m_thphys[i]
+                          << " yhphys= " << m_shapes->m_yhphys[i]
+                          << " ydhphys " << m_shapes->m_ydhphys[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  thphys= " << m_pulsevar->m_thphys[i]
+                          << " yhphys= " << m_pulsevar->m_yhphys[i]
+                          << " ydhphys " << m_pulsevar->m_ydhphys[i] << endmsg;
+        }
+        phase += PHYS_DT_HI;
+      }
+
+      //================================================
+      // Speedup for physics processing (max_iter=1):
+      //  read initial pulse shapes into arrays
+      m_fnpar[0] = 0.0;
+      m_fnpar[1] = 0.0;
+      m_fnpar[2] = 1.0;
+      m_t0fit = 0.0;
+      
+      m_gphyslo.reserve(m_frameLength);
+      m_dgphyslo.reserve(m_frameLength);
+      m_gphyshi.reserve(m_frameLength);
+      m_dgphyshi.reserve(m_frameLength);
+      
+      double rsamp;
+      for (int isamp = 0; isamp < m_frameLength; ++isamp) {
+        rsamp = (isamp) * 1.0;
+        m_gphyslo.push_back(scaledpulse(rsamp, &(m_pulsevar->m_tlphys), &(m_pulsevar->m_ylphys)));
+        m_dgphyslo.push_back(pulse(rsamp, &(m_pulsevar->m_tdlphys), &(m_pulsevar->m_ydlphys)));
+        m_gphyshi.push_back(scaledpulse(rsamp, &(m_pulsevar->m_thphys), &(m_pulsevar->m_yhphys)));
+        m_dgphyshi.push_back(pulse(rsamp, &(m_pulsevar->m_tdhphys), &(m_pulsevar->m_ydhphys)));
+      }
+    } else {
+      ATH_MSG_ERROR( " Unknown run type "
+                    << "idophys=" << m_idophys
+                    << " idolas=" << m_idolas
+                    << " idocis=" << m_idocis
+                    << " idoped= " << m_idoped );
+    }
+
+    // -------------- CIS shapes
+
+    if (m_idocis) {
+      // --- low gain 100 pF Pulse
+      m_shapes->m_ylcis.reserve(MAX_LO_PULSE_CIS);
+      m_shapes->m_tlcis.reserve(MAX_LO_PULSE_CIS);
+      m_shapes->m_ydlcis.reserve(MAX_LO_PULSE_CIS);
+      m_shapes->m_tdlcis.reserve(MAX_LO_PULSE_CIS);
+      phase = CIS_START_T_LO;
+      for (int i = 0; i < MAX_LO_PULSE_CIS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolPulseShape->getPulseShapeYDY(drawerIdx, 0, 0, phase, y, dy);
+        m_shapes->m_tlcis.push_back(phase);
+        m_shapes->m_ylcis.push_back(y);
+        m_shapes->m_tdlcis.push_back(phase);
+        m_shapes->m_ydlcis.push_back(dy);
+        //aa	m_shapes->m_ydlcis.push_back(m_pulsevar->m_ydlcis[i]);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new  tlcis= " << m_shapes->m_tlcis[i]
+                          << " ylcis= " << m_shapes->m_ylcis[i]
+                          << " ydlcis " << m_shapes->m_ydlcis[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  tlcis= " << m_pulsevar->m_tlcis[i]
+                          << " ylcis= " << m_pulsevar->m_ylcis[i]
+                          << " ydlcis " << m_pulsevar->m_ydlcis[i] << endmsg;
+        }
+        phase += CIS_DT_LO;
+      }
+      // --- high gain 100 pF Pulse
+      m_shapes->m_yhcis.reserve(MAX_HI_PULSE_CIS);
+      m_shapes->m_thcis.reserve(MAX_HI_PULSE_CIS);
+      m_shapes->m_ydhcis.reserve(MAX_HI_PULSE_CIS);
+      m_shapes->m_tdhcis.reserve(MAX_HI_PULSE_CIS);
+      phase = CIS_START_T_HI;
+      for (int i = 0; i < MAX_HI_PULSE_CIS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolPulseShape->getPulseShapeYDY(drawerIdx, 0, 1, phase, y, dy);
+        m_shapes->m_thcis.push_back(phase);
+        m_shapes->m_yhcis.push_back(y);
+        m_shapes->m_tdhcis.push_back(phase);
+        m_shapes->m_ydhcis.push_back(dy);
+        //aa	m_shapes->m_ydhcis.push_back(m_pulsevar->m_ydhcis[i]);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new  thcis= " << m_shapes->m_thcis[i]
+                          << " yhcis= " << m_shapes->m_yhcis[i]
+                          << " ydhcis " << m_shapes->m_ydhcis[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  thcis= " << m_pulsevar->m_thcis[i]
+                          << " yhcis= " << m_pulsevar->m_yhcis[i]
+                          << " ydhcis " << m_pulsevar->m_ydhcis[i] << endmsg;
+        }
+        phase += CIS_DT_HI;
+      }
+
+      // --- low gain 5.2 pF Pulse
+      m_shapes->m_yslcis.reserve(MAX_LO_PULSE_CIS_SMALL);
+      m_shapes->m_tslcis.reserve(MAX_LO_PULSE_CIS_SMALL);
+      m_shapes->m_ydslcis.reserve(MAX_LO_PULSE_CIS_SMALL);
+      m_shapes->m_tdslcis.reserve(MAX_LO_PULSE_CIS_SMALL);
+      phase = SCIS_START_T_LO;
+      for (int i = 0; i < MAX_LO_PULSE_CIS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolPulse5p2Shape->getPulseShapeYDY(drawerIdx, 0, 0, phase, y, dy);
+        m_shapes->m_tslcis.push_back(phase);
+        m_shapes->m_yslcis.push_back(y);
+        m_shapes->m_tdslcis.push_back(phase);
+        m_shapes->m_ydslcis.push_back(dy);
+        //aa	m_shapes->m_ydslcis.push_back(m_pulsevar->m_ydslcis[i]);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new  tslcis= " << m_shapes->m_tslcis[i]
+                          << " yslcis= " << m_shapes->m_yslcis[i]
+                          << " ydslcis " << m_shapes->m_ydslcis[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  tslcis= " << m_pulsevar->m_tslcis[i]
+                          << " yslcis= " << m_pulsevar->m_yslcis[i]
+                          << " ydslcis " << m_pulsevar->m_ydslcis[i] << endmsg;
+        }
+        phase += CIS_DT_LO;
+      }
+      // --- high gain 5.2 pF Pulse
+      m_shapes->m_yshcis.reserve(MAX_HI_PULSE_CIS_SMALL);
+      m_shapes->m_tshcis.reserve(MAX_HI_PULSE_CIS_SMALL);
+      m_shapes->m_ydshcis.reserve(MAX_HI_PULSE_CIS_SMALL);
+      m_shapes->m_tdshcis.reserve(MAX_HI_PULSE_CIS_SMALL);
+      phase = SCIS_START_T_HI;
+      for (int i = 0; i < MAX_HI_PULSE_CIS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolPulse5p2Shape->getPulseShapeYDY(drawerIdx, 0, 1, phase, y, dy);
+        m_shapes->m_tshcis.push_back(phase);
+        m_shapes->m_yshcis.push_back(y);
+        m_shapes->m_tdshcis.push_back(phase);
+        m_shapes->m_ydshcis.push_back(dy);
+        //aa	m_shapes->m_ydshcis.push_back(m_pulsevar->m_ydshcis[i]);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new  tshcis= " << m_shapes->m_tshcis[i]
+                          << " yshcis= " << m_shapes->m_yshcis[i]
+                          << " ydshcis " << m_shapes->m_ydshcis[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  tshcis= " << m_pulsevar->m_tshcis[i]
+                          << " yshcis= " << m_pulsevar->m_yshcis[i]
+                          << " ydshcis " << m_pulsevar->m_ydshcis[i] << endmsg;
+        }
+        phase += CIS_DT_HI;
+      }
+
+      // --- low gain 100 pF Leak
+      m_shapes->m_leaklo.reserve(MAX_LO_PULSE_CIS);
+      m_shapes->m_tleaklo.reserve(MAX_LO_PULSE_CIS);
+      m_shapes->m_dleaklo.reserve(MAX_LO_PULSE_CIS);
+      m_shapes->m_tdleaklo.reserve(MAX_LO_PULSE_CIS);
+      phase = LEAK_START_T_LO;
+      for (int i = 0; i < MAX_LO_PULSE_CIS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolLeak100Shape->getPulseShapeYDY(drawerIdx, 0, 0, phase, y, dy);
+        m_shapes->m_tleaklo.push_back(phase);
+        m_shapes->m_leaklo.push_back(y);
+        m_shapes->m_tdleaklo.push_back(phase);
+        //aa m_shapes->m_dleaklo.push_back(m_pulsevar->m_dleaklo[i]);
+        m_shapes->m_dleaklo.push_back(dy);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new tleaklo = " << m_shapes->m_tleaklo[i]
+                          << " leaklo = " << m_shapes->m_leaklo[i]
+                          << " dleaklo  " << m_shapes->m_dleaklo[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  tleaklo= " << m_pulsevar->m_tleaklo[i]
+                          << " leaklo= " << m_pulsevar->m_leaklo[i]
+                          << " dleaklo " << m_pulsevar->m_dleaklo[i] << endmsg;
+        }
+        phase += CIS_DT_LO;
+      }
+      // --- high gain 100 pF Leak
+      m_shapes->m_leakhi.reserve(MAX_HI_PULSE_CIS);
+      m_shapes->m_tleakhi.reserve(MAX_HI_PULSE_CIS);
+      m_shapes->m_dleakhi.reserve(MAX_HI_PULSE_CIS);
+      m_shapes->m_tdleakhi.reserve(MAX_HI_PULSE_CIS);
+      phase = LEAK_START_T_HI;
+      for (int i = 0; i < MAX_HI_PULSE_CIS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolLeak100Shape->getPulseShapeYDY(drawerIdx, 0, 1, phase, y, dy);
+        m_shapes->m_tleakhi.push_back(phase);
+        m_shapes->m_leakhi.push_back(y);
+        m_shapes->m_tdleakhi.push_back(phase);
+        m_shapes->m_dleakhi.push_back(dy);
+        //aa	m_shapes->m_dleakhi.push_back(m_pulsevar->m_dleakhi[i]);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new tleakhi = " << m_shapes->m_tleakhi[i]
+                          << " leakhi= " << m_shapes->m_leakhi[i]
+                          << " dleakhi " << m_shapes->m_dleakhi[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old tleakhi = " << m_pulsevar->m_tleakhi[i]
+                          << " leakhi= " << m_pulsevar->m_leakhi[i]
+                          << " dleakhi " << m_pulsevar->m_dleakhi[i] << endmsg;
+        }
+        phase += CIS_DT_HI;
+      }
+
+      // --- low gain 5.2 pF Leak
+      m_shapes->m_sleaklo.reserve(MAX_LO_PULSE_CIS_SMALL);
+      m_shapes->m_tsleaklo.reserve(MAX_LO_PULSE_CIS_SMALL);
+      m_shapes->m_dsleaklo.reserve(MAX_LO_PULSE_CIS_SMALL);
+      m_shapes->m_tdsleaklo.reserve(MAX_LO_PULSE_CIS_SMALL);
+      phase = SLEAK_START_T_LO;
+      for (int i = 0; i < MAX_LO_PULSE_CIS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolLeak5p2Shape->getPulseShapeYDY(drawerIdx, 0, 0, phase, y, dy);
+        m_shapes->m_tsleaklo.push_back(phase);
+        m_shapes->m_sleaklo.push_back(y);
+        m_shapes->m_tdsleaklo.push_back(phase);
+        m_shapes->m_dsleaklo.push_back(dy);
+        //aa	m_shapes->m_dsleaklo.push_back(m_pulsevar->m_dsleaklo[i]);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new tsleaklo = " << m_shapes->m_tsleaklo[i]
+                          << " sleaklo = " << m_shapes->m_sleaklo[i]
+                          << " dsleaklo " << m_shapes->m_dsleaklo[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old tsleaklo = " << m_pulsevar->m_tslcis[i]
+                          << " sleaklo= " << m_pulsevar->m_sleaklo[i]
+                          << " dsleaklo " << m_pulsevar->m_dsleaklo[i] << endmsg;
+        }
+        phase += CIS_DT_LO;
+      }
+      // --- high gain 5.2 pF Leak
+      m_shapes->m_sleakhi.reserve(MAX_HI_PULSE_CIS_SMALL);
+      m_shapes->m_tsleakhi.reserve(MAX_HI_PULSE_CIS_SMALL);
+      m_shapes->m_dsleakhi.reserve(MAX_HI_PULSE_CIS_SMALL);
+      m_shapes->m_tdsleakhi.reserve(MAX_HI_PULSE_CIS_SMALL);
+      phase = SLEAK_START_T_HI;
+      for (int i = 0; i < MAX_HI_PULSE_CIS; i++) {
+        //                drawerIdx, channel,gain, phase+25*(i-3), py, pdy
+        m_tileToolLeak5p2Shape->getPulseShapeYDY(drawerIdx, 0, 1, phase, y, dy);
+        m_shapes->m_tsleakhi.push_back(phase);
+        m_shapes->m_sleakhi.push_back(y);
+        m_shapes->m_tdsleakhi.push_back(phase);
+        m_shapes->m_dsleakhi.push_back(dy);
+        //aa	m_shapes->m_dsleakhi.push_back(m_pulsevar->m_dsleakhi[i]);
+        if (msgLvl(MSG::DEBUG)) {
+          msg(MSG::DEBUG) << " new  tsleakhi= " << m_shapes->m_tsleakhi[i]
+                          << " sleakhi = " << m_shapes->m_sleakhi[i]
+                          << " dsleakhi " << m_shapes->m_dsleakhi[i] << endmsg;
+
+          msg(MSG::DEBUG) << " old  tsleakhi= " << m_pulsevar->m_tsleakhi[i]
+                          << " sleakhi= " << m_pulsevar->m_sleakhi[i]
+                          << " dsleakhi " << m_pulsevar->m_dsleakhi[i] << endmsg;
+        }
+        phase += CIS_DT_HI;
+      }
+    } // endif(m_idocis)
+  } // endif "beginrun"
+}
+
+StatusCode TileRawChannelBuilderFitFilterCool::finalize() {
+
+  ATH_MSG_DEBUG( "Finalizing" );
+
+  return StatusCode::SUCCESS;
+}
+
+TileRawChannel* TileRawChannelBuilderFitFilterCool::rawChannel(const TileDigits* digits) {
+
+  ++m_chCounter;
+
+  const HWIdentifier adcId = digits->adc_HWID();
+
+  ATH_MSG_VERBOSE ( "Running FitFilter for TileRawChannel with HWID "
+                   << m_tileHWID->to_string(adcId) );
+  
+  // tmp variables for filtering
+  double amplitude = 0.0;
+  double chi2 = 0.0;
+  double time = 0.0;
+  double pedestal = 0.0;
+
+  // use fit filter
+  pulseFit(digits, amplitude, time, pedestal, chi2);
+  
+  // fit filter calib
+  // note that when called from TileROD_Decoder, m_calibrateEnergy is set
+  // from TileROD_Decoder...
+  if (m_calibrateEnergy) {
+    amplitude = m_tileInfo->CisCalib(adcId, amplitude);
+  }
+
+  ATH_MSG_VERBOSE ( "Creating RawChannel"
+                   << " a=" << amplitude
+                   << " t=" << time
+                   << " q=" << chi2
+                   << " ped=" << pedestal );
+
+  // return new TileRawChannel
+  //  TileRawChannel *rawCh = new TileRawChannel(adcId,amplitude,time,chi2,pedestal);
+
+  static DataPool<TileRawChannel> tileRchPool(m_dataPoollSize);
+  TileRawChannel *rawCh = tileRchPool.nextElementPtr();
+  rawCh->m_adc_hwid = adcId;
+  rawCh->m_amplitude.resize(1);
+  rawCh->m_amplitude[0] = amplitude;
+  rawCh->m_time.resize(1);
+  rawCh->m_time[0] = time;
+  rawCh->m_quality.resize(1);
+  rawCh->m_quality[0] = chi2;
+  rawCh->m_pedestal = pedestal;
+
+  if (m_correctTime && chi2 > 0) {
+    rawCh->insertTime(m_tileInfo->TimeCalib(adcId, time));
+    ATH_MSG_VERBOSE ( "Correcting time, new time=" << rawCh->time() );
+
+  }
+  
+  int gain = m_tileHWID->adc(adcId);
+  if (TileID::HIGHGAIN == gain) {
+    ++m_nChH;
+    m_RChSumH += amplitude;
+  } else {
+    ++m_nChL;
+    m_RChSumL += amplitude;
+  }
+
+  return rawCh;
+}
+
+/**
+ * Calculate energy, time and chi2 for one channel
+ * using fitted pulse shape. Method uses HFITV.
+ * @param samples TileDigits
+ */
+void TileRawChannelBuilderFitFilterCool::pulseFit(const TileDigits *digit, double &p_amplitude
+    , double &p_time, double &p_pedestal, double &p_chi2) {
+
+  p_amplitude = 0.0;
+  p_time = 0.0;
+  p_pedestal = 0.0;
+  p_chi2 = MAX_CHI2;
+
+  const HWIdentifier adcId = digit->adc_HWID();
+  int ros = m_tileHWID->ros(adcId);
+  int channel = m_tileHWID->channel(adcId);
+  int igain = m_tileHWID->adc(adcId);
+
+  // Estimate channel noise
+  double rms = 0.0;
+  int noise_channel = (ros < 3) ? channel : channel + 48;
+
+  if (igain == 0) {
+    switch (m_RMSChannelNoise) {
+      case 3:
+        rms = m_tileInfo->DigitsPedSigma(adcId);
+        if (rms > 0.0) break;
+      case 2:
+        rms = m_pulsevar->m_noiseNkLo[noise_channel];
+        if (rms > 0.0) break;
+      case 1:
+        rms = m_pulsevar->m_noiseOrigLo[noise_channel];
+        if (rms > 0.0) break;
+      default:
+        rms = m_noiseLow;
+    }
+  } else if (igain == 1) {
+    switch (m_RMSChannelNoise) {
+      case 3:
+        rms = m_tileInfo->DigitsPedSigma(adcId);
+        if (rms > 0.0) break;
+      case 2:
+        rms = m_pulsevar->m_noiseNkHi[noise_channel];
+        if (rms > 0.0) break;
+      case 1:
+        rms = m_pulsevar->m_noiseOrigHi[noise_channel];
+        if (rms > 0.0) break;
+      default:
+        rms = m_noiseHigh;
+    }
+  } else {
+    // neither low nor hi-gain, (e.g. adder data)
+    p_chi2 = -1.0;
+    return;
+  }
+
+  ATH_MSG_VERBOSE ( "PulseFit:"
+                   << " ROS=" << ros
+                   << " Channel=" << channel
+                   << " gain=" << igain
+                   << " RMS=" << rms
+                   << " RMSChNoise=" << m_RMSChannelNoise );
+
+  // First sample to fit, number of samples to fit
+  int ifit1 = 0;
+  int nfit = std::min(m_frameLength, digit->NtimeSamples());
+
+  int ipeakMax = m_ipeak0;
+  int ipeakMin = m_ipeak0;
+
+  ATH_MSG_VERBOSE ( "ipeak0=" << m_ipeak0
+                   << " ifit1=" << ifit1
+                   << " ifit2=" << ifit1 + nfit - 1
+                   << " nfit=" << nfit
+                   << " idolas=" << m_idolas
+                   << " idocis=" << m_idocis
+                   << " CISchan=" << m_cischan
+                   << " capdaq=" << m_capdaq );
+
+  std::vector<float> samples = digit->samples();
+  double maxsamp = 0.0;
+  double minsamp = SATURATED_ADC_VALUE;
+
+  double xvec[MAX_SAMPLES], yvec0[MAX_SAMPLES];
+  double yvec[MAX_SAMPLES], eyvec[MAX_SAMPLES];
+
+  bool no_signal = true;
+  p_pedestal = samples[0];
+  const double delta = 1.e-6;
+
+  for (int isamp = 0; isamp < nfit; ++isamp) {
+    int j = isamp + ifit1;
+    xvec[isamp] = j;
+    yvec0[isamp] = samples[j];
+    if (no_signal) no_signal = (fabs(samples[j] - p_pedestal) < delta);
+
+    // Check for saturated or zero samples and de-weight accordingly
+    if ((yvec0[isamp] < SATURATED_ADC_VALUE) && (yvec0[isamp] > 0)) {
+      eyvec[isamp] = rms;
+    } else {
+      if (yvec0[isamp] >= SATURATED_ADC_VALUE) {
+        eyvec[isamp] = m_SaturatedSampleError * rms;
+        ATH_MSG_VERBOSE ( "Saturated ADC value yvec0[" << isamp << "]=" << yvec0[isamp]
+                         << " (MAX=" << SATURATED_ADC_VALUE << " ) RMS=" << eyvec[isamp] );
+      } else { // must be yvec0[isamp]==0
+        eyvec[isamp] = m_ZeroSampleError * rms;
+        ATH_MSG_VERBOSE ( "Zero ADC value yvec0[" << isamp << "]=" << yvec0[isamp]
+                         << " RMS=" << eyvec[isamp] );
+      }
+    }
+    
+    if (yvec0[isamp] > maxsamp) {
+      // initial time guess based on
+      // sample with maximum value
+      maxsamp = yvec0[isamp];
+      ipeakMax = j;
+    }
+    if (yvec0[isamp] < minsamp) {
+      minsamp = yvec0[isamp];
+      ipeakMin = j;
+    }
+    ATH_MSG_VERBOSE ( "isamp=" << isamp
+                     << ", xvec=" << xvec[isamp]
+                     << ", yvec0=" << yvec0[isamp]
+                     << ", eyvec=" << eyvec[isamp] );
+  }
+
+  if (no_signal) {
+    ATH_MSG_VERBOSE ( "No signal detected" );
+    return;
+  }
+
+  // Make an initial guess about pedestal
+  double pedg = (yvec0[0] + yvec0[nfit - 1]) / 2.0;
+
+  // Position of max sample compared to nominal peak position
+  int delta_peak = 0;
+  // Time offset in pulse functions
+  m_t0fit = 0.0;
+  // Flag for fixed time in fit
+  bool fixedTime = (m_maxIterate < 0);
+  
+  if (!fixedTime) {
+    if (maxsamp - pedg > m_NoiseThresholdRMS * rms) {
+      delta_peak = ipeakMax - m_ipeak0;  // Adjust initial phase guess,
+      m_t0fit = (delta_peak) * DTIME;       // positive amplitude
+    } else if (pedg - minsamp > m_NoiseThresholdRMS * rms) {
+      delta_peak = ipeakMin - m_ipeak0;       // Adjust initial phase guess,
+      m_t0fit = (delta_peak) * DTIME;     // negative amplitude
+    } else {
+      fixedTime = true; // no signal above noise
+      m_t0fit = 0.0;    // fit with fixed time
+    }
+  }
+  
+  ATH_MSG_VERBOSE ( " initial value of t0fit=" << m_t0fit
+                   << " ipeakMax=" << ipeakMax
+                   << " ipeakMin=" << ipeakMin
+                   << " fixedTime=" << ((fixedTime) ? "true" : "false") );
+
+  std::vector<double> *tpulse = &m_dummy, *ypulse = &m_dummy, *tdpulse = &m_dummy, *dpulse = &m_dummy;
+  std::vector<double> *tleak = &m_dummy, *yleak = &m_dummy, *tdleak = &m_dummy, *dleak = &m_dummy;
+  
+  if (m_idocis && ((m_cischan == -1) || (channel == m_cischan))) { // CIS pulse
+    if (igain == 0) { // low gain
+      if (m_capdaq > 10) { // 100 pF capacitor
+        tpulse = &(m_pulsevar->m_tlcis);
+        ypulse = &(m_pulsevar->m_ylcis);
+        tdpulse = &(m_pulsevar->m_tdlcis);
+        dpulse = &(m_pulsevar->m_ydlcis);
+        tleak = &(m_pulsevar->m_tleaklo);
+        yleak = &(m_pulsevar->m_leaklo);
+        tdleak = &(m_pulsevar->m_tdleaklo);
+        dleak = &(m_pulsevar->m_dleaklo);
+      } else { // 5.2 pF capacitor
+        tpulse = &(m_pulsevar->m_tslcis);
+        ypulse = &(m_pulsevar->m_yslcis);
+        tdpulse = &(m_pulsevar->m_tdslcis);
+        dpulse = &(m_pulsevar->m_ydslcis);
+        tleak = &(m_pulsevar->m_tsleaklo);
+        yleak = &(m_pulsevar->m_sleaklo);
+        tdleak = &(m_pulsevar->m_tdsleaklo);
+        dleak = &(m_pulsevar->m_dsleaklo);
+      }
+    } else { // igain==1 => high-gain
+      if (m_capdaq > 10) { // 100 pF capacitor
+        tpulse = &(m_pulsevar->m_thcis);
+        ypulse = &(m_pulsevar->m_yhcis);
+        tdpulse = &(m_pulsevar->m_tdhcis);
+        dpulse = &(m_pulsevar->m_ydhcis);
+        tleak = &(m_pulsevar->m_tleakhi);
+        yleak = &(m_pulsevar->m_leakhi);
+        tdleak = &(m_pulsevar->m_tdleakhi);
+        dleak = &(m_pulsevar->m_dleakhi);
+      } else { // 5.2 pF capacitor
+        tpulse = &(m_pulsevar->m_tshcis);
+        ypulse = &(m_pulsevar->m_yshcis);
+        tdpulse = &(m_pulsevar->m_tdshcis);
+        dpulse = &(m_pulsevar->m_ydshcis);
+        tleak = &(m_pulsevar->m_tsleakhi);
+        yleak = &(m_pulsevar->m_sleakhi);
+        tdleak = &(m_pulsevar->m_tdsleakhi);
+        dleak = &(m_pulsevar->m_dsleakhi);
+      }
+    }
+  } else {
+    if (m_idolas) { // laser pulse
+      if (igain == 0) { // low gain
+        tpulse = &(m_pulsevar->m_tllas);
+        ypulse = &(m_pulsevar->m_yllas);
+        tdpulse = &(m_pulsevar->m_tdllas);
+        dpulse = &(m_pulsevar->m_ydllas);
+      } else { // igain==1 => high-gain
+        tpulse = &(m_pulsevar->m_thlas);
+        ypulse = &(m_pulsevar->m_yhlas);
+        tdpulse = &(m_pulsevar->m_tdhlas);
+        dpulse = &(m_pulsevar->m_ydhlas);
+      }
+    } else { // physics pulse
+      if (igain == 0) { // low gain
+        tpulse = &(m_pulsevar->m_tlphys);
+        ypulse = &(m_pulsevar->m_ylphys);
+        tdpulse = &(m_pulsevar->m_tdlphys);
+        dpulse = &(m_pulsevar->m_ydlphys);
+      } else { // igain==1 => high-gain
+        tpulse = &(m_pulsevar->m_thphys);
+        ypulse = &(m_pulsevar->m_yhphys);
+        tdpulse = &(m_pulsevar->m_tdhphys);
+        dpulse = &(m_pulsevar->m_ydhphys);
+      }
+    }
+  }
+  
+  // Variables used for iterative fitting
+  double gval, gpval, sy, syg, sygp, sg, sgp, sgg, sgpgp, sggp, serr, err2;
+  double dgg0, dgg, dggp, dgpgp, dyg, dygp, dg, dc, xd;
+  double sllp, sylp, slplp, dleakage, leakage;
+  double fixtau = 0.0, fixped = 0.0, fixampl = 0.0, fixchi2 = MAX_CHI2;
+  double leaktau = 0.0, leakped = 0.0, leakampl = 0.0, leakchi2 = MAX_CHI2;
+  double cistau = 0.0, cisped = 0.0, cisampl = 0.0, cisatau = 0.0, cischi2 = MAX_CHI2;
+  double tau, ped, ampl, atau = 0.0, chi2 = MAX_CHI2, oldchi2 = MAX_CHI2 / 2;
+
+  // number of iterations
+  int niter = 0;
+  do {
+    ++niter;
+    ATH_MSG_VERBOSE ( "niter=" << niter << " maxIterate=" << m_maxIterate );
+
+    if (chi2 < oldchi2) oldchi2 = chi2; // best chi2 up to now
+
+    // parameters for pulse shape functions
+    // 0. phase
+    m_fnpar[0] = 0.0;
+    // 1. pedestal 
+    m_fnpar[1] = 0.0;
+    // 2. amplitude
+    m_fnpar[2] = 1.0;
+
+    // CIS events linear fit
+    if (m_idocis && ((m_cischan == -1) || (channel == m_cischan))) {
+      ATH_MSG_VERBOSE ( "Fit time with leakage" );
+      // CIS Part (A): fit for time using leakage pulse
+      sllp = 0.0;
+      sylp = 0.0;
+      slplp = 0.0;
+      for (int isamp = 0; isamp < nfit; ++isamp) {
+        ATH_MSG_VERBOSE ( "Lo gain leakage xvec[" << isamp << "]=" << xvec[isamp] );
+
+        leakage = pulse(xvec[isamp], tleak, yleak);
+        dleakage = deriv(xvec[isamp], tdleak, dleak);
+
+        // Samples with pedestal subtracted
+        yvec[isamp] = yvec0[isamp] - pedg;
+
+        ATH_MSG_VERBOSE ( " yvec[" << isamp << "]=" << yvec[isamp]
+                         << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                         << " pedg=" << pedg );
+
+        sllp += leakage * dleakage;
+        sylp += yvec[isamp] * dleakage;
+        slplp += dleakage * dleakage;
+      }
+      // Also allow for fixed-time fit to CIS events
+      if (fabs(slplp) > EPS_DG && !fixedTime) {
+        leaktau = (sllp - sylp) / slplp;
+        // Also have to check the range for leaktau
+        if (leaktau > m_max_tau)
+          leaktau = m_max_tau;
+        else if (leaktau < m_min_tau) leaktau = m_min_tau;
+      } else {
+        leaktau = 0.0;
+      }
+      
+      ATH_MSG_VERBOSE ( " sllp=" << sllp
+                      << " sylp=" << sylp
+                      << " slplp=" << slplp
+                      << " leaktau=" << leaktau );
+
+      // CIS Part (B): using phase determined in part (A), 
+      // subtract leakage pedestal and fit for amplitude, pedestal
+      m_fnpar[0] = leaktau;
+      sy = 0.0;
+      sg = 0.0;
+      syg = 0.0;
+      sgg = 0.0;
+      serr = 0.0;
+      for (int isamp = 0; isamp < nfit; ++isamp) {
+        leakage = pulse(xvec[isamp], tleak, yleak);
+        gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+        yvec[isamp] = yvec0[isamp] - leakage;
+
+        ATH_MSG_VERBOSE ( " yvec[" << isamp << "]=" << yvec[isamp]
+                         << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                         << " leakage=" << leakage );
+
+        err2 = eyvec[isamp] * eyvec[isamp];
+        sy += yvec[isamp] / err2;
+        sg += gval / err2;
+        syg += yvec[isamp] * gval / err2;
+        sgg += gval * gval / err2;
+        serr += 1.0 / err2;
+      }
+      dgg0 = sg * sg - serr * sgg;
+      if (fabs(dgg0) > EPS_DG) {
+        leakampl = (sy * sg - serr * syg) / dgg0;
+        leakped = (syg * sg - sy * sgg) / dgg0;
+      } else {
+        leakampl = 0.0;
+        leakped = sy / serr;
+      }
+
+      // Determine Chi2 for corresponding function  for CIS leakage + pulse
+      ATH_MSG_VERBOSE ( " Determine Chi2 for CIS leakage + pulse" );
+      
+      leakchi2 = 0.0;
+      m_fnpar[0] = leaktau;
+      m_fnpar[1] = leakped;
+      m_fnpar[2] = leakampl;
+      for (int isamp = 0; isamp < nfit; ++isamp) {
+        gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+        leakage = pulse(xvec[isamp], tleak, yleak);
+        xd = yvec0[isamp] - (gval + leakage);
+        leakchi2 = leakchi2 + (xd * xd) / (eyvec[isamp] * eyvec[isamp]);
+
+        ATH_MSG_VERBOSE ( " isamp=" << isamp
+                         << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                         << " gval=" << gval
+                         << ", leakage=" << leakage
+                         << ", xd=" << xd );
+      }
+      leakchi2 = leakchi2 / (nfit - 3.0);
+
+      ATH_MSG_VERBOSE ( " leaktau=" << leaktau
+                       << " leakped=" << leakped
+                       << " leakampl=" << leakampl
+                       << " leakchi2=" << leakchi2 );
+      
+      // CIS Part C: Least-squares fit with 3 parameters for pulse+leakage
+      if (!fixedTime) {
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 0.0;
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          leakage = pulse(xvec[isamp], tleak, yleak);
+
+          // Subtract leakage from samples
+          yvec[isamp] = yvec0[isamp] - leakage;
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                          << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                          << " leakage=" << leakage
+                          << " yvec[" << isamp << "]=" << yvec[isamp] );
+        }
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 1.0;
+        sy = 0.0;
+        sg = 0.0;
+        sgp = 0.0;
+        syg = 0.0;
+        sygp = 0.0;
+        sgg = 0.0;
+        sggp = 0.0;
+        sgpgp = 0.0;
+        serr = 0.0;
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          gpval = deriv(xvec[isamp], tdpulse, dpulse);
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec[isamp] / err2;
+          sg += gval / err2;
+          sgp += gpval / err2;
+          syg += yvec[isamp] * gval / err2;
+          sygp += yvec[isamp] * gpval / err2;
+          sgg += gval * gval / err2;
+          sggp += gval * gpval / err2;
+          sgpgp += gpval * gpval / err2;
+          serr += 1.0 / err2;
+        }
+        dgg = sgg - sg * sg / serr;
+        dggp = sggp - sg * sgp / serr;
+        dgpgp = sgpgp - sgp * sgp / serr;
+        dyg = syg - sy * sg / serr;
+        dygp = sygp - sy * sgp / serr;
+        dg = dgg * dgpgp - dggp * dggp;
+
+        if (fabs(dg) > EPS_DG) {
+          cisampl = (dyg * dgpgp - dygp * dggp) / dg;
+          cisatau = (dyg * dggp - dygp * dgg) / dg;
+          cisped = (sy
+              - (dyg * dgpgp * sg - dygp * dggp * sg + dyg * dggp * sgp - dygp * dgg * sgp) / dg)
+              / serr;
+
+          if (fabs(cisampl) > EPS_DG) {
+            cistau = cisatau / cisampl;
+            if (cistau > m_max_tau)
+              cistau = m_max_tau;
+            else if (cistau < m_min_tau) cistau = m_min_tau;
+          } else {
+            cistau = 0.0;
+          }
+        } else {
+          cisampl = 0.0;
+          cisatau = 0.0;
+          cistau = 0.0;
+          cisped = sy / serr;
+        }
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " sy=" << sy
+                            << " sg=" << sg
+                            << " sgp=" << sgp
+                            << " syg=" << syg
+                            << " sygp=" << sygp
+                            << " sgg=" << sgg
+                            << " sggp=" << sggp
+                            << " sgpgp=" << sgpgp << endmsg;
+
+          msg(MSG::VERBOSE) << " dgg=" << dgg
+                            << " dggp=" << dggp
+                            << " sgpgp=" << sgpgp
+                            << " dyg=" << dyg
+                            << " dygp=" << dygp
+                            << " dg=" << dg << endmsg;
+
+          msg(MSG::VERBOSE) << " cistau=" << cistau
+                            << " cisped=" << cisped
+                            << " cisampl=" << cisampl << endmsg;
+        }
+
+        // Determine Chi2 for pulse shape + leakage fit CIS Part C
+        cischi2 = 0.0;
+        m_fnpar[0] = cistau;
+        m_fnpar[1] = cisped;
+        m_fnpar[2] = cisampl;
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          // Subtract leakage from samples
+          yvec[isamp] = yvec0[isamp] - leakage;
+          xd = yvec[isamp] - gval;
+          cischi2 = cischi2 + (xd * xd) / (eyvec[isamp] * eyvec[isamp]);
+
+          ATH_MSG_VERBOSE( " yvec0[" << isamp << "]=" << yvec0[isamp]
+                          << " yvec[" << isamp << "]=" << yvec[isamp]
+                          << " leakage=" << leakage
+                          << " gval=" << gval
+                          << " xd=" << xd );
+        }
+        cischi2 = cischi2 / (nfit - 3.0);
+        
+        ATH_MSG_VERBOSE ( " cischi2=" << cischi2 );
+      }
+
+      // Determine which set of parameters to use from CIS fit methods based on minimum chi2
+      if ((cischi2 < leakchi2) && !fixedTime) {
+        tau = cistau;
+        ped = cisped;
+        ampl = cisampl;
+        chi2 = cischi2;
+      } else {
+        tau = leaktau;
+        ped = leakped;
+        ampl = leakampl;
+        chi2 = leakchi2;
+      }
+    }
+    // End of fit for CIS events
+
+    else {    // Physics and laser events
+
+      if (niter == 1) { /* For first iteration, also calculate 2-Parameter Fit
+       for pedestal and amplitude */
+        double t0fit_old = m_t0fit;
+        m_t0fit = 0.0;
+
+        sy = 0.0;
+        sg = 0.0;
+        sgg = 0.0;
+        syg = 0.0;
+        serr = 0.0;
+
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          if (!m_idolas) {
+            // Use initial values for speeding up the physics events
+            int jsamp = (int) xvec[isamp] - delta_peak;
+            if (jsamp < 0)
+              jsamp = 0;
+            else if (jsamp >= nfit) jsamp = nfit - 1;
+
+            if (igain == 0) {
+              gval = m_gphyslo[jsamp];
+            } else {
+              gval = m_gphyshi[jsamp];
+            }
+          } else {
+            gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          }
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec0[isamp] / err2;
+          sg += gval / err2;
+          syg += yvec0[isamp] * gval / err2;
+          sgg += gval * gval / err2;
+          serr += 1.0 / err2;
+        }
+        fixtau = 0.0;
+        dgg0 = sg * sg - serr * sgg;
+        if (fabs(dgg0) > EPS_DG) {
+          fixampl = (sy * sg - serr * syg) / dgg0;
+          fixped = (syg * sg - sy * sgg) / dgg0;
+          ATH_MSG_VERBOSE ( "  2-par fit:"
+                           << " fixampl = " << fixampl
+                           << " fixped = " << fixped );
+        } else {
+          fixampl = 0.0;
+          fixped = sy / serr;
+          ATH_MSG_VERBOSE ( "  2-par fit:"
+                          << " small dgg0 = " << dgg0
+                          << ", fixampl = " << fixampl
+                          << " fixped = " << fixped  );
+        }
+        m_fnpar[0] = fixtau; /* 2-Par fit Calculate chi2 for physics events */
+        m_fnpar[1] = fixped;
+        m_fnpar[2] = fixampl;
+        fixchi2 = 0.0;
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          dc = yvec0[isamp] - scaledpulse(xvec[isamp], tpulse, ypulse);
+          fixchi2 = fixchi2 + (dc * dc) / (eyvec[isamp] * eyvec[isamp]);
+          ATH_MSG_VERBOSE ( "   isamp= " << isamp
+                           << " yvec0[" << isamp << "]= " << yvec0[isamp]
+                           << " eyvec[" << isamp << "]= " << eyvec[isamp]
+                           << " fixchi2= " << fixchi2  );
+        }
+        fixchi2 = fixchi2 / (nfit - 2.0);
+        ATH_MSG_VERBOSE ( "  fixchi2/(nfit-2.0)=" << fixchi2 << " nfit=" << nfit );
+
+        m_t0fit = t0fit_old;
+
+        // restore initial parameters for pulse shape functions - to be used in 3-par fit
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 1.0;
+      } /* end of 2-par fit in first iteration */
+
+      if (fixedTime) {
+        m_t0fit = 0.0;
+        tau = fixtau;
+        ped = fixped;
+        ampl = fixampl;
+        chi2 = oldchi2 = -fabs(fixchi2);
+      } else {
+
+        sy = 0.0;
+        sg = 0.0;
+        sgp = 0.0;
+        syg = 0.0;
+        sygp = 0.0;
+        sgg = 0.0;
+        sggp = 0.0;
+        sgpgp = 0.0;
+        serr = 0.0;
+
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          if ((niter == 1) && (!m_idolas)) {
+            // Use initial function values stored in array for niter=1 physics
+            // XXX: double->int
+            int jsamp = (int) xvec[isamp] - delta_peak;
+            if (jsamp < 0)
+              jsamp = 0;
+            else if (jsamp >= nfit) jsamp = nfit - 1;
+
+            if (igain == 0) {       // igain ==0 => low-gain
+              gval = m_gphyslo[jsamp];
+              gpval = m_dgphyslo[jsamp];
+            } else { 	          // must be igain==1 => high-gain
+              gval = m_gphyshi[jsamp];
+              gpval = m_dgphyshi[jsamp];
+            }
+          } else {
+            // Use the respective function values
+            gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+            gpval = deriv(xvec[isamp], tdpulse, dpulse);
+          }
+
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec0[isamp] / err2;
+          sg += gval / err2;
+          sgp += gpval / err2;
+          syg += yvec0[isamp] * gval / err2;
+          sygp += yvec0[isamp] * gpval / err2;
+          sgg += gval * gval / err2;
+          sggp += gval * gpval / err2;
+          sgpgp += gpval * gpval / err2;
+          serr += 1.0 / err2;
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                          << " gval=" << gval
+                          << " sg=" << sg
+                          << " gpval=" << gpval
+                          << " sgp=" << sgp );
+        }
+
+        dgg = sgg - sg * sg / serr;
+        dggp = sggp - sg * sgp / serr;
+        dgpgp = sgpgp - sgp * sgp / serr;
+        dyg = syg - sy * sg / serr;
+        dygp = sygp - sy * sgp / serr;
+        dg = dgg * dgpgp - dggp * dggp;
+
+        // Fit for time, pedestal, and amplitude
+        if (fabs(dg) > EPS_DG) {
+          // Amplitude           : ampl
+          ampl = (dyg * dgpgp - dygp * dggp) / dg;
+          // and Amplitude * time: atau
+          atau = (dyg * dggp - dygp * dgg) / dg;
+          // Pedestal
+          ped = (sy - ((dyg * dgpgp - dygp * dggp) * sg + (dyg * dggp - dygp * dgg) * sgp) / dg)
+              / serr;
+
+          if (fabs(ampl) > EPS_DG) {
+            // Time
+            tau = atau / ampl;
+            if (tau > m_max_tau)
+              tau = m_max_tau;
+            else if (tau < m_min_tau) tau = m_min_tau;
+          } else {
+            tau = 0.0;
+          }
+        } else {
+          ampl = 0.0;
+          atau = 0.0;
+          tau = 0.0;
+          ped = sy / serr;
+        }
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " ped=" << ped << endmsg;
+          msg(MSG::VERBOSE) << " sy=" << sy
+                            << " sg=" << sg
+                            << " sgp=" << sgp << endmsg;
+
+          msg(MSG::VERBOSE) << " syg=" << syg
+                            << " sygp=" << sygp
+                            << " sgg=" << sgg << endmsg;
+
+          msg(MSG::VERBOSE) << " sggp=" << sggp
+                            << " sgpgp=" << sgpgp << endmsg;
+
+          msg(MSG::VERBOSE) << " ampl = (dyg*dgpgp - dygp*dggp)= " << ampl << endmsg;
+
+          msg(MSG::VERBOSE) << " dyg=" << dyg
+                            << " dgpgp=" << dgpgp
+                            << " dyg*dgpgp=" << (dyg * dgpgp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dygp=" << dygp
+                            << " dggp=" << dggp
+                            << " dygp*dggp=" << (dygp * dggp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dyg=" << dyg
+                            << " dggp=" << dggp
+                            << " dyg*dggp=" << (dyg * dggp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dygp=" << dygp
+                            << " dgg=" << dgg
+                            << " dygp*dgg=" << (dygp * dgg) << endmsg;
+
+          msg(MSG::VERBOSE) << " dg=" << dg
+                            << " atau=" << atau
+                            << " tau=" << tau << endmsg;
+        }
+
+        m_fnpar[0] = tau;
+        m_fnpar[1] = ped;
+        m_fnpar[2] = ampl;
+
+        chi2 = 0;
+        // Calculate chi2 for physics and laser events
+        for (int isamp = 0; isamp < nfit; ++isamp) {
+          dc = yvec0[isamp] - scaledpulse(xvec[isamp], tpulse, ypulse);
+          chi2 = chi2 + (dc * dc) / (eyvec[isamp] * eyvec[isamp]);
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                            << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                            << " eyvec[" << isamp << "]=" << eyvec[isamp]
+                            << " dc=" << dc << " chi2=" << chi2 );
+        }
+        chi2 = chi2 / (nfit - 3.0);
+        ATH_MSG_VERBOSE ( " chi2/(nfit-3.0)=" << chi2 << " nfit=" << nfit );
+      } // end if fixedTime
+    } // end of physics and laser specific part
+
+    if (msgLvl(MSG::VERBOSE))
+      msg(MSG::VERBOSE) << " t0fit: " << m_t0fit << ((tau < 0.0) ? " - " : " + ") << fabs(tau);
+
+    // avoid infinite loop at the boundary
+    if (fabs(m_t0fit - m_max_time) < 0.001 && tau >= 0.0) { // trying to go outside max boudary second time
+      m_t0fit = fixtau + (m_min_time - fixtau) * niter / m_maxIterate; // jump to negative time
+      if (msgLvl(MSG::VERBOSE)) msg(MSG::VERBOSE) << " jumping to " << m_t0fit << endmsg;
+      chi2 = MAX_CHI2;
+    } else if (fabs(m_t0fit - m_min_time) < 0.001 && tau <= 0.0) { // trying to go outside min boudary second time
+      m_t0fit = fixtau + (m_max_time - fixtau) * niter / m_maxIterate; // jump to positive time
+      if (msgLvl(MSG::VERBOSE)) msg(MSG::VERBOSE) << " jumping to " << m_t0fit << endmsg;
+      chi2 = MAX_CHI2;
+    } else {
+
+      // Iteration with parameter for time 
+      m_t0fit += tau;
+      if (msgLvl(MSG::VERBOSE)) msg(MSG::VERBOSE) << " = " << m_t0fit << endmsg;
+
+      // Check if total time does not exceed the limits:
+      if (m_t0fit > m_max_time) {
+        m_t0fit = m_max_time;
+        chi2 = MAX_CHI2;
+      } else if (m_t0fit < m_min_time) {
+        m_t0fit = m_min_time;
+        chi2 = MAX_CHI2;
+      }
+    }
+    
+    // save values of the best iteration
+    if (chi2 < p_chi2) {
+      p_time = m_t0fit;
+      p_pedestal = ped;
+      p_amplitude = ampl;
+      p_chi2 = chi2;
+    }
+
+    if (!fixedTime && chi2 < MAX_CHI2 && fabs(tau) < EPS_DG) { // too small tau
+      if (m_t0fit > fixtau)
+        m_t0fit = fixtau + (m_min_time - fixtau) * niter / m_maxIterate; // jump to negative time
+      else
+        m_t0fit = fixtau + (m_max_time - fixtau) * niter / m_maxIterate; // jump to positive time
+
+      ATH_MSG_VERBOSE ( " too small tau - jump to " << m_t0fit );
+    }
+    
+    ATH_MSG_VERBOSE ( " iter=" << niter
+                     << " t0fit=" << m_t0fit
+                     << " phase=" << tau
+                     << " ped=" << ped
+                     << " ampl=" << ampl
+                     << " chi2=" << chi2 );
+
+  } while (fabs(chi2 - oldchi2) > DELTA_CHI2 && (niter < m_maxIterate));
+  
+  //NGO never use the 2par fit result if non-pedestal event was detected!
+//   if ((fabs(fixchi2) <= fabs(p_chi2)) && 
+//       !(m_idocis && ((m_cischan == -1) || (channel == m_cischan)))) {
+//     /* results from 2-par fit */
+//     p_time      = fixtau;
+//     p_pedestal  = fixped;
+//     p_amplitude = fixampl;
+//     p_chi2      = - fabs(fixchi2);
+//   }
+
+// TD: fit converged, now one extra iteration, leaving out the samples that
+// are more then m_MaxTimeFromPeak ns from the peak. We want to avoid to 
+// extrapolate the pulse shape beyond the region where it is known.
+
+// Do it only in case when at least last sample is beyond m_MaxTimeFromPeak:
+  if ((nfit - 1 - m_ipeak0) * DTIME > p_time + m_MaxTimeFromPeak) {
+
+    ATH_MSG_VERBOSE ( "Result before last iteration:"
+                     << " Time=" << p_time
+                     << " Ped=" << p_pedestal
+                     << " Amplitude=" << p_amplitude
+                     << " Chi2=" << p_chi2);
+
+    m_t0fit = p_time;
+    int nfit_real;
+
+    // parameters for pulse shape functions
+    // 0. phase
+    m_fnpar[0] = 0.0;
+    // 1. pedestal 
+    m_fnpar[1] = 0.0;
+    // 2. amplitude
+    m_fnpar[2] = 1.0;
+
+    // CIS events linear fit
+    if (m_idocis && ((m_cischan == -1) || (channel == m_cischan))) {
+      if (!fixedTime) {
+        ATH_MSG_VERBOSE ( "Fit time with leakage" );
+        // CIS Part (A): fit for time using leakage pulse
+        sllp = 0.0;
+        sylp = 0.0;
+        slplp = 0.0;
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+          ATH_MSG_VERBOSE ( "Lo gain leakage xvec[" << isamp << "]=" << xvec[isamp] );
+
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          dleakage = deriv(xvec[isamp], tdleak, dleak);
+
+          // Samples with pedestal subtracted
+          yvec[isamp] = yvec0[isamp] - pedg;
+
+          ATH_MSG_VERBOSE ( " yvec[" << isamp << "]=" << yvec[isamp]
+                           << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " pedg=" << pedg );
+
+          sllp += leakage * dleakage;
+          sylp += yvec[isamp] * dleakage;
+          slplp += dleakage * dleakage;
+        }
+        // Also allow for fixed-time fit to CIS events
+        if (fabs(slplp) > EPS_DG && !fixedTime) {
+          leaktau = (sllp - sylp) / slplp;
+          // Also have to check the range for leaktau
+          if (leaktau > m_max_tau)
+            leaktau = m_max_tau;
+          else if (leaktau < m_min_tau) leaktau = m_min_tau;
+        } else {
+          leaktau = 0.0;
+        }
+
+        ATH_MSG_VERBOSE ( " sllp=" << sllp
+                        << " sylp=" << sylp
+                        << " slplp=" << slplp
+                        << " leaktau=" << leaktau );
+
+        // CIS Part (B): using phase determined in part (A), 
+        // subtract leakage pedestal and fit for amplitude, pedestal
+        m_fnpar[0] = leaktau;
+        sy = 0.0;
+        sg = 0.0;
+        syg = 0.0;
+        sgg = 0.0;
+        serr = 0.0;
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          yvec[isamp] = yvec0[isamp] - leakage;
+
+          ATH_MSG_VERBOSE ( " yvec[" << isamp << "]=" << yvec[isamp]
+                           << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " leakage=" << leakage );
+
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec[isamp] / err2;
+          sg += gval / err2;
+          syg += yvec[isamp] * gval / err2;
+          sgg += gval * gval / err2;
+          serr += 1.0 / err2;
+        }
+        dgg0 = sg * sg - serr * sgg;
+        if (fabs(dgg0) > EPS_DG) {
+          leakampl = (sy * sg - serr * syg) / dgg0;
+          leakped = (syg * sg - sy * sgg) / dgg0;
+        } else {
+          leakampl = 0.0;
+          leakped = sy / serr;
+        }
+
+        // Determine Chi2 for corresponding function  for CIS leakage + pulse
+        ATH_MSG_VERBOSE( " Determine Chi2 for CIS leakage + pulse");
+
+        leakchi2 = 0.0;
+        nfit_real = 0;
+        m_fnpar[0] = leaktau;
+        m_fnpar[1] = leakped;
+        m_fnpar[2] = leakampl;
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+          ++nfit_real;
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          xd = yvec0[isamp] - (gval + leakage);
+          leakchi2 = leakchi2 + (xd * xd) / (eyvec[isamp] * eyvec[isamp]);
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                           << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " gval=" << gval
+                           << ", leakage=" << leakage
+                           << ", xd=" << xd );
+        }
+        leakchi2 = leakchi2 / (nfit_real - 3.0);
+
+        ATH_MSG_VERBOSE ( " leaktau=" << leaktau
+                         << " leakped=" << leakped
+                         << " leakampl=" << leakampl
+                         << " leakchi2=" << leakchi2 );
+
+        // CIS Part C: Least-squares fit with 3 parameters for pulse+leakage
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 0.0;
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+
+          leakage = pulse(xvec[isamp], tleak, yleak);
+
+          // Subtract leakage from samples
+          yvec[isamp] = yvec0[isamp] - leakage;
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                           << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " leakage=" << leakage
+                           << " yvec[" << isamp << "]=" << yvec[isamp] );
+        }
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 1.0;
+        sy = 0.0;
+        sg = 0.0;
+        sgp = 0.0;
+        syg = 0.0;
+        sygp = 0.0;
+        sgg = 0.0;
+        sggp = 0.0;
+        sgpgp = 0.0;
+        serr = 0.0;
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          gpval = deriv(xvec[isamp], tdpulse, dpulse);
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec[isamp] / err2;
+          sg += gval / err2;
+          sgp += gpval / err2;
+          syg += yvec[isamp] * gval / err2;
+          sygp += yvec[isamp] * gpval / err2;
+          sgg += gval * gval / err2;
+          sggp += gval * gpval / err2;
+          sgpgp += gpval * gpval / err2;
+          serr += 1.0 / err2;
+        }
+        dgg = sgg - sg * sg / serr;
+        dggp = sggp - sg * sgp / serr;
+        dgpgp = sgpgp - sgp * sgp / serr;
+        dyg = syg - sy * sg / serr;
+        dygp = sygp - sy * sgp / serr;
+        dg = dgg * dgpgp - dggp * dggp;
+
+        if (fabs(dg) > EPS_DG) {
+          cisampl = (dyg * dgpgp - dygp * dggp) / dg;
+          cisatau = (dyg * dggp - dygp * dgg) / dg;
+          cisped = (sy
+              - (dyg * dgpgp * sg - dygp * dggp * sg + dyg * dggp * sgp - dygp * dgg * sgp) / dg)
+              / serr;
+
+          if (fabs(cisampl) > EPS_DG) {
+            cistau = cisatau / cisampl;
+            if (cistau > m_max_tau)
+              cistau = m_max_tau;
+            else if (cistau < m_min_tau) cistau = m_min_tau;
+          } else {
+            cistau = 0.0;
+          }
+        } else {
+          cisampl = 0.0;
+          cisatau = 0.0;
+          cistau = 0.0;
+          cisped = sy / serr;
+        }
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " sy=" << sy
+                            << " sg=" << sg
+                            << " sgp=" << sgp
+                            << " syg=" << syg
+                            << " sygp=" << sygp
+                            << " sgg=" << sgg
+                            << " sggp=" << sggp
+                            << " sgpgp=" << sgpgp << endmsg;
+
+          msg(MSG::VERBOSE) << " dgg=" << dgg
+                            << " dggp=" << dggp
+                            << " sgpgp=" << sgpgp
+                            << " dyg=" << dyg
+                            << " dygp=" << dygp
+                            << " dg=" << dg << endmsg;
+
+          msg(MSG::VERBOSE) << " cistau=" << cistau
+                            << " cisped=" << cisped
+                            << " cisampl=" << cisampl << endmsg;
+        }
+
+        // Determine Chi2 for pulse shape + leakage fit CIS 
+        cischi2 = 0.0;
+        nfit_real = 0;
+        m_fnpar[0] = cistau;
+        m_fnpar[1] = cisped;
+        m_fnpar[2] = cisampl;
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+          ++nfit_real;
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          leakage = pulse(xvec[isamp], tleak, yleak);
+          // Subtract leakage from samples
+          yvec[isamp] = yvec0[isamp] - leakage;
+          xd = yvec[isamp] - gval;
+          cischi2 = cischi2 + (xd * xd) / (eyvec[isamp] * eyvec[isamp]);
+
+          ATH_MSG_VERBOSE ( " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " yvec[" << isamp << "]=" << yvec[isamp]
+                           << " leakage=" << leakage
+                           << " gval=" << gval
+                           << " xd=" << xd );
+        }
+        cischi2 = cischi2 / (nfit_real - 3.0);
+
+        ATH_MSG_VERBOSE ( " cischi2=" << cischi2 );
+
+        // Determine which set of parameters to use from CIS fit methods based on minimum chi2
+        if ((cischi2 < leakchi2) && !fixedTime) {
+          tau = cistau;
+          ped = cisped;
+          ampl = cisampl;
+          chi2 = cischi2;
+        } else {
+          tau = leaktau;
+          ped = leakped;
+          ampl = leakampl;
+          chi2 = leakchi2;
+        }
+        // End of fit for CIS events
+      }
+    } else {    // Physics and laser events
+      if (!fixedTime) {
+
+        // restore initial parameters for pulse shape functions - to be used in 3-par fit
+        m_fnpar[0] = 0.0;
+        m_fnpar[1] = 0.0;
+        m_fnpar[2] = 1.0;
+
+        sy = 0.0;
+        sg = 0.0;
+        sgp = 0.0;
+        syg = 0.0;
+        sygp = 0.0;
+        sgg = 0.0;
+        sggp = 0.0;
+        sgpgp = 0.0;
+        serr = 0.0;
+
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+          // Use the respective function values
+          gval = scaledpulse(xvec[isamp], tpulse, ypulse);
+          gpval = deriv(xvec[isamp], tdpulse, dpulse);
+
+          err2 = eyvec[isamp] * eyvec[isamp];
+          sy += yvec0[isamp] / err2;
+          sg += gval / err2;
+          sgp += gpval / err2;
+          syg += yvec0[isamp] * gval / err2;
+          sygp += yvec0[isamp] * gpval / err2;
+          sgg += gval * gval / err2;
+          sggp += gval * gpval / err2;
+          sgpgp += gpval * gpval / err2;
+          serr += 1.0 / err2;
+
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                           << " gval=" << gval
+                           << " sg=" << sg
+                           << " gpval=" << gpval
+                           << " sgp=" << sgp );
+        }
+
+        dgg = sgg - sg * sg / serr;
+        dggp = sggp - sg * sgp / serr;
+        dgpgp = sgpgp - sgp * sgp / serr;
+        dyg = syg - sy * sg / serr;
+        dygp = sygp - sy * sgp / serr;
+        dg = dgg * dgpgp - dggp * dggp;
+
+        // Fit for time, pedestal, and amplitude
+        if (fabs(dg) > EPS_DG) {
+          // Amplitude           : ampl 
+          ampl = (dyg * dgpgp - dygp * dggp) / dg;
+          // and Amplitude * time: atau
+          atau = (dyg * dggp - dygp * dgg) / dg;
+          // Pedestal
+          ped = (sy - ((dyg * dgpgp - dygp * dggp) * sg + (dyg * dggp - dygp * dgg) * sgp) / dg)
+              / serr;
+
+          if (fabs(ampl) > EPS_DG) {
+            // Time
+            tau = atau / ampl;
+            if (tau > m_max_tau)
+              tau = m_max_tau;
+            else if (tau < m_min_tau) tau = m_min_tau;
+          } else {
+            tau = 0.0;
+          }
+        } else {
+          ampl = 0.0;
+          atau = 0.0;
+          tau = 0.0;
+          ped = sy / serr;
+        }
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " ped=" << ped << endmsg;
+          msg(MSG::VERBOSE) << " sy=" << sy
+                            << " sg=" << sg
+                            << " sgp=" << sgp << endmsg;
+
+          msg(MSG::VERBOSE) << " syg=" << syg
+                            << " sygp=" << sygp
+                            << " sgg=" << sgg << endmsg;
+
+          msg(MSG::VERBOSE) << " sggp=" << sggp
+                            << " sgpgp=" << sgpgp << endmsg;
+
+          msg(MSG::VERBOSE) << " ampl = (dyg*dgpgp - dygp*dggp)= " << ampl << endmsg;
+          msg(MSG::VERBOSE) << " dyg=" << dyg
+                            << " dgpgp=" << dgpgp
+                            << " dyg*dgpgp=" << (dyg * dgpgp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dygp=" << dygp
+                            << " dggp=" << dggp
+                            << " dygp*dggp=" << (dygp * dggp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dyg=" << dyg
+                            << " dggp=" << dggp
+                            << " dyg*dggp=" << (dyg * dggp) << endmsg;
+
+          msg(MSG::VERBOSE) << " dygp=" << dygp
+                            << " dgg=" << dgg
+                            << " dygp*dgg=" << (dygp * dgg) << endmsg;
+
+          msg(MSG::VERBOSE) << " dg=" << dg
+                            << " atau=" << atau
+                            << " tau=" << tau << endmsg;
+        }
+
+        m_fnpar[0] = tau;
+        m_fnpar[1] = ped;
+        m_fnpar[2] = ampl;
+
+        chi2 = 0;
+        nfit_real = 0;
+        // Calculate chi2 for physics and laser events
+        for (int isamp = 0;
+            (isamp < nfit) && (DTIME * (isamp - m_ipeak0) - m_t0fit < m_MaxTimeFromPeak); ++isamp) {
+          ++nfit_real;
+          dc = yvec0[isamp] - scaledpulse(xvec[isamp], tpulse, ypulse);
+          chi2 = chi2 + (dc * dc) / (eyvec[isamp] * eyvec[isamp]);
+          ATH_MSG_VERBOSE ( " isamp=" << isamp
+                           << " yvec0[" << isamp << "]=" << yvec0[isamp]
+                           << " eyvec[" << isamp << "]=" << eyvec[isamp]
+                           << " dc=" << dc
+                           << " chi2=" << chi2 );
+        }
+        chi2 = chi2 / (nfit_real - 3.0);
+        ATH_MSG_VERBOSE ( " chi2/(nfit_real-3.0)=" << chi2
+                         << " nfit_real=" << nfit_real );
+      } // end if fixedTime
+    } // end of physics and laser specific part
+
+    if (msgLvl(MSG::VERBOSE))
+      msg(MSG::VERBOSE) << " t0fit: " << m_t0fit << ((tau < 0.0) ? " - " : " + ") << fabs(tau);
+    // Iteration with parameter for time 
+    m_t0fit += tau;
+    ATH_MSG_VERBOSE ( " = " << m_t0fit );
+
+    // Check if total time does not exceed the limits:
+    if (m_t0fit > m_max_time) {
+      m_t0fit = m_max_time;
+      chi2 = MAX_CHI2;
+    } else if (m_t0fit < m_min_time) {
+      m_t0fit = m_min_time;
+      chi2 = MAX_CHI2;
+    }
+
+    if (chi2 < MAX_CHI2) {
+      p_time = m_t0fit;
+      p_pedestal = ped;
+      p_amplitude = ampl;
+      p_chi2 = chi2;
+    } // otherwise using the previous iteration
+    
+  } // end if to use extra iteration
+  
+  ATH_MSG_VERBOSE ( "Result:"
+                   << " Time=" << p_time
+                   << " Ped=" << p_pedestal
+                   << " Amplitude=" << p_amplitude
+                   << " Chi2=" << p_chi2 );
+}
+
+
+/**
+ * pulse interpolation
+ */
+double TileRawChannelBuilderFitFilterCool::pulse(double x, const std::vector<double> * xvec
+    , const std::vector<double> * yvec, bool zeroOutside) const {
+
+  int size1 = xvec->size() - 1;
+  if (size1 < 1) return 0.0;
+
+  const double delta = 1.e-6;
+  
+  double xpmin = xvec->at(0);
+  double xpmax = xvec->at(size1);
+
+  double xp = (x - m_ipeak0) * DTIME - m_t0fit - m_fnpar[0];
+
+  double val = 0.0;
+  double tdiv = (xpmax - xpmin) / size1;
+
+  if (xp < xpmin + delta) {
+    if (zeroOutside && xp < xpmin - delta)
+      val = 0.0;
+    else
+      val = yvec->at(0);
+#ifdef EXTRAPOLATE_TO_ZERO
+    if (xp < xpmin - delta && val != 0.0) {
+      double newval = val + ((yvec->at(1) - val) / tdiv) * (xp - xpmin);
+      if (val * newval < 0.0) {
+        val = 0.0;
+      } else if (fabs(newval) < fabs(val)) {
+        val = newval;
+      }
+    }
+#endif
+  } else if (xp > xpmax - delta) {
+    if (zeroOutside && xp > xpmax + delta)
+      val = 0.0;
+    else
+      val = yvec->at(size1);
+#ifdef EXTRAPOLATE_TO_ZERO
+    if (xp > xpmax + delta && val != 0.0) {
+      double newval = val + ((yvec->at(size1 - 1) - val) / tdiv) * (xp - xpmax);
+      if (val * newval < 0.0) {
+        val = 0.0;
+      } else if (fabs(newval) < fabs(val)) {
+        val = newval;
+      }
+    }
+#endif
+  } else {
+    int j = (int) ((xp - xpmin) / tdiv);
+    val = yvec->at(j) + ((yvec->at(j + 1) - yvec->at(j)) / tdiv) * (xp - xvec->at(j));
+  }
+  
+  return val;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderFlatFilter.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderFlatFilter.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..df3f3b097063b85fa692df82e1cc11773cdd1993
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderFlatFilter.cxx
@@ -0,0 +1,580 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// small hack to enable datapool usage
+#define private public
+#define protected public
+#include "TileEvent/TileRawChannel.h"
+#undef private
+#undef protected
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+
+// Atlas includes
+#include "DataModel/DataPool.h"
+#include "AthenaKernel/errorcheck.h"
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilderFlatFilter.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileDigitsContainer.h"
+#include "TileEvent/TileDigits.h"
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileIdentifier/TileTrigType.h"
+#include "TileConditions/TileInfo.h"
+
+// lang include
+#include <algorithm>
+#include <cmath>
+
+static const InterfaceID IID_ITileRawChannelBuilderFlatFilter
+       ("TileRawChannelBuilderFlatFilter", 1, 0);
+
+const InterfaceID& TileRawChannelBuilderFlatFilter::interfaceID( ) { 
+  return IID_ITileRawChannelBuilderFlatFilter;
+  //return TileRawChannelBuilder::interfaceID();
+}
+
+#define TILE_FLATFILTERBUILDERVERBOSE false
+
+/**
+ * Constructor
+ */
+TileRawChannelBuilderFlatFilter::TileRawChannelBuilderFlatFilter(const std::string& type,
+    const std::string& name, const IInterface *parent)
+    : TileRawChannelBuilder(type, name, parent)
+{
+  //declare interfaces
+  declareInterface< TileRawChannelBuilder >( this );
+  declareInterface< TileRawChannelBuilderFlatFilter >(this);
+    
+  m_TileRawChannelContainerID = "TileRawChannelFlat";
+
+  //declare properties
+  declareProperty("PedStart",m_pedStart = 0);
+  declareProperty("PedLength",m_pedLength = 1);
+  declareProperty("PedOffset",m_pedOffset = 0);
+  declareProperty("SignalStart",m_signalStart = 1);
+  declareProperty("SignalLength",m_signalLength = 8);
+  declareProperty("FilterLength",m_filterLength = 5);
+  declareProperty("FrameLength",m_frameLength = 9);
+  declareProperty("DeltaCutLo",m_deltaCut[0] = 4.5);
+  declareProperty("DeltaCutHi",m_deltaCut[1] = 8.5);
+  declareProperty("RMSCutLo",m_rmsCut[0] = 1.0);
+  declareProperty("RMSCutHi",m_rmsCut[1] = 2.5);
+}
+
+/**
+ * Destructor
+ */
+TileRawChannelBuilderFlatFilter::~TileRawChannelBuilderFlatFilter(){ 
+}
+
+/**
+ * Initializer
+ */
+StatusCode TileRawChannelBuilderFlatFilter::initialize() {
+
+  ATH_MSG_INFO( "TileRawChannelBuilderFlatFilter::initialize()" );
+
+  m_rChType = TileFragHash::FlatFilter;
+
+  // init in superclass
+  CHECK( TileRawChannelBuilder::initialize() );
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TileRawChannelBuilderFlatFilter::finalize() {
+
+  ATH_MSG_DEBUG( "Finalizing" );
+  return StatusCode::SUCCESS;
+}
+
+TileRawChannel* TileRawChannelBuilderFlatFilter::rawChannel(const TileDigits* digits) {
+
+  ++m_chCounter;
+
+  const HWIdentifier adcId = digits->adc_HWID();
+  int gain = m_tileHWID->adc(adcId);
+
+  ATH_MSG_VERBOSE( "Running FlatFilter for TileRawChannel with HWID "
+                  << m_tileHWID->to_string(adcId) );
+
+  // tmp variables for filtering
+  double amplitude = 0.0;
+  double time = 0.0;
+
+  // use flat filter
+  flatFilter(digits->samples(), gain, amplitude, time);
+
+  // flat filter calib
+  if (m_calibrateEnergy) {
+    amplitude = m_tileInfo->CisCalib(adcId, amplitude);
+  }
+
+  ATH_MSG_VERBOSE( "Creating RawChannel a=" << amplitude << " t=" << time );
+
+  // return new TileRawChannel
+  //  TileRawChannel *rawCh = new TileRawChannel(adcId,amplitude,time,0.0);
+
+  static DataPool<TileRawChannel> tileRchPool(m_dataPoollSize);
+  TileRawChannel *rawCh = tileRchPool.nextElementPtr();
+  rawCh->m_adc_hwid = adcId;
+  rawCh->m_amplitude.resize(1);
+  rawCh->m_amplitude[0] = amplitude;
+  rawCh->m_time.resize(1);
+  rawCh->m_time[0] = time;
+  rawCh->m_quality.resize(1);
+  rawCh->m_quality[0] = 0.0;
+  rawCh->m_pedestal = 0.0; // default value in TileRawChannel constructor
+
+  if (m_correctTime) {
+    rawCh->insertTime(m_tileInfo->TimeCalib(adcId, time));
+    ATH_MSG_VERBOSE( "Correcting time, new time=" << rawCh->time() );
+  }
+
+  if (TileID::HIGHGAIN == gain) {
+    ++m_nChH;
+    m_RChSumH += amplitude;
+  } else {
+    ++m_nChL;
+    m_RChSumL += amplitude;
+  }
+
+  return rawCh;
+}
+
+bool TileRawChannelBuilderFlatFilter::isSignalInFrame(const std::vector<float> &digits,
+			 double deltaCut, double rmsCut, int &max, int &min, double &mean, double &rms) const {
+
+  int t, val;
+  // First compute the values that will be used to decide
+  min  = 65536;
+  max  = 0;
+  mean = 0.0;
+  rms  = 0.0;
+  
+  if(TILE_FLATFILTERBUILDERVERBOSE)
+    ATH_MSG_VERBOSE( "Checking for signal in frame" );
+  
+  for (t = 0;  t < m_frameLength;  ++t) {
+    val = (int)digits.at(t);
+    mean += val;
+    rms  += val*val;
+    if ( val < min ) min = val;
+    if ( val > max ) max = val;
+  }
+
+  mean /= m_frameLength;
+  rms  /= m_frameLength;
+  rms   = sqrt(rms - mean*mean);
+  if ( ( (max-min) >= deltaCut ) &&
+       ( rms    >=  rmsCut  ) ) {
+    return true;
+  } 
+  else return false;
+}
+
+double TileRawChannelBuilderFlatFilter::getPedestal(const std::vector<float> &digits
+                                                    , int pedStart, int pedLgt) const {
+  int i, t, sum = 0;
+  double ped;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE) {
+    ATH_MSG_VERBOSE( "Getting pedestal " << pedStart
+                    << "->" << (pedStart + pedLgt) );
+  }
+    
+  if (pedLgt == 0)
+    return 0.0;
+  else if (pedLgt < 0) {
+    // calculate sum of ped_length samples starting from ped_start
+    // and go to the left (negative direction)
+    // if number of samples is not enough, start from the end of the frame */
+    pedLgt *= -1;
+    t = pedStart;
+    for (i = 0; i < pedLgt; ++i) {
+      if (t < 0) t += m_frameLength;
+      sum += (int) digits.at(t--);
+    }
+  }
+  else {
+    // calculate sum of ped_length samples starting from ped_start
+    // and go to the right
+    // if number of samples is not enough, start from the begining of the frame */
+    t = pedStart;
+    for (i = 0; i < pedLgt; ++i) {
+      if (t == m_frameLength) t -= m_frameLength;
+      sum += (int) digits.at(t++);
+    }
+  }
+    
+  // calculate mean ped
+  ped = (double) sum / (double) pedLgt;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE) ATH_MSG_VERBOSE( "Got pedestal " << ped );
+
+  return (ped);
+}
+
+
+int TileRawChannelBuilderFlatFilter::getMaxAdder(const std::vector<float> &digits,
+			                                            int filterLength,int signalStart, int signalLength
+			                                            , int &tMax, int &tMaxFrame, int &adderFrame) const {
+  int i, t, minW, maxW, adder, adderWindow;
+  int tm=0, maxAdder=-1;
+  
+  if(TILE_FLATFILTERBUILDERVERBOSE) {
+    ATH_MSG_VERBOSE( "Getting adder flgt " << filterLength
+                    << " Signal " << signalStart
+                    << "->" << (signalStart + signalLength) );
+  }
+
+  // left and right limits for signal window
+  minW = signalStart;
+  maxW = signalStart + signalLength - filterLength + 1;
+
+  // max adder inside signal window
+  for (t = minW; t < maxW; ++t) {
+    adder = 0;
+    // sum of all samples in window
+    for (i = t; i < t+filterLength; ++i) adder += (int)digits.at(i);
+    // store maximum
+    if ( adder > maxAdder ) {
+      tm = t;
+      maxAdder = adder;
+    }
+  }
+
+  // return parameters for max adder in signal window
+  tMax = tm;
+
+  adderWindow = maxAdder;
+
+  // right limit for whole frame
+  maxW = m_frameLength - filterLength + 1;
+  
+  // max adder after signal window
+  for ( ; t < maxW; ++t) {
+    adder = 0;
+    for (i = t; i < t+filterLength; ++i) adder += (int)digits.at(i);
+    
+    if ( adder > maxAdder ) {
+      tm = t;
+      maxAdder = adder;
+    }
+  }
+
+  // max adder before signal window
+  for (t = 0; t < minW; ++t) {
+    adder = 0;
+    for (i = t; i < t+filterLength; ++i)
+      adder += (int)digits.at(i);
+
+    if ( adder > maxAdder ) {
+      tm = t;
+      maxAdder = adder;
+    }
+  }
+
+  // return parameters for max adder in whole frame
+  tMaxFrame  = tm;
+  adderFrame = maxAdder;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE) {
+    ATH_MSG_VERBOSE( "Got max adder, tMax=" << tMax
+                    << " tMaxFrame=" << tMaxFrame
+                    << " maxAdder=" << maxAdder
+                    << " adderWindow=" << adderWindow );
+  }
+  
+  return adderWindow;
+}
+
+int TileRawChannelBuilderFlatFilter::getMaxSample(const std::vector<float> &digits
+    , int signalStart, int signalLength, int &tMax, int &tMaxFrame, int &sampleFrame) const {
+
+  int t, minW, maxW, sampleWindow;
+  int tm = 0, maxSample = -1;
+    
+  if (TILE_FLATFILTERBUILDERVERBOSE) {
+    ATH_MSG_VERBOSE( "Getting max sample, Signal " << signalStart
+                    << "->" << (signalStart + signalLength) );
+  }
+
+  // left and right limits for signal window
+  minW = signalStart;
+  maxW = signalStart + signalLength;
+
+  // max sample inside signal window take leftmost maximum
+  for (t = minW; t < maxW; ++t) {
+    if ((int) digits.at(t) > maxSample) {
+      tm = t;
+      maxSample = (int) digits.at(t);
+    }
+  }
+
+  // let's check if we have more than one sample with the same amplitude
+  // in such a case take central sample and also
+  // avoid maximum at the boundary (tm=minW)
+  for (t = tm + 1; t < maxW; ++t)
+    if ((int) digits.at(t) != maxSample) break;
+
+  t -= tm; // number of identical samples
+  if (t == 2 && tm == minW) ++tm; // move from the boundary
+  else tm += (t - 1) / 2; // center of the window
+
+  // return parameters for max sample in signal window
+  tMax = tm;
+  sampleWindow = maxSample;
+
+  // right limit for whole frame
+  maxW = m_frameLength;
+
+  // max sample after signal window
+  for ( ; t < maxW; ++t) {
+    if ( (int)digits.at(t) > maxSample ) {
+      tm = t;
+      maxSample = (int)digits.at(t);
+    }
+  }
+
+  // max sample before signal window
+  for (t = 0; t < minW; ++t) {
+    if ( (int)digits.at(t) > maxSample ) {
+      tm = t;
+      maxSample = (int)digits.at(t);
+    }
+  }
+
+  // return parameters for max adder in whole frame
+  tMaxFrame = tm;
+  sampleFrame = maxSample;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE) {
+    ATH_MSG_VERBOSE( "Got max sample " << sampleFrame
+                    << " tMaxFrame=" << tMaxFrame
+                    << " tMax=" << tMax );
+  }
+
+  return sampleWindow;
+}
+
+double TileRawChannelBuilderFlatFilter::calculatePeak(const std::vector<float> &digits
+    , int peakPos, double ped, double &position) const {
+
+  int y_1, y0, y1;
+  double peak, tmax, A, B, C;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE) {
+    ATH_MSG_VERBOSE( "Calculating peak, Pos " << peakPos
+                    << " Pedestal " << ped );
+  }
+
+  if ((peakPos == 0) || (peakPos == m_frameLength - 1)) {
+    // peak at the boundary
+    tmax = peakPos;
+    peak = digits.at(peakPos);
+  } else {
+    // let's say that "peak_pos" is X=0 point
+    y_1 = (int) digits.at(peakPos - 1); // ordinate at X=-1
+    y0 = (int) digits.at(peakPos); // ordinate at X=0
+    y1 = (int) digits.at(peakPos + 1); // ordinate at X=1
+
+    // coefficients A,B,C of Y(x) = A*x^2 + B*x + C
+    C = y0;
+    B = (y1 - y_1) / 2.;
+    A = y1 - B - C;
+
+    if (A < 0.0) {
+      // amplitude at peak_pos is local maximim
+      tmax = peakPos - B / A / 2.;
+      peak = C - B * B / A / 4.;
+    } 
+    else if ( y_1 == y0 && y0 == y1 ) {
+      // flat distribution, no peak at all
+      tmax = peakPos;
+      peak = y0;
+      } 
+    else {
+      // there is no local maximim 
+      // phase has negative sign to indicate that,
+      // peak is amplitude at peak_pos
+      tmax = -peakPos;
+      peak = y0;
+    }
+  }
+
+  // subtract pedestal
+  peak -= ped;
+  // convert to nanoseconds
+  position = tmax * 25.;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE)
+    ATH_MSG_VERBOSE( "Found Peak " << peak << " at " << position );
+
+  return (peak);
+}
+
+double TileRawChannelBuilderFlatFilter::calculateFlatFilter(const std::vector<float> &digits,
+    int filterStart, int filterLength, double ped, double &position) const {
+
+  int t, sum = 0;
+  double energy;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE) {
+    ATH_MSG_VERBOSE( "Calc flat filter Filter: " << filterStart
+                    << "->" << (filterStart + filterLength)
+                    << " pedestal=" << ped );
+  }
+  
+  // calculate sum of filter_length samples
+  for (t = filterStart;  t < filterStart+filterLength;  ++t)
+      sum += (int)digits.at(t);
+
+  // subtract pedestal
+  energy = (double)sum - ped * filterLength;
+
+  // calculate center of the filter and convert to nanoseconds
+  position = (filterStart + (filterLength-1)/2.) * 25.;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE) {
+    ATH_MSG_VERBOSE( "Flat amplitude=" << energy
+                    << " position=" << position );
+  }
+
+  return(energy);
+}
+
+double TileRawChannelBuilderFlatFilter::getTime(const std::vector<float> &digits
+    , int signalStart, int signalLength, double ped) const {
+
+  int t, tmax = signalStart + signalLength;
+  double position, val, sig = 0.0, sum = 0.0;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE) {
+    ATH_MSG_VERBOSE( "Getting time Signal: " << signalStart
+                    << "->" << (signalStart + signalLength)
+                    << " pedestal=" << ped );
+  }
+
+  // calculate sum of samples and sum of samples weighted with time
+  for (t = signalStart; t < tmax; ++t) {
+    val = (int) (digits.at(t) - ped);
+    sig += val;
+    sum += val * t;
+  }
+
+  if (sig > 0.0 && sum > 0) {
+    position = sum / sig;
+    // convert to nanoseconds
+    if (position < tmax) position *= 25.0;
+    else position = 0.0;
+
+  } else {
+    position = 0.0;
+  }
+
+  if (TILE_FLATFILTERBUILDERVERBOSE)
+    ATH_MSG_VERBOSE( "Got time " << position );
+
+  return (position);
+}
+
+void TileRawChannelBuilderFlatFilter::flatFilter(const std::vector<float>& samples
+    , const int gain, double& amplitude, double& time) const {
+
+  int frameMax = 0;
+  int frameMin = 0;
+  double frameMean = 0.0;
+  double frameRMS = 0.0;
+  bool signal = false;
+  double pedestal = 0.0;
+
+  //int adderMax = 0; // maximum of adder
+  int tAdder = 0; // time for adder
+  int tAdderFrame = 0;
+  int adderMaxFrame = 0;
+
+  //int sampleMax = 0;
+  int tSample = 0;
+  int tSampleFrame = 0;
+  int sampleMaxFrame = 0;
+
+  //double peakAmplitude;
+  //double peakEnergy;
+  //double peakTime;
+  //double flatEnergy;
+
+  double flatFilter; // non calibrated energy from flat filter
+  double flatTime;
+
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "Digits." << samples.size() << " {";
+    for (unsigned i = 0; i < samples.size(); i++) {
+      msg(MSG::VERBOSE) << samples[i] << " ";
+    }
+    msg(MSG::VERBOSE) << "}" << endmsg;
+  }
+
+  // check if signal in frame
+  signal = isSignalInFrame(samples, m_deltaCut[gain % 2], m_rmsCut[gain % 2]
+                           , frameMax, frameMin, frameMean, frameRMS);
+
+  if (signal && (m_pedOffset < 0)) {
+    // take pedestal before signal
+    // and if number of samples before signal is not enough -
+    // use samples at the end of the frame
+    pedestal = getPedestal(samples, tAdder + m_pedOffset, -m_pedLength);
+  } else {
+    // fixed position of pedestal window for channel without signal
+    pedestal = getPedestal(samples, m_pedStart, m_pedLength);
+  }
+
+  //------------------ Flat Filter ---------------
+  /*adderMax =*/getMaxAdder(samples, m_filterLength, m_signalStart, m_signalLength
+                            , tAdder , tAdderFrame, adderMaxFrame);
+
+  // look for max. sample inside maxAdder
+  /*sampleMax =*/getMaxSample(samples, tAdder, m_filterLength, tSample
+                              , tSampleFrame, sampleMaxFrame);
+
+  // position of the peak is always in the signal window even if
+  // real maximum is outside signal window
+  //peakAmplitude = calculatePeak (samples, tSample, pedestal, peakTime);
+  //peakEnergy = calibrateCSL(calibratePeak(peakAmplitude, adcId), adcId);
+
+  // flat filter method
+  // position of the filter is always in the signal window even if
+  // real maximum is outside signal window
+  flatFilter = calculateFlatFilter(samples, tAdder, m_filterLength, pedestal, flatTime);
+
+  //ZZZ: No calibration at all!
+  //flatEnergy = calibrateCSL(calibrateFlatFilter(flatFilter,adcId),adcId);
+
+  // calculate time of the signal as weighted sum of all samples in signal window
+  time = getTime(samples, m_signalStart, m_signalLength, pedestal);
+  if (time <= 0.0) time = -flatTime;
+
+  amplitude = flatFilter;
+}
+
+void TileRawChannelBuilderFlatFilter::flatFilter(const std::vector<uint32_t> &digits,
+    const int gain, double& amplitude, double& time) const {
+
+  std::vector<float> dVec;
+
+  if (TILE_FLATFILTERBUILDERVERBOSE)
+    ATH_MSG_VERBOSE( "TileRawChannelBuilderFlatFilter - Invoked with vector<int>..");
+
+  dVec.reserve(digits.size());
+  for (uint32_t i = 0; i < digits.size(); i++) {
+    dVec.push_back((float) digits[i]);
+  }
+  return flatFilter(dVec, gain, amplitude, time);
+}
+
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderMF.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderMF.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..fe7c5e13485c7be86b845be88c5a55c4c96e8687
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderMF.cxx
@@ -0,0 +1,543 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// small hack to enable datapool usage
+#define private public
+#define protected public
+#include "TileEvent/TileRawChannel.h"
+#undef private
+#undef protected
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+
+// Atlas includes
+#include "DataModel/DataPool.h"
+#include "AthenaKernel/errorcheck.h"
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilderMF.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileDigits.h"
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileConditions/TileInfo.h"
+#include "TileRecUtils/TileFilterManager.h"
+#include "TileRecUtils/TileFilterTester.h"
+#include "TileRecUtils/TileRawChannelBuilderOpt2Filter.h"
+#include "TileConditions/TileOptFilterWeights.h"
+#include "TileConditions/TilePulseShapes.h"
+#include "CLHEP/Matrix/Matrix.h"
+
+// lang include
+#include <algorithm>
+#include <cmath>
+
+// C++ STL includes
+#include <vector>
+
+// for matrix treatment
+#include "CLHEP/Matrix/Matrix.h"
+
+
+/**
+ * Standard constructor
+ */
+TileRawChannelBuilderMF::TileRawChannelBuilderMF(const std::string& type
+    , const std::string& name, const IInterface *parent)
+    : TileRawChannelBuilder(type, name, parent)
+    , m_tileToolTiming("TileCondToolTiming")
+    , m_tileCondToolOfcCool("TileCondToolOfcCool")
+    , m_tileToolNoiseSample("TileCondToolNoiseSample")
+    , m_NSamp(0)
+    , m_t0Samp(0)
+    , m_maxTime(0.0)
+    , m_minTime(0.0)
+{
+  //declare interfaces
+  declareInterface< TileRawChannelBuilder >( this );
+  declareInterface< TileRawChannelBuilderMF >(this);
+    
+  m_TileRawChannelContainerID = "TileRawChannelMF";
+
+  //declare properties
+  declareProperty("TileCondToolTiming", m_tileToolTiming);
+  declareProperty("TileCondToolOfcCool", m_tileCondToolOfcCool  ,"TileCondToolOfcCool");
+  declareProperty("TileCondToolNoiseSample", m_tileToolNoiseSample);
+  declareProperty("AmplitudeCorrection", m_correctAmplitude = false);
+  declareProperty("PedestalMode", m_pedestalMode = 1);
+  declareProperty("DefaultPedestal", m_defPedestal = 0.0);
+  declareProperty("MF", m_MF = 0);
+}
+
+
+/**
+ * Destructor
+ */
+TileRawChannelBuilderMF::~TileRawChannelBuilderMF() {
+}
+
+
+/**
+ * Initialize
+ */
+StatusCode TileRawChannelBuilderMF::initialize() {
+
+  ATH_MSG_INFO( "TileRawChannelBuilderMF::initialize()" );
+
+  m_rChType = TileFragHash::MF;
+
+  // Initialize arrays for ped estimation
+  memset(m_chPedCounter, 0, sizeof(m_chPedCounter));
+  memset(m_chPed, 0, sizeof(m_chPed));
+
+  // init in superclass
+  CHECK( TileRawChannelBuilder::initialize() );
+  
+  // bits 12-15 - various options
+  if (m_correctAmplitude) m_bsflags |= 0x2000;
+
+  m_NSamp = m_tileInfo->NdigitSamples();
+  m_t0Samp = m_tileInfo->ItrigSample();
+  m_maxTime = 25 * (m_NSamp - m_t0Samp - 1);
+  m_minTime = -25 * m_t0Samp;
+
+  //=== get TileCondToolOfcCool
+  CHECK( m_tileCondToolOfcCool.retrieve() );
+
+  //=== get TileToolTiming
+  CHECK( m_tileToolTiming.retrieve() );
+
+   //=== get TileCondToolNoiseSample
+  CHECK( m_tileToolNoiseSample.retrieve() );
+
+
+  ATH_MSG_DEBUG( "TileRawChannelBuilderMF::initialize() completed successfully" );
+
+  return StatusCode::SUCCESS;
+}
+
+/**
+ *  Finalize
+ */
+StatusCode TileRawChannelBuilderMF::finalize() {
+
+  ATH_MSG_DEBUG( "Finalizing" );
+  
+  return StatusCode::SUCCESS;
+}
+
+TileRawChannel* TileRawChannelBuilderMF::rawChannel(const TileDigits* tiledigits) {
+
+  ++m_chCounter;
+  int i = 0;
+  int j = 0;
+  unsigned int k = 0;
+  double MFchi2 = 0.;
+
+  const HWIdentifier adcId = tiledigits->adc_HWID();
+  int gain = m_tileHWID->adc(adcId);
+  int ros = m_tileHWID->ros(adcId);
+  int drawer = m_tileHWID->drawer(adcId);
+  int channel = m_tileHWID->channel(adcId);
+  
+  ATH_MSG_VERBOSE ( "Running Matched Filter for TileRawChannel with HWID "
+                   << m_tileHWID->to_string(adcId) );
+
+  /* get pedestal value assumed for reconstruction */
+  //double digSigma = m_tileInfo->DigitsResolution(gain);
+  // new way to get channel-dependent sigma:
+  // but we lost difference between sigma used in digitization and 
+  // sigma assumed in reconstruction - it's the same sigma now
+  //double digSigma = m_tileInfo->DigitsPedSigma(adcId);
+  /* Get vector of time-slice amplitudes. */
+  digits = tiledigits->samples();
+  double amp_ch = 0;      // Fitted amplitude of RC (to be returned from Filtering code).
+  double cof[7] = { 0., 0., 0., 0., 0., 0., 0. };
+  float ped_ch = 0;      // Ped retrieved from m_tileToolNoiseSample.
+  double chisq_ch = 0.; // Chisq resulting from Filtering.
+  double t_ch = 0.;     // Fitted time to be supplied by Filtering code
+  double amp_norm=0.;     // Normalization factor for MF method
+  double amp_mf = 0.; 
+  float ped_aux = 0;
+  //float rms_aux = 0;
+  float phase = 0;
+  int err = 0;	//error for matrix handling
+
+  float mindig, maxdig;
+
+  // to treat issues discussed...
+  if (Are3FF(mindig, maxdig)) {
+    
+    ped_ch = 0.;
+    if (mindig > 2046.99)
+      chisq_ch = 9999.;
+    else
+      chisq_ch = 0.;
+
+  } else {
+
+    unsigned int drawerIdx = TileCalibUtils::getDrawerIdx(ros, drawer);
+    phase = (float) -m_tileToolTiming->getSignalPhase(drawerIdx, channel, gain);
+
+    switch(m_pedestalMode) {
+
+      case -1:
+        // use pedestal from conditions DB
+        ped_ch = m_tileToolNoiseSample->getPed(drawerIdx, channel, gain);
+        break;
+
+      case 0:
+        // use fixed pedestal from jobOptions
+        ped_ch = m_defPedestal;
+        break;
+
+      default:
+        // use ped estimated from data based on the conditions value
+        ped_aux = m_tileToolNoiseSample->getPed(drawerIdx, channel, gain);
+        /*rms_aux = m_tileToolNoiseSample->getHfn(drawerIdx, channel, gain); */
+
+        if (m_chPedCounter[ros][drawer][channel][gain] < 200) {
+
+          if ((digits[0] < (ped_aux + 10)) && (digits[0] > (ped_aux - 10))) {
+
+            ++m_chPedCounter[ros][drawer][channel][gain];
+            m_chPed[ros][drawer][channel][gain] += digits[0];
+
+          }
+        }
+
+        if (m_chPedCounter[ros][drawer][channel][gain] > 0) {
+          ped_ch = m_chPed[ros][drawer][channel][gain] / m_chPedCounter[ros][drawer][channel][gain];
+        } else {
+          ped_ch = ped_aux;
+        }
+        break;
+
+    }
+
+    // apply the Matched Filter
+    const TileOfcWeightsStruct* m_weights;
+    m_weights = m_tileCondToolOfcCool->getOfcWeights(drawerIdx, channel, gain, phase, true);
+
+    double g[9];
+    //double dg[9];
+    //double a[9];
+    double b[9];
+    double t_aux = 0;	//variable auxiliar to compute the MF time
+
+    for (k = 0; k < digits.size(); ++k) {
+      //a[k] = m_weights->w_a[k];
+      b[k] = m_weights->w_b[k];
+      g[k] = m_weights->g[k];
+      amp_mf += g[k]*(digits[k]-ped_ch);  // matched filter
+      amp_norm += g[k]*g[k];  // matched filter calibration for amp estimation
+      t_aux += b[k] * digits[k];
+      //dg[k] = m_weights->dg[k];
+    }
+
+    amp_mf = amp_mf/amp_norm;	// pure MF (for muon receiver board simulation)
+
+    // apply deconvolution for pileup handling
+    int n = 7;
+    HepMatrix A(n, n, 0);
+
+    int t = 4;
+    for (int row = 0; row < n; row++) {
+      t -= 1;
+      for (int col = 0; col < n; col++) {
+        if (((col + t) > 6) || ((col + t) < 0)) {
+          A[row][col] = 0.0;
+        } else {
+          A[row][col] = g[col + t];
+        }
+
+      }
+    }
+
+    HepMatrix B(n, n, 0);
+    err = 0;
+    B = A;
+    B.invert(err);
+
+    double xDecon[7] = { 0, 0, 0, 0, 0, 0, 0 };
+    for (j = 0; j < n; ++j) {
+      for (i = 0; i < n; ++i) {
+        xDecon[j] += (digits[i] - ped_ch) * B[i][j];
+      }
+    }
+
+    // test whether pileup is present
+    int thr = 0;
+    if (gain == 0) {
+      thr = 4;
+    } else {
+      thr = 9;
+    }
+
+    int pileupDet[7] = { 0, 0, 0, 1, 0, 0, 0 };
+    int constraint = 1;
+    for (i = 0; i < n; ++i) {
+      if (i == 3) continue;
+      if (xDecon[i] > thr) {
+        pileupDet[i] = 1;
+        constraint += 1;
+      }
+
+    }
+
+    // apply GOF
+    HepMatrix H(constraint, n, 0);
+
+    t = 4;
+    int rowAux = 0;
+    //int ind4 = 0;
+    for (int row = 0; row < n; row++) {
+      t -= 1;
+      if (pileupDet[row] == 0) continue;
+      //if (row == 3) ind4 = rowAux;
+      for (int col = 0; col < n; col++) {
+        if (((col + t) > 6) || ((col + t) < 0)) {
+          H[rowAux][col] = 0.0;
+        } else {
+          H[rowAux][col] = g[col + t];
+        }
+
+      }
+      rowAux += 1;
+    }
+
+    HepMatrix tH(n, constraint, 0);
+    HepMatrix resultH(constraint, n, 0);
+    HepMatrix multH(constraint, constraint, 0);
+    tH = H.T();
+    multH = H * tH;
+    err = 0;
+    multH.invert(err);
+    resultH = multH * H;
+    double signalModel[7] = { 0., 0., 0., 0., 0., 0., 0. };	// signal model built from COF amplitude estimates
+    int r = 0;
+    if (m_MF == 1){
+	cof[3] = amp_mf;
+	for (j = 0; j< n; j++){ 
+		signalModel[j] += cof[3] * A[3][j];
+	}
+    }	
+    else{
+    	for (i = 0; i < n; ++i) {
+
+      	if (pileupDet[i] == 0) continue;
+
+      	for (j = 0; j < n; j++) {
+        	cof[i] += (digits[j] - ped_ch) * resultH[r][j];
+      	}
+
+      	for (j = 0; j < n; j++) {
+        	signalModel[j] += cof[i] * A[i][j];
+      	}
+
+      	r++;
+
+    	}
+    }
+    amp_ch = cof[3];	// with COF, no need for the amp_ch variable anymore
+        
+    // ped=0 means channel disconnect for physics runs/ work only for CIS runs
+    // digits =1023, drawer off
+    if ((ped_ch == 0) || (digits[0] == 1023)) {
+      amp_ch = 0;
+      for (i = 0; i < n; ++i) {
+        cof[i] = 0;
+      }
+    }
+
+    //bool goodEne = (fabs(amp_ch) > 1.0e-04);
+    bool goodEne = (fabs(cof[3]) > 1.0e-04);
+    if (goodEne) {
+      //t_ch = t_aux/amp_ch;
+      t_ch = t_aux / cof[3];
+    } else {
+      t_ch = amp_ch = 0.0;
+      for (i = 0; i < n; ++i) {
+        cof[i] = 0.0;
+      }
+    }
+
+    // If weights for tau=0 are used, deviations are seen in the amplitude =>
+    // function to correct the amplitude
+    if (m_correctAmplitude && amp_ch > m_ampMinThresh && t_ch > m_timeMinThresh
+        && t_ch < m_timeMaxThresh) {
+      amp_ch *= correctAmp(t_ch);
+      for (i = 0; i < n; ++i) {
+        cof[i] *= correctAmp(t_ch);
+      }
+
+      ATH_MSG_VERBOSE ( "Amplitude corrected by " << correctAmp(t_ch)
+                       << " new amplitude is " << amp_ch );
+    }
+
+    if (t_ch < m_minTime) t_ch = m_minTime;
+    if (t_ch > m_maxTime) t_ch = m_maxTime;
+
+    if (m_calibrateEnergy) {
+      amp_ch = m_tileInfo->CisCalib(adcId, amp_ch);
+      for (i = 0; i < n; ++i) {
+        cof[i] = m_tileInfo->CisCalib(adcId, cof[i]);
+      }
+    }
+
+    // we know that time is zero here, put negagive chi^2 to indicate that
+    for (k = 0; k < digits.size(); ++k) {
+      double dqf = (signalModel[k] - (digits[k] - ped_ch));
+      MFchi2 += dqf * dqf;
+    }
+    chisq_ch = sqrt(MFchi2 / 7);
+
+    if (fabs(chisq_ch) > 1.0e-04 || goodEne) {
+      if (msgLvl(MSG::VERBOSE)) {
+        msg(MSG::VERBOSE) << "MFtime=" << t_ch << endmsg;
+        msg(MSG::VERBOSE) << "MFped=" << ped_ch << endmsg;
+        msg(MSG::VERBOSE) << "MFchi2=" << chisq_ch << endmsg;
+      }
+    } else {
+      if (msgLvl(MSG::VERBOSE)) {
+        msg(MSG::VERBOSE) << "MFtime=" << t_ch << endmsg;
+        msg(MSG::VERBOSE) << "MFped=" << ped_ch << endmsg;
+        msg(MSG::VERBOSE) << "MFchi2=" << chisq_ch << "   ... assuming 0.0" << endmsg;
+      }
+      chisq_ch = 0.0;
+    }
+
+  }
+
+  //  TileRawChannel *rawCh = new TileRawChannel(adcId,amp_ch,t_ch,chisq_ch);
+  static DataPool<TileRawChannel> tileRchPool(m_dataPoollSize);
+  TileRawChannel *rawCh = tileRchPool.nextElementPtr();
+  rawCh->m_adc_hwid = adcId;
+  rawCh->m_amplitude.resize(7);
+  rawCh->m_amplitude[0] = cof[3];
+  rawCh->m_amplitude[1] = cof[0];
+  rawCh->m_amplitude[2] = cof[1];
+  rawCh->m_amplitude[3] = cof[2];
+  rawCh->m_amplitude[4] = cof[4];
+  rawCh->m_amplitude[5] = cof[5];
+  rawCh->m_amplitude[6] = cof[6];
+  rawCh->m_time.resize(7);
+  rawCh->m_time[0] = t_ch;
+  rawCh->m_time[1] = -75.;
+  rawCh->m_time[2] = -50.;
+  rawCh->m_time[3] = -25.;
+  rawCh->m_time[4] = 25.;
+  rawCh->m_time[5] = 50.;
+  rawCh->m_time[6] = 75.;
+  rawCh->m_quality.resize(1);
+  rawCh->m_quality[0] = chisq_ch;
+  rawCh->m_pedestal = ped_ch; // default value in TileRawChannel constructor
+
+  ATH_MSG_VERBOSE ( "Creating RawChannel"
+                   << " a=" << amp_ch
+                   << " t=" << t_ch
+                   << " q=" << chisq_ch );
+  
+  if (m_correctTime && (t_ch != 0 && t_ch < m_maxTime && t_ch > m_minTime)) {
+    rawCh->insertTime(m_tileInfo->TimeCalib(adcId, t_ch));
+    ATH_MSG_VERBOSE ( "Correcting time, new time=" << rawCh->time() );
+
+  }
+
+  if (TileID::HIGHGAIN == gain) {
+    ++m_nChH;
+    m_RChSumH += amp_ch;
+  } else {
+    ++m_nChL;
+    m_RChSumL += amp_ch;
+  }
+
+  return rawCh;
+}
+
+bool TileRawChannelBuilderMF::Are3FF(float &dmin, float &dmax) {
+  bool allSaturated = true;
+  bool jump = false;
+
+  unsigned int nSamp = digits.size();
+  if (nSamp) {
+    dmin = dmax = digits[0];
+
+    for (unsigned int i = 1; i < nSamp; ++i) {
+      float dig = digits[i];
+      if (dig > dmax)
+        dmax = dig;
+      else if (dig < dmin) dmin = dig;
+    }
+    allSaturated = (dmin > 1022.99);
+
+    // FIXME:: set these 2 parameters from JobOptions
+    // FIXME:: move this method to base class 
+    const float epsilon = 2.1; // allow 1 count fluctuations around const value
+    const float delta = 99.9;  // consider jumps by 100 counts only
+    const float level0 = 39.9; // jump from this level to zero is bad
+    const float level1 = 99.9; // jump from this level to 1023 is bad 
+
+    if (!allSaturated && (dmax - dmin) > delta) {
+      float abovemin = dmax;
+      float belowmax = dmin;
+      unsigned int nmin = 0;
+      unsigned int nmax = 0;
+      //unsigned int pmin = nSamp;
+      unsigned int pmax = nSamp;
+      for (unsigned int i = 0; i < nSamp; ++i) {
+        float smp = digits[i];
+        if (smp - dmin < epsilon) {
+          ++nmin;
+          //pmin = i;
+        }
+        if (dmax - smp < epsilon) {
+          ++nmax;
+          pmax = i;
+        }
+        if (smp < abovemin && smp > dmin) {
+          abovemin = smp;
+        }
+        if (smp > belowmax && smp < dmax) {
+          belowmax = smp;
+        }
+      }
+
+      if (dmin < 0.01 && dmax > 1022.99) { // jump from zero to saturation
+        jump = true;
+      } else if (dmin < 0.01 && abovemin > level0 && nmin > 1) { // at least two samples at zero, others - above pedestal
+        jump = true;
+      } else if (dmax > 1022.99 && belowmax < level1 && nmax > 1) { // at least two saturated. others - close to pedestal
+        jump = true;
+      } else if (nmax + nmin == nSamp) {
+        if (nmax > 1 && nmin > 1) { // at least 2 samples at two distinct levels
+          jump = true;
+        } else if (nmax == 1) {
+          if (pmax > 0 && pmax < nSamp - 1) { // jump up in one sample, but not at the edge
+            jump = true;
+          }
+        } else if (nmin == 1) { // jump down in one sample
+          jump = true;
+        }
+      }
+    }
+
+    ATH_MSG_VERBOSE ( "  TileRawChannelBuilderMF::Are3FF()"
+                     << "  mindig= " << dmin
+                     << "  maxdig= " << dmax
+                     << "  allSat= " << allSaturated
+                     << "  jump= " << jump );
+
+    if (jump) {
+      dmin = dmax = 4095.; // this is needed to set bad quality from Opt Filter later
+    }
+
+  } else {
+    dmin = dmax = 0.0;
+  }
+
+  return (allSaturated || jump);
+}
+
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderManyAmps.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderManyAmps.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..702e9601786743335439d93551e51bb18312d80f
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderManyAmps.cxx
@@ -0,0 +1,226 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// small hack to enable datapool usage
+#define private public
+#define protected public
+#include "TileEvent/TileRawChannel.h"
+#undef private
+#undef protected
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+
+//Atlas includes
+#include "DataModel/DataPool.h"
+#include "AthenaKernel/errorcheck.h"
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilderManyAmps.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileDigits.h"
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileConditions/TileInfo.h"
+#include "TileRecUtils/TileFilterManager.h"
+#include "TileRecUtils/TileFilterTester.h"
+
+// lang include
+#include <algorithm>
+#include <cmath>
+
+// C++ STL includes
+#include <vector>
+
+
+/**
+ * Standard constructor
+ */
+TileRawChannelBuilderManyAmps::TileRawChannelBuilderManyAmps(const std::string& type,
+    const std::string& name, const IInterface *parent)
+  : TileRawChannelBuilder(type, name, parent)
+  , m_tileFilterManagerHi(0)
+  , m_tileFilterManagerLo(0)
+{
+  //declare interfaces
+  declareInterface< TileRawChannelBuilder >( this );
+  declareInterface< TileRawChannelBuilderManyAmps >(this);
+    
+  m_TileRawChannelContainerID = "TileRawChannelManyAmps";
+
+  //declare properties
+  declareProperty("FilterMode",  m_digitFilterMode = 2);  // can be 2 or 3
+  declareProperty("FilterLevel", m_digitFilterLevel = 5); // number of parameters for fit (3-9 for mode 2)
+  declareProperty("FilterTester", m_digitFilterTest = 0);  // non-zero means call Tester (during initialization phase).  
+}
+
+
+/**
+ * Destructor
+ */
+TileRawChannelBuilderManyAmps::~TileRawChannelBuilderManyAmps() {
+}
+
+
+/**
+ * Initialize
+ */
+StatusCode TileRawChannelBuilderManyAmps::initialize() {
+
+  ATH_MSG_INFO( "TileRawChannelBuilderManyAmps::initialize()" );
+
+  m_rChType = TileFragHash::ManyAmps;
+
+  // init in superclass
+  CHECK( TileRawChannelBuilder::initialize() );
+
+
+  //  **************************************************************************************
+  ATH_MSG_INFO( " TileRawChannelBuilderManyAmps init:"
+               << " FilterMode=" << m_digitFilterMode
+               << ", FilterLevel=" << m_digitFilterLevel
+               << ", FilterTest=" << m_digitFilterTest );
+
+  if (!(m_digitFilterMode == 2) && !(m_digitFilterMode == 3)) { // filter mode different from 2 or 3
+
+    ATH_MSG_WARNING( "Wrong Filter mode defined: FilterMode=" << m_digitFilterMode );
+    ATH_MSG_WARNING( "Switching to FilterMode=2" );
+    m_digitFilterMode = 2;
+  }
+
+  //  if( (m_digitFilterMode == 2) || (m_digitFilterMode == 3) ) { 
+  /* Get information needed to initialize TileFilterManager. */
+  int Nsamp = m_tileInfo->NdigitSamples();
+  // Set maximum number of parameters to fit, using FilterLevel
+  int nParamMax = m_digitFilterLevel;
+  if (nParamMax > Nsamp + 1) nParamMax = Nsamp;
+  if (nParamMax < 3) nParamMax = 3;
+  int InTsamp = m_tileInfo->ItrigSample();
+  int jBsamp = 0;
+  int jEsamp = Nsamp - 1;
+  int jBcross = 0;
+  int jEcross = Nsamp - 1;
+  int Nshape = m_tileInfo->NdigitSamples();
+  int InTshape = m_tileInfo->ItrigSample(); // need new method to give this !!! 
+  std::vector<double> ShapeXHi = m_tileInfo->digitsShapeHi();
+  std::vector<double> ShapeXLo = m_tileInfo->digitsShapeLo();
+
+  bool lVerbose = msgLvl(MSG::VERBOSE);
+  m_tileFilterManagerHi = new TileFilterManager(m_digitFilterMode, m_digitFilterLevel, nParamMax,
+      Nsamp, InTsamp, jBsamp, jEsamp, jBcross, jEcross, Nshape, InTshape, ShapeXHi, lVerbose);
+
+  m_tileFilterManagerLo = new TileFilterManager(m_digitFilterMode, m_digitFilterLevel, nParamMax,
+      Nsamp, InTsamp, jBsamp, jEsamp, jBcross, jEcross, Nshape, InTshape, ShapeXLo, lVerbose);
+
+  // Run TileFilterTester if m_digitFilterTest in non-zero).
+
+  if (m_digitFilterTest > 0) {
+
+    TileFilterTester* tFilterTestHi = new TileFilterTester(m_tileFilterManagerHi
+        , m_digitFilterMode, m_digitFilterTest, lVerbose);
+
+    tFilterTestHi->GenEvents(10);
+    delete tFilterTestHi;
+
+    TileFilterTester* tFilterTestLo = new TileFilterTester(m_tileFilterManagerLo
+        , m_digitFilterMode, m_digitFilterTest, lVerbose);
+
+    tFilterTestLo->GenEvents(10);
+    delete tFilterTestLo;
+  } // end FilterTest
+
+  ATH_MSG_DEBUG( "TileRawChannelBuilderManyAmps::initialize() completed successfully" );
+
+  return StatusCode::SUCCESS;
+}
+
+/**
+ *  Finalize
+ */
+StatusCode TileRawChannelBuilderManyAmps::finalize() {
+
+  ATH_MSG_DEBUG( "Finalizing" );
+  delete m_tileFilterManagerHi;
+  delete m_tileFilterManagerLo;
+  return StatusCode::SUCCESS;
+}
+
+TileRawChannel* TileRawChannelBuilderManyAmps::rawChannel(const TileDigits* tiledigits) {
+  ++m_chCounter;
+
+  const HWIdentifier adcId = tiledigits->adc_HWID();
+  int gain = m_tileHWID->adc(adcId);
+  bool lVerbose(false);
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "Running ManyAmps Fit for TileRawChannel with HWID "
+                      << m_tileHWID->to_string(adcId) << endmsg;
+    lVerbose = true;
+  }
+
+
+  /* get pedestal value assumed for reconstruction */
+  //double digSigma = m_tileInfo->DigitsResolution(gain);
+  // new way to get channel-dependent sigma:
+  // but we lost difference between sigma used in digitization and 
+  // sigma assumed in reconstruction - it's the same sigma now
+  double digSigma = m_tileInfo->DigitsPedSigma(adcId);
+
+  /* Get vector of time-slice amplitudes. */
+  std::vector<float> digits = tiledigits->samples();
+  double amp_ch = 0;      // Fitted amplitude of RC (to be returned from Filtering code).
+  double err_ch;      // Error in amp_ch (from Filtering code)
+  double ped_ch;      // Fitted pedestal of raw channel (from Filtering).
+  double chisq_ch = 0.; // Chisq resulting from Filtering.
+  double t_ch = 0.;     // Fitted time to be supplied by Filtering code
+  // ---------------------------------------------------------------------------------------
+
+  //int icode = 0;  
+
+  // Instantiate tResult, which will collect results from Filtering code.
+  TileFilterResult tResult(digits, digSigma);
+  // Call Fitter to extract the in-time pulse height and related info.
+  if (TileID::HIGHGAIN == gain)
+  /*icode =*/m_tileFilterManagerHi->FitDigits(tResult, lVerbose);
+
+  if (TileID::LOWGAIN == gain)
+  /*icode =*/m_tileFilterManagerLo->FitDigits(tResult, lVerbose);
+
+  tResult.getInTime(amp_ch, err_ch, ped_ch, chisq_ch, t_ch);
+
+  ATH_MSG_VERBOSE( " TileRawChannelBuilderManyAmps: return from FilterManager/Fitter."
+                  << ", chisq_ch=" << chisq_ch );
+
+  // convert to pCb (if needed)
+  if (m_calibrateEnergy) {
+    amp_ch = m_tileInfo->CisCalib(adcId, amp_ch);
+  }
+  // we know that time is zero here, put negagive chi^2 to indicate that
+  chisq_ch = -fabs(chisq_ch);
+
+  //  TileRawChannel *rawCh = new TileRawChannel(adcId,amp_ch,t_ch,chisq_ch);
+  static DataPool<TileRawChannel> tileRchPool(m_dataPoollSize);
+  TileRawChannel *rawCh = tileRchPool.nextElementPtr();
+  rawCh->m_adc_hwid = adcId;
+  rawCh->m_amplitude.resize(1);
+  rawCh->m_amplitude[0] = amp_ch;
+  rawCh->m_time.resize(1);
+  rawCh->m_time[0] = t_ch;
+  rawCh->m_quality.resize(1);
+  rawCh->m_quality[0] = chisq_ch;
+  rawCh->m_pedestal = 0.0; // default value in TileRawChannel constructor
+  ATH_MSG_VERBOSE(  "Creating RawChannel"
+                  << " a=" << amp_ch
+                  << " t=" << t_ch
+                  << " q=" << chisq_ch );
+
+  if (TileID::HIGHGAIN == gain) {
+    ++m_nChH;
+    m_RChSumH += amp_ch;
+  } else {
+    ++m_nChL;
+    m_RChSumL += amp_ch;
+  }
+
+  return rawCh;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderOpt2Filter.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderOpt2Filter.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..ea3915eaa7eba4ca5b99a38ba97cfde8b33f54c7
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderOpt2Filter.cxx
@@ -0,0 +1,768 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//////////////////////////////////////////////////////////////////////
+//
+//     Base on the code of Ximo Poveda.
+//     Andrei.Artamonov@cern.ch, July 2008
+//
+//     TileRawChannelBuilderOpt2Filter.cxx
+//
+//     implementation of the Optimal Filtering based on Lagrange multipliers
+//       for energy/time reconstruction in TileCal 
+//
+//////////////////////////////////////////////////////////////////////
+
+// small hack to enable datapool usage
+#define private public
+#define protected public
+#include "TileEvent/TileRawChannel.h"
+#include "TileCalibBlobObjs/TileCalibUtils.h"
+#undef private
+#undef protected
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+#include "GeoModelInterfaces/IGeoModelSvc.h"
+
+// Atlas includes
+#include "DataModel/DataPool.h"
+#include "AthenaKernel/errorcheck.h"
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilderOpt2Filter.h"
+#include "TileRecUtils/TileRawChannelBuilderOpt2FilterLookup.h"
+#include "TileConditions/TileOptFilterWeights.h"
+#include "TileConditions/TilePulseShapes.h"
+#include "CLHEP/Matrix/Matrix.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileDigitsContainer.h"
+#include "TileEvent/TileDigits.h"
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileConditions/TileInfo.h"
+#include "TileConditions/TileOptFilterWeights.h"
+
+using namespace std;
+
+//interface stuff
+static const InterfaceID IID_ITileRawChannelBuilderOpt2Filter("TileRawChannelBuilderOpt2Filter", 1, 0);
+
+
+const InterfaceID& TileRawChannelBuilderOpt2Filter::interfaceID() {
+  return IID_ITileRawChannelBuilderOpt2Filter;
+}
+
+
+#define TILE_Opt2FilterBUILDERVERBOSE false
+
+TileRawChannelBuilderOpt2Filter::TileRawChannelBuilderOpt2Filter(const std::string& type,
+    const std::string& name, const IInterface *parent)
+  : TileRawChannelBuilder(type, name, parent)
+  , m_tileToolTiming("TileCondToolTiming")
+  , m_tileCondToolOfc("TileCondToolOfc")
+  , m_tileCondToolOfcCool("TileCondToolOfcCool")
+  , m_tileToolNoiseSample("TileCondToolNoiseSample")
+  , c_signal(0)
+  , c_negat(0)
+  , c_center(0)
+  , c_const(0)
+  , m_NSamp(0)
+  , m_t0Samp(0)
+  , m_maxTime(0.0)
+  , m_minTime(0.0)
+{
+  //declare interfaces
+  declareInterface< TileRawChannelBuilder >( this );
+  declareInterface< TileRawChannelBuilderOpt2Filter >(this);
+
+  m_TileRawChannelContainerID = "TileRawChannelOpt2";
+  
+  //declare properties
+  declareProperty("TileCondToolTiming", m_tileToolTiming);
+  declareProperty("TileCondToolOfc",    m_tileCondToolOfc  ,"TileCondToolOfc");
+  declareProperty("TileCondToolOfcCool",m_tileCondToolOfcCool  ,"TileCondToolOfcCool");
+  declareProperty("TileCondToolNoiseSample", m_tileToolNoiseSample,"TileCondToolNoiseSample");
+  declareProperty("MaxIterations",m_maxIterations = 5);
+  declareProperty("PedestalMode",m_pedestalMode = 17);
+  declareProperty("TimeForConvergence",m_timeForConvergence = 0.5);
+  declareProperty("ConfTB",m_ConfTB = false);
+  declareProperty("OF2",m_of2 = true);
+  declareProperty("Minus1Iteration",m_minus1Iter = false);
+  declareProperty("AmplitudeCorrection",m_correctAmplitude = false);
+  declareProperty("BestPhase",m_bestphase = false);
+  declareProperty("OfcfromCool",m_ofcfromcool = false);
+  declareProperty("EmulateDSP",m_emulatedsp = false);
+}
+
+
+TileRawChannelBuilderOpt2Filter::~TileRawChannelBuilderOpt2Filter() {
+}
+
+
+StatusCode TileRawChannelBuilderOpt2Filter::initialize() {
+
+  ATH_MSG_INFO( "TileRawChannelBuilderOpt2Filter::initialize()" );
+
+  m_rChType = TileFragHash::OptFilterOffline; // type for offline Opt Filter
+
+  // init in superclass
+  CHECK( TileRawChannelBuilder::initialize() );
+
+  
+  // bits 12-15 - various options
+  if (m_correctAmplitude) m_bsflags |= 0x2000;
+  if (m_maxIterations > 1) m_bsflags |= 0x4000;
+  if (m_bestphase) m_bsflags |= 0x8000;
+
+  ATH_MSG_DEBUG( " MaxIterations=" << m_maxIterations
+                << " PedestalMode=" << m_pedestalMode
+                << " TimeForConvergence=" << m_timeForConvergence
+                << " ConfTB=" << m_ConfTB
+                << " OF2=" << m_of2
+                << " Minus1Iteration=" << m_minus1Iter
+                << " AmplitudeCorrection=" << m_correctAmplitude
+                << " Best Phase " << m_bestphase );
+
+  m_NSamp = m_tileInfo->NdigitSamples();
+  m_t0Samp = m_tileInfo->ItrigSample();
+  m_maxTime = 25 * (m_NSamp - m_t0Samp - 1);
+  m_minTime = -25 * m_t0Samp;
+  ATH_MSG_DEBUG(" NSamples=" << m_NSamp
+                << " T0Sample=" << m_t0Samp
+                << " minTime=" << m_minTime
+                << " maxTime=" << m_maxTime );
+
+  if (m_pedestalMode % 10 > 2 && m_NSamp != m_pedestalMode % 10) {
+    if (msgLvl(MSG::DEBUG)) msg(MSG::DEBUG) << "Changing PedestalMode from " << m_pedestalMode;
+    m_pedestalMode = (m_pedestalMode / 10) * 10 + m_NSamp;
+    if (msgLvl(MSG::DEBUG)) msg(MSG::DEBUG) << " to " << m_pedestalMode << endmsg;
+  }
+
+
+  if (m_NSamp != 7 && (m_pedestalMode == 71 || m_pedestalMode == 7621)) {
+    ATH_MSG_ERROR( "Incompatable pedestal mode [" << m_pedestalMode
+		   << "] and number of samples [" << m_NSamp << "]" );
+    return StatusCode::FAILURE;
+  }
+
+  c_signal = 0;
+  c_negat = 0;
+  c_center = 0;
+  c_const = 0;
+
+  const IGeoModelSvc *geoModel = 0;
+  CHECK( service("GeoModelSvc", geoModel) );
+  
+  // dummy parameters for the callback:
+  int dummyInt = 0;
+  std::list<std::string> dummyList;
+  
+  if (geoModel->geoInitialized()) {
+    return geoInit(dummyInt, dummyList);
+  } else {
+    CHECK( detStore()->regFcn(&IGeoModelSvc::geoInit, geoModel,
+        &TileRawChannelBuilderOpt2Filter::geoInit, this) );
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TileRawChannelBuilderOpt2Filter::geoInit(IOVSVC_CALLBACK_ARGS) {
+  
+  if (m_ofcfromcool) {
+    //=== get TileCondToolOfcCool
+    CHECK( m_tileCondToolOfcCool.retrieve() );
+  } else {
+    //=== get TileCondToolOfc
+    CHECK( m_tileCondToolOfc.retrieve() );
+  }
+  
+  //=== get TileCondToolNoiseSample
+  CHECK( m_tileToolNoiseSample.retrieve() );
+
+
+  if (m_bestphase) {
+    //=== get TileToolTiming
+    CHECK( m_tileToolTiming.retrieve() );
+  }
+  
+  ATH_MSG_INFO( "initialization completed" );
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TileRawChannelBuilderOpt2Filter::finalize() {
+
+  if (msgLvl(MSG::VERBOSE)) {
+    if (m_maxIterations == 1) { // Without iterations
+      msg(MSG::VERBOSE) << "Counters: Signal=" << c_signal
+                        << " Constant=" << c_const
+                        << " Total=" << c_signal + c_const << endmsg;
+    } else {
+      msg(MSG::VERBOSE) << "Counters: Signal=" << c_signal
+                        << " Negat=" << c_negat
+                        << " Center=" << c_center
+                        << " Constant=" << c_const
+                        << " Total=" << c_signal + c_negat + c_const + c_center << endmsg;
+    }
+  }
+
+  ATH_MSG_DEBUG( "Finalizing" );
+
+  return StatusCode::SUCCESS;
+}
+
+
+TileRawChannel * TileRawChannelBuilderOpt2Filter::rawChannel(const TileDigits* digits) {
+
+  ++m_chCounter;
+
+  double OptFilterPed = 0., OptFilterEne = 0., OptFilterTime = 0., OptFilterChi2 = 0.;
+  OptFilterDigits = digits->samples();
+  const HWIdentifier adcId = digits->adc_HWID();
+  int OptFilterGain = m_tileHWID->adc(adcId);
+  
+  ATH_MSG_VERBOSE( "Building Raw Channel, with OptFilter, HWID:" << m_tileHWID->to_string(adcId)
+                  << " gain=" << OptFilterGain );
+
+  int ros = m_tileHWID->ros(adcId);
+  int drawer = m_tileHWID->drawer(adcId);
+  int channel = m_tileHWID->channel(adcId);
+  OptFilterChi2 = Filter(ros, drawer, channel, OptFilterGain, OptFilterPed, OptFilterEne,
+      OptFilterTime);
+  
+  if (m_calibrateEnergy) {
+    OptFilterEne = m_tileInfo->CisCalib(adcId, OptFilterEne);
+  }
+  
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "Creating OptFilter RawChannel"
+                      << " a=" << OptFilterEne
+                      << " t=" << OptFilterTime
+                      << " ped=" << OptFilterPed
+                      << " q=" << OptFilterChi2 << endmsg;
+
+    msg(MSG::VERBOSE) << "digits:";
+
+    for (unsigned int i = 0; i < OptFilterDigits.size(); ++i)
+      msg(MSG::VERBOSE) << " " << OptFilterDigits[i];
+
+    msg(MSG::VERBOSE) << " " << endmsg;
+  }
+  
+  // return new TileRawChannel
+  // TileRawChannel *rawCh = new TileRawChannel(adcId,OptFilterEne,OptFilterTime,OptFilterChi2,OptFilterPed);
+  static DataPool<TileRawChannel> tileRchPool(m_dataPoollSize);
+  TileRawChannel *rawCh = tileRchPool.nextElementPtr();
+  rawCh->m_adc_hwid = adcId;
+  rawCh->m_amplitude.resize(1);
+  rawCh->m_amplitude[0] = OptFilterEne;
+  rawCh->m_time.resize(1);
+  rawCh->m_time[0] = OptFilterTime;
+  rawCh->m_quality.resize(1);
+  rawCh->m_quality[0] = OptFilterChi2;
+  rawCh->m_pedestal = OptFilterPed;
+
+  if (m_correctTime
+      && (OptFilterTime != 0
+          && OptFilterTime < m_maxTime
+          && OptFilterTime > m_minTime)) {
+
+    rawCh->insertTime(m_tileInfo->TimeCalib(adcId, OptFilterTime));
+    ATH_MSG_VERBOSE( "Correcting time, new time=" << rawCh->time() );
+
+  }
+
+  if (TileID::HIGHGAIN == OptFilterGain) {
+    ++m_nChH;
+    m_RChSumH += OptFilterEne;
+  } else {
+    ++m_nChL;
+    m_RChSumL += OptFilterEne;
+  }
+  
+  return rawCh;
+}
+
+
+float TileRawChannelBuilderOpt2Filter::FindMaxDigit() {
+
+  ATH_MSG_VERBOSE( "  TileRawChannelBuilderOpt2Filter::FindMaxDigit()" );
+
+  int imaxdig = 0;
+  float maxdig = 0.;
+  bool saturated = false;
+  
+  for (unsigned int i = 0; i < OptFilterDigits.size(); i++) {
+    if (OptFilterDigits[i] > 1022.99) saturated = true;
+    if (maxdig < OptFilterDigits[i]) {
+      maxdig = OptFilterDigits[i];
+      imaxdig = i;
+    }
+  }
+  
+  if (msgLvl(MSG::VERBOSE)) {
+    for (unsigned int i = 0; i < OptFilterDigits.size(); i++) {
+      msg(MSG::VERBOSE) << " " << OptFilterDigits[i];
+    }
+
+    msg(MSG::VERBOSE) << "; Max: digit[" << imaxdig << "]=" << maxdig << endmsg;
+
+    if (saturated)  msg(MSG::VERBOSE) << " Samples saturated" << endmsg;
+  }
+  
+  return imaxdig;
+}
+
+
+float TileRawChannelBuilderOpt2Filter::SetPedestal(int ros, int drawer, int channel, int gain) {
+  float pedestal = 0.;
+  
+  switch (m_pedestalMode) {
+    case -1:
+      // use pedestal from conditions DB
+      pedestal = m_tileToolNoiseSample->getPed(TileCalibUtils::getDrawerIdx(ros, drawer), channel, gain);
+      break;
+    case 7:
+      pedestal = OptFilterDigits[6];
+      break;
+    case 9:
+      pedestal = OptFilterDigits[8];
+      break;
+    case 12:
+      pedestal = .5 * (OptFilterDigits[0] + OptFilterDigits[1]);
+      break;
+    case 17:
+      pedestal = .5 * (OptFilterDigits[0] + OptFilterDigits[6]);
+      break;
+    case 19:
+      pedestal = .5 * (OptFilterDigits[0] + OptFilterDigits[8]);
+      break;
+    case 71:
+      pedestal = std::min(OptFilterDigits[0], OptFilterDigits[6]);
+      break;
+    case 7621:
+      pedestal = 0.5 * std::min(OptFilterDigits[0] + OptFilterDigits[1], OptFilterDigits[5] + OptFilterDigits[6]); 
+      break;
+    default:
+      pedestal = OptFilterDigits[0];
+      break;
+  }
+
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "  TileRawChannelBuilderOpt2Filter::SetPedestal()" << endmsg;
+    msg(MSG::VERBOSE) << "      pedestal=" << pedestal << endmsg;
+  }
+  
+  return pedestal;
+}
+
+
+double TileRawChannelBuilderOpt2Filter::Filter(int ros, int drawer, int channel
+    , int &OptFilterGain, double &OptFilterPed, double &OptFilterEne, double &OptFilterTime) {
+
+  ATH_MSG_VERBOSE( "TileRawChannelBuilderOpt2Filter::Filter" );
+
+  OptFilterEne = 0.;
+  OptFilterTime = 0.;
+  double OptFilterChi2 = 0.;
+
+  std::vector<float>::const_iterator idig = OptFilterDigits.begin();
+  std::vector<float>::const_iterator iend = OptFilterDigits.end();
+  float mindig = (*idig);
+  float maxdig = mindig;
+
+  for (++idig; idig != iend; ++idig) {
+    float samp = (*idig);
+    if (samp < mindig)
+      mindig = samp;
+    else if (samp > maxdig) maxdig = samp;
+  }
+  
+  if (maxdig - mindig < 0.01) { // constant value in all samples
+
+    OptFilterPed = mindig;
+    OptFilterChi2 = 0.;
+    ATH_MSG_VERBOSE( "CASE NO SIGNAL: maxdig-mindig = " << maxdig << "-" << mindig
+                    << " = " << maxdig - mindig );
+
+    c_const++;
+
+  } else {
+
+    OptFilterPed = SetPedestal(ros, drawer, channel, OptFilterGain);
+    double OptFilterPha = 0.;
+    int niter = 0;
+
+    if (m_maxIterations == 1) {  // Without iterations
+      unsigned int drawerIdx = TileCalibUtils::getDrawerIdx(ros, drawer);
+      // AA 3.10.08 --- take best phase from COOL
+      if (m_bestphase) {
+        // AS 19.11.09 - note minus sign here - time in DB is opposite to best phase 
+        OptFilterPha = -m_tileToolTiming->getSignalPhase(drawerIdx, channel, OptFilterGain);
+        ATH_MSG_VERBOSE( "Best phase: " << OptFilterPha
+                        << " drawerIdx " << drawerIdx
+                        << " channel " << channel );
+      }
+      
+      OptFilterChi2 = Compute(ros, drawer, channel, OptFilterGain, OptFilterPed
+          , OptFilterEne, OptFilterTime, OptFilterPha);
+
+      // If weights for tau=0 are used, deviations are seen in the amplitude =>
+      // function to correct the amplitude
+      if (m_correctAmplitude
+          && OptFilterEne > m_ampMinThresh
+          && OptFilterTime > m_timeMinThresh
+          && OptFilterTime < m_timeMaxThresh) {
+
+        OptFilterEne *= correctAmp(OptFilterTime);
+        ATH_MSG_VERBOSE( "Amplitude corrected by " << correctAmp(OptFilterTime)
+                        << " new amplitude is " << OptFilterEne );
+      }
+
+      if (OptFilterTime > m_maxTime) OptFilterTime = m_maxTime;
+      if (OptFilterTime < m_minTime) OptFilterTime = m_minTime;
+
+      c_signal++;
+
+    } else { // With iterations => 3 cases defined for correct pedestal treatment
+
+      // values used for pedestal-like events when iterations are performed
+      const int sigma_hi = 5;
+      const int sigma_lo = 3;
+      int sigma = (OptFilterGain) ? sigma_hi : sigma_lo;
+      int digits_size_1 = OptFilterDigits.size() - 1;
+
+      // Signal events: OF with iterations
+      if ((maxdig - OptFilterDigits[0] > sigma)
+          || (OptFilterDigits[0] - OptFilterDigits[digits_size_1] > 4 * sigma)) {
+
+        ATH_MSG_VERBOSE( "CASE Signal: maxdig-OptFilterDigits[0]="
+                        << maxdig - OptFilterDigits[0]
+                        << "   OptFilterDigits[0]-OptFilterDigits[ digits_size_1]="
+                        << OptFilterDigits[0] - OptFilterDigits[digits_size_1] );
+
+        niter = Iterator(ros, drawer, channel, OptFilterGain, OptFilterPed
+            , OptFilterEne, OptFilterTime, OptFilterChi2);
+
+        ATH_MSG_VERBOSE( "number of iterations= " << niter );
+
+        c_signal++;
+      } else if (OptFilterDigits[0] - mindig > sigma) { //Pedestal events: OF with negative iterations
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << "CASE NEGATIVE: maxdig-OptFilterDigits[0]="
+                            << maxdig - OptFilterDigits[0]
+                            << "   OptFilterDigits[0]-OptFilterDigits[digits_size_1]="
+                            << OptFilterDigits[0] - OptFilterDigits[digits_size_1] << endmsg;
+          msg(MSG::VERBOSE) << "OptFilterDigits[0]-mindig="
+                            << OptFilterDigits[0] - mindig << endmsg;
+        }
+
+        for (int i = 0; i <= digits_size_1; i++)  // Mirror around pedestal
+          OptFilterDigits[i] = OptFilterPed - (OptFilterDigits[i] - OptFilterPed);
+
+        niter = Iterator(ros, drawer, channel, OptFilterGain, OptFilterPed
+            , OptFilterEne, OptFilterTime, OptFilterChi2);
+
+        OptFilterEne = -OptFilterEne;
+
+        c_negat++;
+
+      } else { // Gaussian center: no iterations and phase=0
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << "CASE CENTER: maxdig-OptFilterDigits[0]="
+                            << maxdig - OptFilterDigits[0]
+                            << "   OptFilterDigits[0]-OptFilterDigits[digits_size_1]="
+                            << OptFilterDigits[0] - OptFilterDigits[digits_size_1] << endmsg;
+          msg(MSG::VERBOSE) << "OptFilterDigits[0]-mindig="
+                            << OptFilterDigits[0] - mindig
+                            << endmsg;
+        }
+        //	OptFilterTime=-100.;
+
+        OptFilterChi2 = Compute(ros, drawer, channel, OptFilterGain, OptFilterPed, OptFilterEne,
+            OptFilterTime, OptFilterPha);
+
+        OptFilterTime = 0.;
+        c_center++;
+
+      }
+      
+    }
+
+  }
+
+  return OptFilterChi2;
+}
+
+
+int TileRawChannelBuilderOpt2Filter::Iterator(int ros, int drawer, int channel, int OptFilterGain,
+    double &OptFilterPed, double &OptFilterEne, double &OptFilterTime, double &OptFilterChi2) {
+
+  ATH_MSG_VERBOSE( "TileRawChannelBuilderOpt2Filter::Iterator()" );
+
+  int niter = 0;
+  double save_phase = 0.0;
+  double OptFilterPha = 0.0;
+  OptFilterTime = -1000.;
+  
+  // Mythic -1 iteration or DSP emulation case
+  if (m_minus1Iter || (m_emulatedsp && (m_maxIterations > 1)))
+    OptFilterPha = 25 * (m_t0Samp - FindMaxDigit());
+
+  while ((OptFilterTime > m_timeForConvergence
+          || OptFilterTime < (-1.) * m_timeForConvergence
+          || m_emulatedsp)
+         && niter < m_maxIterations) {
+
+    OptFilterChi2 = Compute(ros, drawer, channel, OptFilterGain, OptFilterPed
+                            , OptFilterEne, OptFilterTime, OptFilterPha);
+
+    save_phase = OptFilterPha;
+
+    if (m_emulatedsp)
+      OptFilterPha -= round(OptFilterTime); // rounding phase to integer like in DSP
+    else if (m_ofcfromcool)
+      OptFilterPha -= round(OptFilterTime * 10.) / 10.; // rounding phase to 0.1 - OFC in DB are stored with 0.1ns steps
+    else
+      OptFilterPha -= OptFilterTime; // no rounding at all for OFC on the fly
+
+    if (OptFilterPha > m_maxTime) OptFilterPha = m_maxTime;
+    if (OptFilterPha < m_minTime) OptFilterPha = m_minTime;
+
+    ++niter;
+    ATH_MSG_VERBOSE( " OptFilter computed with phase=" << save_phase
+                    << " Time=" << OptFilterTime
+                    << " END ITER=" << niter
+                    << " new phase=" << OptFilterPha
+                    << " chi2=" << OptFilterChi2
+                    << "  Amp=" << OptFilterEne );
+  }
+  
+  OptFilterTime -= save_phase;
+  if (OptFilterTime > m_maxTime) OptFilterTime = m_maxTime;
+  if (OptFilterTime < m_minTime) OptFilterTime = m_minTime;
+  
+  ATH_MSG_VERBOSE( "OptFilterEne=" << OptFilterEne
+                  << " Phase=" << save_phase
+                  << " Absolute Time=" << OptFilterTime );
+
+  return niter;
+}
+
+
+double TileRawChannelBuilderOpt2Filter::Compute(int ros, int drawer, int channel, int OptFilterGain,
+    double &OptFilterPed, double &OptFilterEne, double &OptFilterTime, double OptFilterPha) {
+
+ ATH_MSG_VERBOSE( "TileRawChannelBuilderOpt2Filter::Compute();"
+                 << " ros=" << ros
+                 << " drawer="  << drawer
+                 << " channel=" << channel
+                 << " gain=" << OptFilterGain );
+
+  int i = 0, digits_size = OptFilterDigits.size();
+  double OptFilterChi2 = 0.;
+  double a[9];
+  double b[9];
+  double c[9];
+  double g[9];
+  double dg[9];
+  
+  OptFilterEne = 0.;
+  OptFilterTime = 0.;
+  float phase = (float) OptFilterPha;
+
+  unsigned int drawerIdx = TileCalibUtils::getDrawerIdx(ros, drawer);
+  const TileOfcWeightsStruct* m_weights;
+  if (m_ofcfromcool) {
+    m_weights = m_tileCondToolOfcCool->getOfcWeights(drawerIdx, channel, OptFilterGain, phase, m_of2);
+  } else {
+    m_weights = m_tileCondToolOfc->getOfcWeights(drawerIdx, channel, OptFilterGain, phase, m_of2);
+  }
+
+  for (i = 0; i < digits_size; ++i) {
+    a[i] = m_weights->w_a[i];
+    b[i] = m_weights->w_b[i];
+    g[i] = m_weights->g[i];
+    dg[i] = m_weights->dg[i];
+    if (m_of2) c[i] = m_weights->w_c[i]; // [OptFilterPha+100];
+  }
+
+  // for DSP emulation
+  short a_int[9], ascale = 0, calib = 0, calib_offset = 0;
+  short b_int[9], bscale = 0;
+  unsigned short scale = 0, round = 0, lut = 0;
+  int slope_scale = 0;
+  int OptFilterEneDSP = 0, OptFilterTimeDSP = 0;
+  if (m_emulatedsp) {
+    OptFilterEneDSP = 0;
+    OptFilterTimeDSP = 0;
+    slope_scale = (int) truncf(log(pow(2., 15) - 1.) / log(2.));
+    calib_offset = (short) roundf(pow(2., slope_scale));
+    ofc2int(digits_size, a, a_int, ascale);
+    ofc2int(digits_size, b, b_int, bscale);
+    calib = ascale + slope_scale;
+  }
+  
+  ATH_MSG_VERBOSE( "OptFilterPha=" << OptFilterPha );
+
+  if (m_of2) {
+    if (m_emulatedsp) {
+      OptFilterPed = OptFilterDigits[0];
+      for (i = 0; i < digits_size; ++i) {
+        OptFilterEneDSP += a_int[i] * ((int) OptFilterDigits[i]);
+        OptFilterTimeDSP += b_int[i] * ((int) OptFilterDigits[i]);
+        OptFilterEne += a[i] * OptFilterDigits[i]; //aa temp
+        OptFilterTime += b[i] * OptFilterDigits[i]; //aa temp
+      }
+    } else {
+      OptFilterPed = 0.;
+      for (i = 0; i < digits_size; ++i) {
+        OptFilterEne += a[i] * OptFilterDigits[i];
+        OptFilterTime += b[i] * OptFilterDigits[i];
+        OptFilterPed += c[i] * OptFilterDigits[i];
+      }
+    }
+  } else {
+    if (m_emulatedsp) OptFilterPed = OptFilterDigits[0];
+    for (i = 0; i < digits_size; ++i) {
+      OptFilterEne += a[i] * (OptFilterDigits[i] - OptFilterPed);
+      OptFilterTime += b[i] * (OptFilterDigits[i] - OptFilterPed);
+    }
+  }
+
+  if (m_emulatedsp) {
+    round = 1 << (ascale - 2);
+    short OptFilterE2DSP = (unsigned short) ((OptFilterEneDSP + round) >> (ascale - 1));
+    size_t OptInd = abs(OptFilterE2DSP);
+    if (OptInd >= (sizeof(lookup) / sizeof(short))) OptInd = 0;
+    lut = lookup[OptInd];
+    scale = bscale - 4 + lookup[0] - 9;
+    round = 1 << (scale - 1);
+    //    int told=OptFilterTimeDSP;
+    OptFilterTimeDSP = (((OptFilterTimeDSP + 0x100) >> 9) * lut + round) >> scale;
+    //        printf(" 1 OptFilterTime %f OptFilterTimeDSP %d e2 %d round %d lut %d bscale %d scale %d told %d\n",OptFilterTime/OptFilterEne,OptFilterTimeDSP,OptFilterE2DSP,round,lut,bscale,scale,told);
+    OptFilterTime = OptFilterTimeDSP / 16.0;
+
+    //  printf(" 1 OptFilterEneDSP %d calib_offset  %d calib %d \n",OptFilterEneDSP,calib_offset,calib);
+
+    OptFilterEneDSP = (OptFilterEneDSP + 1024) >> 11;
+    //  printf(" 2 OptFilterEneDSP %d  \n",OptFilterEneDSP);
+    OptFilterEneDSP = (OptFilterEneDSP * calib_offset + (1 << (calib - 15 - 1))) >> (calib - 15);
+    //  printf(" 3 OptFilterEneDSP %d  \n",OptFilterEneDSP);
+    double goffset = (OptFilterGain == 0) ? 512 : 2048;
+    OptFilterEneDSP = (int) (OptFilterEneDSP + goffset);
+    OptFilterEne = (OptFilterEneDSP - goffset) / 16.;
+  }
+
+  bool goodEne = (fabs(OptFilterEne) > 1.0e-04);
+  if (goodEne) {
+    if (msgLvl(MSG::VERBOSE)) {
+      msg(MSG::VERBOSE) << "OptFilterEne=" << OptFilterEne << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterTime*OptFilterEne=" << OptFilterTime << endmsg;
+    }
+    if (!m_emulatedsp) OptFilterTime /= OptFilterEne;
+  } else {
+    if (msgLvl(MSG::VERBOSE)) {
+      msg(MSG::VERBOSE) << "OptFilterEne=" << OptFilterEne
+                        << "   ... assuming 0.0" << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterTime*OptFilterEne=" << OptFilterTime
+                        << "   ... assuming 0.0" << endmsg;
+    }
+    OptFilterTime = OptFilterEne = 0.0;
+  }
+  
+  /* new QF in both cases now 02.2010 AA
+   if(!m_emulatedsp) {
+   for (i=0; i<digits_size; ++i){
+   if(OptFilterDigits[i]>0)
+   OptFilterChi2 += 50*fabs(OptFilterDigits[i]-(OptFilterEne*g[i]+OptFilterPed))/OptFilterDigits[i];
+   else
+   OptFilterChi2 += 50*fabs(OptFilterDigits[i]-(OptFilterEne*g[i]+OptFilterPed))/1024.;
+   }
+   } else {
+   */
+  for (i = 0; i < digits_size; ++i) {
+    double dqf = OptFilterDigits[i] - OptFilterEne * g[i] + OptFilterEne * OptFilterTime * dg[i]
+        - OptFilterPed;
+    OptFilterChi2 += dqf * dqf;
+  }
+  OptFilterChi2 = sqrt(OptFilterChi2);
+  // new QF  }
+  
+  //  std::cout << "emulate " << m_emulatedsp << " OptFilterEne " << OptFilterEne << " OptFilterDigits[3]" << OptFilterDigits[3] << " OptFilterTime="<<OptFilterTime<<" OptFilterPed="<<OptFilterPed<<" OptFilterChi2="<<OptFilterChi2<<" g 3 " << g[3] << " dg 1 3 5 " << dg[1] << " " << dg[3] << " " << dg[5] <<std::endl;
+  if (fabs(OptFilterChi2) > 1.0e-04 || goodEne) {
+    if (msgLvl(MSG::VERBOSE)) {
+      msg(MSG::VERBOSE) << "OptFilterTime=" << OptFilterTime << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterPed=" << OptFilterPed << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterChi2=" << OptFilterChi2 << endmsg;
+    }
+  } else {
+    if (msgLvl(MSG::VERBOSE)) {
+      msg(MSG::VERBOSE) << "OptFilterTime=" << OptFilterTime << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterPed=" << OptFilterPed << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterChi2=" << OptFilterChi2
+                        << "   ... assuming 0.0" << endmsg;
+    }
+    OptFilterChi2 = 0.0;
+  }
+
+  return OptFilterChi2;
+}
+
+
+void TileRawChannelBuilderOpt2Filter::ofc2int(int ndigits, double* w_off, short* w_int,
+    short &scale) {
+  // Number of bits of the integer word (signed -1 )
+  int NumberBits = 16;
+  NumberBits = NumberBits - 1;
+  
+  // Get Absolute Maximum
+  double max = -10000.;
+  double sum = 0.;
+  for (int i = 0; i < ndigits; i++) {
+    sum += w_off[i];
+    if (fabs(w_off[i]) > max) max = fabs(w_off[i]);
+    //    printf("idig = %d weight  = %f \n",i, w_off[i]);
+  }
+  if (fabs(sum) > max) max = fabs(sum);
+  
+  // Get Scale at Maximum
+  scale = 0;
+  if (max != 0) scale = (int) truncf(log((pow(2., NumberBits) - 1.) / max) / log(2.));
+  
+  // Convert to Integer the weights and the sum
+  for (int i = 0; i < ndigits; i++)
+    w_int[i] = (short) roundf(w_off[i] * pow(2., scale));
+  //aa  w_sum_dsp = (short) roundf(sum*pow(2.,scale));
+  
+
+/* 
+  if (msgLvl(MSG::VERBOSE)) {
+    printf("\nAbsolute Max value = %15.8f -> Scale = %3d\n",max,scale);
+
+    for (int i=0; i<ndigits; i++)
+      {
+	if ( i == 0){
+	  printf("\n        Offline              Off*Scale             Dsp/scale    Dsp      Scale  \n");
+	  printf("----------------------------------------------------------------------------------\n");
+	  printf("  %17.10f     %17.10f    %17.10f  0x%04X    %3d   \n",
+		 w_off[i],w_off[i]*pow(2.,scale), w_int[i]/pow(2.,scale),(unsigned int) w_int[i] ,scale);
+	} else {
+	  printf("  %17.10f     %17.10f    %17.10f  0x%04X   \n",
+		 w_off[i],w_off[i]*pow(2.,scale), w_int[i]/pow(2.,scale),(unsigned int)w_int[i]);
+	}
+      }
+    //aa    printf("  %17.10f     %17.10f    %17.10f  0x%04X     <- SUM\n",
+    //aa	   sum,sum*pow(2.,scale),w_sum_dsp/pow(2.,scale),(unsigned int)w_sum_dsp);
+  }
+*/  
+  return;
+}
+
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderOptFilter.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderOptFilter.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..c5b44fc2a9de23068ca6c4d01c9933047c343104
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderOptFilter.cxx
@@ -0,0 +1,887 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//////////////////////////////////////////////////////////////////////
+//
+//     Tilecal Valencia, Ximo.Poveda@cern.ch. June 2007
+//
+//     TileRawChannelBuilderOptFilter.cxx
+//
+//     implementation of the Optimal Filtering based on Lagrange multipliers
+//       for energy/time reconstruction in TileCal
+//
+//////////////////////////////////////////////////////////////////////
+
+// small hack to enable datapool usage
+#define private public
+#define protected public
+#include "TileEvent/TileRawChannel.h"
+#undef private
+#undef protected
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+
+// Atlas includes
+#include "DataModel/DataPool.h"
+#include "AthenaKernel/errorcheck.h"
+
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilderOptFilter.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileDigitsContainer.h"
+#include "TileEvent/TileDigits.h"
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileConditions/TileInfo.h"
+#include "TileConditions/TileOptFilterWeights.h"
+#include "TileConditions/TilePulseShapes.h"
+#include "CLHEP/Matrix/Matrix.h"
+
+using namespace std;
+
+//interface stuff
+static const InterfaceID IID_ITileRawChannelBuilderOptFilter("TileRawChannelBuilderOptFilter", 1, 0);
+
+const InterfaceID& TileRawChannelBuilderOptFilter::interfaceID() {
+  return IID_ITileRawChannelBuilderOptFilter;
+}
+
+
+#define TILE_OptFilterBUILDERVERBOSE false
+
+TileRawChannelBuilderOptFilter::TileRawChannelBuilderOptFilter(const std::string& type,
+    const std::string& name, const IInterface *parent)
+  : TileRawChannelBuilder(type, name, parent)
+  , c_signal(0)
+  , c_negat(0)
+  , c_center(0)
+  , m_NSamp(0)
+  , m_t0Samp(0)
+  , m_maxTime(0.0)
+  , m_minTime(0.0)
+  , m_pulseShapes(0)
+  , m_weights(0)
+{
+  //declare interfaces
+  declareInterface< TileRawChannelBuilder >( this );
+  declareInterface< TileRawChannelBuilderOptFilter >(this);
+
+  m_TileRawChannelContainerID = "TileRawChannelOpt";
+
+  //declare properties
+  declareProperty("MaxIterations",m_maxIterations = 5);
+  declareProperty("PedestalMode",m_pedestalMode = 17);
+  declareProperty("TimeForConvergence",m_timeForConvergence = 0.5);
+  declareProperty("ConfTB",m_ConfTB = false);
+  declareProperty("OF2",m_of2 = true);
+  declareProperty("Minus1Iteration",m_minus1Iter = false);
+  declareProperty("AmplitudeCorrection",m_correctAmplitude = false);
+
+}
+
+
+TileRawChannelBuilderOptFilter::~TileRawChannelBuilderOptFilter() {
+}
+
+
+StatusCode TileRawChannelBuilderOptFilter::initialize() {
+
+  ATH_MSG_INFO( "TileRawChannelBuilderOptFilter::initialize()" );
+
+  m_rChType = TileFragHash::OptFilterOffline; // type for offline Opt Filter
+
+  // init in superclass
+  CHECK( TileRawChannelBuilder::initialize() );
+
+  // bits 12-15 - various options
+  if (m_correctAmplitude) m_bsflags |= 0x2000;
+  if (m_maxIterations > 1) m_bsflags |= 0x4000;
+
+  ATH_MSG_DEBUG( " MaxIterations=" << m_maxIterations
+                << " PedestalMode=" << m_pedestalMode
+                << " TimeForConvergence=" << m_timeForConvergence
+                << " ConfTB=" << m_ConfTB
+                << " OF2=" << m_of2
+                << " Minus1Iteration=" << m_minus1Iter
+                << " AmplitudeCorrection=" << m_correctAmplitude );
+
+  m_NSamp = m_tileInfo->NdigitSamples();
+  m_t0Samp = m_tileInfo->ItrigSample();
+  m_maxTime = 25 * (m_NSamp - m_t0Samp - 1);
+  m_minTime = -25 * m_t0Samp;
+  ATH_MSG_DEBUG( " NSamples=" << m_NSamp
+                << " T0Sample=" << m_t0Samp
+                << " minTime=" << m_minTime
+                << " maxTime=" << m_maxTime );
+
+  if (m_pedestalMode % 10 > 2 && m_NSamp != m_pedestalMode % 10) {
+    int oldPedestalMode(m_pedestalMode);
+    m_pedestalMode = (m_pedestalMode / 10) * 10 + m_NSamp;
+    ATH_MSG_DEBUG( "Changing PedestalMode from " << oldPedestalMode
+                  << " to " << m_pedestalMode );
+  }
+
+  //get pointer to pulse shapes build pulse shapes)
+  m_pulseShapes = m_tileInfo->getPulseShapes();
+
+  vector<double> m_LpulseShapeX_cis, m_LpulseShapeT_cis;
+  vector<double> m_HpulseShapeX_cis, m_HpulseShapeT_cis;
+  vector<double> m_LpulseShapeX_phys, m_LpulseShapeT_phys;
+  vector<double> m_HpulseShapeX_phys, m_HpulseShapeT_phys;
+
+  vector<double> m_LdpulseShapeX_cis, m_LdpulseShapeT_cis;
+  vector<double> m_HdpulseShapeX_cis, m_HdpulseShapeT_cis;
+  vector<double> m_LdpulseShapeX_phys, m_LdpulseShapeT_phys;
+  vector<double> m_HdpulseShapeX_phys, m_HdpulseShapeT_phys;
+
+  m_LpulseShapeX_cis = m_pulseShapes->m_ylcis;
+  m_LpulseShapeT_cis = m_pulseShapes->m_tlcis;
+  m_HpulseShapeX_cis = m_pulseShapes->m_yhcis;
+  m_HpulseShapeT_cis = m_pulseShapes->m_thcis;
+
+  m_LpulseShapeX_phys = m_pulseShapes->m_ylphys;
+  m_LpulseShapeT_phys = m_pulseShapes->m_tlphys;
+  m_HpulseShapeX_phys = m_pulseShapes->m_yhphys;
+  m_HpulseShapeT_phys = m_pulseShapes->m_thphys;
+
+  m_LdpulseShapeX_cis = m_pulseShapes->m_ydlcis;
+  m_LdpulseShapeT_cis = m_pulseShapes->m_tdlcis;
+  m_HdpulseShapeX_cis = m_pulseShapes->m_ydhcis;
+  m_HdpulseShapeT_cis = m_pulseShapes->m_tdhcis;
+
+  m_LdpulseShapeX_phys = m_pulseShapes->m_ydlphys;
+  m_LdpulseShapeT_phys = m_pulseShapes->m_tdlphys;
+  m_HdpulseShapeX_phys = m_pulseShapes->m_ydhphys;
+  m_HdpulseShapeT_phys = m_pulseShapes->m_tdhphys;
+
+  if (msgLvl(MSG::DEBUG)) {
+
+    msg(MSG::DEBUG) << " m_LpulseShapeX_cis.size()=" << m_LpulseShapeX_cis.size()
+                    << " m_HpulseShapeX_cis.size()=" << m_HpulseShapeX_cis.size()
+                    << " m_LpulseShapeX_phys.size()=" << m_LpulseShapeX_phys.size()
+                    << " m_HpulseShapeX_phys.size()=" << m_HpulseShapeX_phys.size()
+                    << " m_LdpulseShapeX_cis.size()=" << m_LdpulseShapeX_cis.size()
+                    << " m_HdpulseShapeX_cis.size()=" << m_HdpulseShapeX_cis.size()
+                    << " m_LdpulseShapeX_phys.size()=" << m_LdpulseShapeX_phys.size()
+                    << " m_HdpulseShapeX_phys.size()=" << m_HdpulseShapeX_phys.size() << endmsg;
+
+
+    int N = 0;
+    for (unsigned int i = 0; i < m_LpulseShapeX_cis.size(); i++)
+      msg(MSG::DEBUG) << "N=" << ++N << std::setw(8) << std::setprecision(4)
+                      << m_LpulseShapeT_cis[i] << std::setw(8) << std::setprecision(4)
+                      << m_LpulseShapeX_cis[i] << endmsg;
+
+    N = 0;
+    for (unsigned int i = 0; i < m_HpulseShapeX_cis.size(); i++)
+      msg(MSG::DEBUG) << "N=" << ++N << std::setw(8) << std::setprecision(4)
+                      << m_HpulseShapeT_cis[i] << std::setw(8) << std::setprecision(4)
+                      << m_HpulseShapeX_cis[i] << endmsg;
+
+    N = 0;
+    for (unsigned int i = 0; i < m_LpulseShapeX_phys.size(); i++)
+      msg(MSG::DEBUG) << "N=" << ++N << std::setw(8) << std::setprecision(4)
+                      << m_LpulseShapeT_phys[i] << std::setw(8) << std::setprecision(4)
+                      << m_LpulseShapeX_phys[i] << endmsg;
+
+    N = 0;
+    for (unsigned int i = 0; i < m_HpulseShapeX_phys.size(); i++)
+      msg(MSG::DEBUG) << "N=" << ++N << std::setw(8) << std::setprecision(4)
+                      << m_HpulseShapeT_phys[i] << std::setw(8) << std::setprecision(4)
+                      << m_HpulseShapeX_phys[i] << endmsg;
+
+    N = 0;
+    for (unsigned int i = 0; i < m_LdpulseShapeX_cis.size(); i++)
+      msg(MSG::DEBUG) << "N=" << ++N << std::setw(8) << std::setprecision(4)
+                    << m_LdpulseShapeT_cis[i] << std::setw(8) << std::setprecision(4)
+                    << m_LdpulseShapeX_cis[i] << endmsg;
+
+    N = 0;
+    for (unsigned int i = 0; i < m_HdpulseShapeX_cis.size(); i++)
+      msg(MSG::DEBUG) << "N=" << ++N << std::setw(8) << std::setprecision(4)
+                      << m_HdpulseShapeT_cis[i] << std::setw(8) << std::setprecision(4)
+                      << m_HdpulseShapeX_cis[i] << endmsg;
+
+    N = 0;
+    for (unsigned int i = 0; i < m_LdpulseShapeX_phys.size(); i++)
+      msg(MSG::DEBUG) << "N=" << ++N << std::setw(8) << std::setprecision(4)
+                      << m_LdpulseShapeT_phys[i] << std::setw(8) << std::setprecision(4)
+                      << m_LdpulseShapeX_phys[i] << endmsg;
+
+    N = 0;
+    for (unsigned int i = 0; i < m_HdpulseShapeX_phys.size(); i++)
+      msg(MSG::DEBUG) << "N=" << ++N << std::setw(8) << std::setprecision(4)
+                      << m_HdpulseShapeT_phys[i] << std::setw(8) << std::setprecision(4)
+                      << m_HdpulseShapeX_phys[i] << endmsg;
+  }
+
+  //Build Pulse Shape (use the one in TileCalibAlgs?)
+  BuildPulseShape(m_LpulseShape_cis, m_LpulseShapeX_cis, m_LpulseShapeT_cis, 9);
+  BuildPulseShape(m_HpulseShape_cis, m_HpulseShapeX_cis, m_HpulseShapeT_cis, 9);
+  BuildPulseShape(m_LpulseShape_phys, m_LpulseShapeX_phys, m_LpulseShapeT_phys, 9);
+  BuildPulseShape(m_HpulseShape_phys, m_HpulseShapeX_phys, m_HpulseShapeT_phys, 9);
+  BuildPulseShape(m_LdpulseShape_cis, m_LdpulseShapeX_cis, m_LdpulseShapeT_cis, 9);
+  BuildPulseShape(m_HdpulseShape_cis, m_HdpulseShapeX_cis, m_HdpulseShapeT_cis, 9);
+  BuildPulseShape(m_LdpulseShape_phys, m_LdpulseShapeX_phys, m_LdpulseShapeT_phys, 9);
+  BuildPulseShape(m_HdpulseShape_phys, m_HdpulseShapeX_phys, m_HdpulseShapeT_phys, 9);
+
+  if (msgLvl(MSG::DEBUG)) {
+    msg(MSG::DEBUG) << "m_LpulseShape_cis.size()=" << m_LpulseShape_cis.size() << endmsg;
+    for (unsigned int i = 0; i < m_LpulseShape_cis.size(); i++)
+      msg(MSG::DEBUG) << i << " " << m_LpulseShape_cis[i] << endmsg;
+
+    msg(MSG::DEBUG) << "m_HpulseShape_cis.size()=" << m_HpulseShape_cis.size() << endmsg;
+    for (unsigned int i = 0; i < m_HpulseShape_cis.size(); i++)
+      msg(MSG::DEBUG) << i << " " << m_HpulseShape_cis[i] << endmsg;
+
+    msg(MSG::DEBUG) << "m_LpulseShape_phys.size()=" << m_LpulseShape_phys.size() << endmsg;
+    for (unsigned int i = 0; i < m_LpulseShape_phys.size(); i++)
+      msg(MSG::DEBUG) << i << " " << m_LpulseShape_phys[i] << endmsg;
+
+    msg(MSG::DEBUG) << "m_HpulseShape_phys.size()=" << m_HpulseShape_phys.size() << endmsg;
+    for (unsigned int i = 0; i < m_HpulseShape_phys.size(); i++)
+      msg(MSG::DEBUG) << i << " " << m_HpulseShape_phys[i] << endmsg;
+
+    msg(MSG::DEBUG) << "m_LdpulseShape_cis.size()=" << m_LdpulseShape_cis.size() << endmsg;
+    for (unsigned int i = 0; i < m_LdpulseShape_cis.size(); i++)
+      msg(MSG::DEBUG) << i << " " << m_LdpulseShape_cis[i] << endmsg;
+
+    msg(MSG::DEBUG) << "m_HdpulseShape_cis.size()=" << m_HdpulseShape_cis.size() << endmsg;
+    for (unsigned int i = 0; i < m_HdpulseShape_cis.size(); i++)
+      msg(MSG::DEBUG) << i << " " << m_HdpulseShape_cis[i] << endmsg;
+
+    msg(MSG::DEBUG) << "m_LdpulseShape_phys.size()=" << m_LdpulseShape_phys.size() << endmsg;
+    for (unsigned int i = 0; i < m_LdpulseShape_phys.size(); i++)
+      msg(MSG::DEBUG) << i << " " << m_LdpulseShape_phys[i] << endmsg;
+
+    msg(MSG::DEBUG) << "m_HdpulseShape_phys.size()=" << m_HdpulseShape_phys.size() << endmsg;
+    for (unsigned int i = 0; i < m_HdpulseShape_phys.size(); i++)
+      msg(MSG::DEBUG) << i << " " << m_HdpulseShape_phys[i] << endmsg;
+  }
+
+  //get pointer to weights (loaded by the InfoLoader in TileInfo, from TileOptFilterWeights)
+  m_weights = m_tileInfo->getOptFilterWeights();
+  ATH_MSG_DEBUG("Weights loaded" );
+
+  c_signal = 0;
+  c_negat = 0;
+  c_center = 0;
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TileRawChannelBuilderOptFilter::finalize() {
+
+  ATH_MSG_VERBOSE( "Counters: Signal=" << c_signal
+                  << " Negat=" << c_negat
+                  << " Center=" << c_center );
+
+  ATH_MSG_DEBUG( "Finalizing" );
+
+  return StatusCode::SUCCESS;
+}
+
+
+TileRawChannel * TileRawChannelBuilderOptFilter::rawChannel(const TileDigits* digits) {
+  ++m_chCounter;
+
+  double OptFilterPed = 0., OptFilterEne = 0., OptFilterTime = 0., OptFilterChi2 = 0.;
+  OptFilterDigits = digits->samples();
+  const HWIdentifier adcId = digits->adc_HWID();
+  int OptFilterGain = m_tileHWID->adc(adcId);
+
+  ATH_MSG_VERBOSE( "Building Raw Channel, with OptFilter, HWID:" << m_tileHWID->to_string(adcId)
+                  << " gain=" << OptFilterGain );
+
+  if (m_ConfTB) {  // Using old weights for CTB
+    int ros = m_tileHWID->ros(adcId);
+    int drawer = m_tileHWID->drawer(adcId);
+    int channel = m_tileHWID->channel(adcId);
+    OptFilterChi2 = Filter(ros, drawer, channel, OptFilterGain, OptFilterPed, OptFilterEne,
+        OptFilterTime);
+
+  } else {  // Using weights obtained with delta correlation
+    OptFilterChi2 = Filter(1000, 1000, 1000, OptFilterGain, OptFilterPed, OptFilterEne,
+        OptFilterTime);
+  }
+
+  if (m_calibrateEnergy) {
+    OptFilterEne = m_tileInfo->CisCalib(adcId, OptFilterEne);
+  }
+
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "Creating OptFilter RawChannel a=" << OptFilterEne
+                      << " t=" << OptFilterTime
+                      << " ped=" << OptFilterPed
+                      << " q=" << OptFilterChi2 << endmsg;
+
+    msg(MSG::VERBOSE) << "digits:";
+    for (unsigned int i = 0; i < OptFilterDigits.size(); ++i)
+      msg(MSG::VERBOSE) << " " << OptFilterDigits[i];
+
+    msg(MSG::VERBOSE) << " " << endmsg;
+  }
+
+  // return new TileRawChannel
+  // TileRawChannel *rawCh = new TileRawChannel(adcId,OptFilterEne,OptFilterTime,OptFilterChi2,OptFilterPed);
+
+  static DataPool<TileRawChannel> tileRchPool(m_dataPoollSize);
+  TileRawChannel *rawCh = tileRchPool.nextElementPtr();
+  rawCh->m_adc_hwid = adcId;
+  rawCh->m_amplitude.resize(1);
+  rawCh->m_amplitude[0] = OptFilterEne;
+  rawCh->m_time.resize(1);
+  rawCh->m_time[0] = OptFilterTime;
+  rawCh->m_quality.resize(1);
+  rawCh->m_quality[0] = OptFilterChi2;
+  rawCh->m_pedestal = OptFilterPed;
+
+  if (m_correctTime
+      && (OptFilterTime != 0 && OptFilterTime < m_maxTime && OptFilterTime > m_minTime)) {
+
+    rawCh->insertTime(m_tileInfo->TimeCalib(adcId, OptFilterTime));
+    ATH_MSG_VERBOSE( "Correcting time, new time=" << rawCh->time() );
+
+  }
+
+  if (TileID::HIGHGAIN == OptFilterGain) {
+    ++m_nChH;
+    m_RChSumH += OptFilterEne;
+  } else {
+    ++m_nChL;
+    m_RChSumL += OptFilterEne;
+  }
+
+  return rawCh;
+}
+
+
+float TileRawChannelBuilderOptFilter::FindMaxDigit() {
+
+  ATH_MSG_VERBOSE( "  TileRawChannelBuilderOptFilter::FindMaxDigit()" );
+
+  int imaxdig = 0;
+  float maxdig = 0.;
+  bool saturated = false;
+
+  for (unsigned int i = 0; i < OptFilterDigits.size(); i++) {
+    if (OptFilterDigits[i] > 1022.99) saturated = true;
+    if (maxdig < OptFilterDigits[i]) {
+      maxdig = OptFilterDigits[i];
+      imaxdig = i;
+    }
+  }
+
+  if (msgLvl(MSG::VERBOSE)) {
+    for (unsigned int i = 0; i < OptFilterDigits.size(); i++) {
+      msg(MSG::VERBOSE) << " " << OptFilterDigits[i];
+    }
+
+    msg(MSG::VERBOSE) << "; Max: digit[" << imaxdig << "]=" << maxdig << endmsg;
+
+    if (saturated) msg(MSG::VERBOSE) << " Samples saturated" << endmsg;
+  }
+
+  return imaxdig;
+}
+
+float TileRawChannelBuilderOptFilter::MaxDigDiff() {
+  float leastdig = 1024.;
+  float maxdig = 0.;
+
+  for (unsigned int i = 0; i < OptFilterDigits.size(); i++) {
+    if (leastdig > OptFilterDigits[i]) leastdig = OptFilterDigits[i];
+    if (maxdig < OptFilterDigits[i]) maxdig = OptFilterDigits[i];
+  }
+
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "  TileRawChannelBuilderOptFilter::MaxDigDiff()" << endmsg;
+    msg(MSG::VERBOSE) << "      maxdig-leastdig=" << maxdig - leastdig << endmsg;
+  }
+
+  return maxdig - leastdig;
+}
+
+
+float TileRawChannelBuilderOptFilter::MaxDigit() {
+  float maxdig = 0.;
+
+  for (unsigned int i = 0; i < OptFilterDigits.size(); i++)
+    if (maxdig < OptFilterDigits[i]) maxdig = OptFilterDigits[i];
+
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "  TileRawChannelBuilderOptFilter::MaxDigit()" << endmsg;
+    msg(MSG::VERBOSE) << "      maxdig=" << maxdig << endmsg;
+  }
+
+  return maxdig;
+}
+
+
+bool TileRawChannelBuilderOptFilter::Are3FF() {
+  bool allSaturated = true;
+
+  for (unsigned int i = 0; i < OptFilterDigits.size(); i++) {
+    allSaturated = allSaturated && (OptFilterDigits[i] > 1022.99);
+  }
+
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "  TileRawChannelBuilderOptFilter::Are3FF()" << endmsg;
+    msg(MSG::VERBOSE) << "      allSat=" << allSaturated << endmsg;
+  }
+
+  return allSaturated;
+}
+
+
+float TileRawChannelBuilderOptFilter::SetPedestal() {
+  float pedestal = 0.;
+
+  switch (m_pedestalMode) {
+    case 7:
+      pedestal = OptFilterDigits[6];
+      break;
+    case 9:
+      pedestal = OptFilterDigits[8];
+      break;
+    case 12:
+      pedestal = .5 * (OptFilterDigits[0] + OptFilterDigits[1]);
+      break;
+    case 17:
+      pedestal = .5 * (OptFilterDigits[0] + OptFilterDigits[6]);
+      break;
+     case 19:
+      pedestal = .5 * (OptFilterDigits[0] + OptFilterDigits[8]);
+      break;
+    default:
+      pedestal = OptFilterDigits[0];
+      break;
+  }
+
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "  TileRawChannelBuilderOptFilter::SetPedestal()" << endmsg;
+    msg(MSG::VERBOSE) << "      pedestal=" << pedestal << endmsg;
+  }
+
+  return pedestal;
+}
+
+
+double TileRawChannelBuilderOptFilter::Filter(int ros, int drawer, int channel
+    , int &OptFilterGain, double &OptFilterPed, double &OptFilterEne, double &OptFilterTime) {
+
+  ATH_MSG_VERBOSE( "TileRawChannelBuilderOptFilter::Filter" );
+
+  OptFilterPed = SetPedestal();
+  OptFilterEne = 0.;
+  OptFilterTime = 0.;
+  int OptFilterPha = 0;
+  double OptFilterChi2 = 0.;
+  int /* imaxdig=FindMaxDigit(), */niter = 0, digits_size = OptFilterDigits.size();
+  float digdiff = MaxDigDiff();
+  float maxdig = MaxDigit();
+  float mindig = maxdig - digdiff;
+
+  // values used for pedestal-like events when iterations are performed
+  int sigma;
+  int sigma_hi = 5;
+  int sigma_lo = 3;
+
+  if (OptFilterGain == 0) sigma = sigma_lo;
+  else sigma = sigma_hi;
+
+  if (Are3FF()) {
+
+    OptFilterGain = 0;
+    OptFilterPed = 0.;
+    OptFilterEne = 0.;
+    OptFilterTime = 0.;
+    if (digits_size > 0 && OptFilterDigits[0] > 2046.99)
+      OptFilterChi2 = 9999.;
+    else
+      OptFilterChi2 = 0.;
+
+  } else {
+
+    if (m_maxIterations == 1) {  // Without iterations
+      OptFilterChi2 = Compute(ros, drawer, channel, OptFilterGain, OptFilterPed
+                              , OptFilterEne, OptFilterTime, OptFilterPha);
+
+      // If weights for tau=0 are used, deviations are seen in the amplitude =>
+      // function to correct the amplitude
+      if (m_correctAmplitude
+          && OptFilterEne > m_ampMinThresh
+          && OptFilterTime > m_timeMinThresh
+          && OptFilterTime < m_timeMaxThresh) {
+
+        OptFilterEne *= correctAmp(OptFilterTime);
+        ATH_MSG_VERBOSE( "Amlitude corrected by " << correctAmp(OptFilterTime)
+                        << " new amplitude is " << OptFilterEne );
+      }
+
+      if (OptFilterTime > m_maxTime) OptFilterTime = m_maxTime;
+      if (OptFilterTime < m_minTime) OptFilterTime = m_minTime;
+
+    } else { // With iterations => 3 cases defined for correct pedestal treatment
+
+      // Signal events: OF with iterations
+      if ((maxdig - OptFilterDigits[0] > sigma)
+          || (OptFilterDigits[0] - OptFilterDigits[digits_size - 1] > 4 * sigma)) {
+
+        ATH_MSG_VERBOSE( "CASE Signal: maxdig-OptFilterDigits[0]="
+                        << maxdig - OptFilterDigits[0]
+                        << "   OptFilterDigits[0]-OptFilterDigits[ digits_size-1]="
+                        << OptFilterDigits[0] - OptFilterDigits[digits_size - 1] );
+
+        niter = Iterator(ros, drawer, channel, OptFilterGain, OptFilterPed, OptFilterEne,
+            OptFilterTime, OptFilterChi2);
+
+        ATH_MSG_VERBOSE( "number of iterations= " << niter );
+
+        c_signal++;
+      } else if (OptFilterDigits[0] - mindig > sigma) { //Pedestal events: OF with negative iterations
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << "CASE NEGATIVE: maxdig-OptFilterDigits[0]="
+                            << maxdig - OptFilterDigits[0]
+                            << "   OptFilterDigits[0]-OptFilterDigits[digits_size-1]="
+                            << OptFilterDigits[0] - OptFilterDigits[digits_size - 1] << endmsg;
+
+          msg(MSG::VERBOSE) << "OptFilterDigits[0]-mindig="
+                            << OptFilterDigits[0] - mindig << endmsg;
+        }
+
+        for (int i = 0; i < digits_size; i++)  // Mirror around pedestal
+          OptFilterDigits[i] = OptFilterPed - (OptFilterDigits[i] - OptFilterPed);
+
+        niter = Iterator(ros, drawer, channel, OptFilterGain, OptFilterPed
+                        , OptFilterEne, OptFilterTime, OptFilterChi2);
+
+        OptFilterEne = -OptFilterEne;
+
+        c_negat++;
+
+      } else { // Gaussian center: no iterations and phase=0
+
+        if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << "CASE CENTER: maxdig-OptFilterDigits[0]="
+                            << maxdig - OptFilterDigits[0]
+                            << "   OptFilterDigits[0]-OptFilterDigits[digits_size-1]="
+                            << OptFilterDigits[0] - OptFilterDigits[digits_size - 1] << endmsg;
+
+          msg(MSG::VERBOSE) << "OptFilterDigits[0]-mindig="
+                            << OptFilterDigits[0] - mindig << endmsg;
+        }
+        //	OptFilterTime=-100.;
+
+        OptFilterChi2 = Compute(ros, drawer, channel, OptFilterGain, OptFilterPed, OptFilterEne,
+            OptFilterTime, OptFilterPha);
+
+        OptFilterTime = 0.;
+        c_center++;
+
+      }
+
+    }
+
+  }
+
+  return OptFilterChi2;
+}
+
+
+int TileRawChannelBuilderOptFilter::Iterator(int ros, int drawer, int channel, int OptFilterGain,
+    double &OptFilterPed, double &OptFilterEne, double &OptFilterTime, double &OptFilterChi2) {
+
+  ATH_MSG_VERBOSE( "TileRawChannelBuilderOptFilter::Iterator()" );
+
+  int niter = 0;
+  double save_phase = 0.0;
+  double OptFilterPha = 0.0;
+  OptFilterTime = -1000.;
+
+  // Mythic -1 iteration
+  if (m_minus1Iter) OptFilterPha = 25 * (m_t0Samp - FindMaxDigit());
+
+  while ((OptFilterTime > m_timeForConvergence
+          || OptFilterTime < (-1.) * m_timeForConvergence)
+        && niter < m_maxIterations) {
+
+    OptFilterChi2 = Compute(ros, drawer, channel, OptFilterGain, OptFilterPed
+                            , OptFilterEne, OptFilterTime, (int) OptFilterPha);
+
+    if (msgLvl(MSG::VERBOSE)) {
+      msg(MSG::VERBOSE) << " OptFilter computed with phase=" << OptFilterPha
+                        << " Time=" << OptFilterTime;
+    }
+
+    save_phase = OptFilterPha;
+
+    OptFilterPha -= round(OptFilterTime);
+    if (OptFilterPha > m_maxTime) OptFilterPha = m_maxTime;
+    if (OptFilterPha < m_minTime) OptFilterPha = m_minTime;
+
+    niter++;
+    if (msgLvl(MSG::VERBOSE)) {
+          msg(MSG::VERBOSE) << " END ITER=" << niter
+                            << " new phase=" << OptFilterPha
+                            << " chi2=" << OptFilterChi2
+                            << "  Amp=" << OptFilterEne << endmsg;
+    }
+  }
+
+  OptFilterTime -= save_phase;
+  if (OptFilterTime > m_maxTime) OptFilterTime = m_maxTime;
+  if (OptFilterTime < m_minTime) OptFilterTime = m_minTime;
+
+  ATH_MSG_VERBOSE( "OptFilterEne=" << OptFilterEne
+                  << " Phase=" << save_phase
+                  << " Absolute Time=" << OptFilterTime );
+
+  return niter;
+}
+
+
+double TileRawChannelBuilderOptFilter::Compute(int ros, int drawer, int channel, int OptFilterGain,
+    double &OptFilterPed, double &OptFilterEne, double &OptFilterTime, int OptFilterPha) {
+
+  ATH_MSG_VERBOSE( "TileRawChannelBuilderOptFilter::Compute(); ros=" << ros
+                  << " drawer=" << drawer
+                  << " channel=" << channel
+                  << " gain=" << OptFilterGain );
+
+  int i = 0, digits_size = OptFilterDigits.size();
+  double OptFilterChi2 = 0.;
+  double a[9];
+  double b[9];
+  double c[9];
+  double g[9];
+
+  OptFilterEne = 0.;
+  OptFilterTime = 0.;
+
+  if (m_idocis) {
+    for (i = 0; i < 7; ++i) { // 7 samples only
+      a[i] = m_weights->a_cis_simp[OptFilterGain][i][OptFilterPha + 100];
+      b[i] = m_weights->b_cis_simp[OptFilterGain][i][OptFilterPha + 100];
+      if (m_of2) c[i] = m_weights->c_cis_simp[OptFilterGain][i][OptFilterPha + 100];
+    }
+  } else {
+    for (i = 0; i < digits_size; ++i) { // 7 or 9 samples
+      a[i] = m_weights->a_phys_simp[OptFilterGain][i][OptFilterPha + 100];
+      b[i] = m_weights->b_phys_simp[OptFilterGain][i][OptFilterPha + 100];
+      if (m_of2) c[i] = m_weights->c_phys_simp[OptFilterGain][i][OptFilterPha + 100];
+    }
+  }
+
+  if (m_idocis)
+    if (OptFilterGain == 0)
+      for (i = 0; i < digits_size; i++)
+        g[i] = m_LpulseShape_cis[(i - (digits_size - 9) / 2) * 25 + 100 + OptFilterPha];
+    else
+      for (i = 0; i < digits_size; i++)
+        g[i] = m_HpulseShape_cis[(i - (digits_size - 9) / 2) * 25 + 100 + OptFilterPha];
+  else if (OptFilterGain == 0)
+    for (i = 0; i < digits_size; i++)
+      g[i] = m_LpulseShape_phys[(i - (digits_size - 9) / 2) * 25 + 100 + OptFilterPha];
+  else
+    for (i = 0; i < digits_size; i++)
+      g[i] = m_HpulseShape_phys[(i - (digits_size - 9) / 2) * 25 + 100 + OptFilterPha];
+
+  ATH_MSG_VERBOSE( "OptFilterPha=" << OptFilterPha << ((m_idocis) ? " cis" : " ") );
+
+  if (m_of2) {
+    OptFilterPed = 0.;
+    for (i = 0; i < digits_size; i++) {
+      OptFilterEne += a[i] * OptFilterDigits[i];
+      OptFilterTime += b[i] * OptFilterDigits[i];
+      OptFilterPed += c[i] * OptFilterDigits[i];
+    }
+  } else {
+    for (i = 0; i < digits_size; i++) {
+      OptFilterEne += a[i] * (OptFilterDigits[i] - OptFilterPed);
+      OptFilterTime += b[i] * (OptFilterDigits[i] - OptFilterPed);
+    }
+  }
+
+  bool goodEne = (fabs(OptFilterEne) > 1.0e-04);
+  if (goodEne) {
+    if (msgLvl(MSG::VERBOSE)) {
+      msg(MSG::VERBOSE) << "OptFilterEne=" << OptFilterEne << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterTime*OptFilterEne=" << OptFilterTime << endmsg;
+    }
+    OptFilterTime /= OptFilterEne;
+  } else {
+    if (msgLvl(MSG::VERBOSE)) {
+      msg(MSG::VERBOSE) << "OptFilterEne=" << OptFilterEne
+                        << "   ... assuming 0.0" << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterTime*OptFilterEne=" << OptFilterTime
+                        << "   ... assuming 0.0" << endmsg;
+    }
+    OptFilterTime = OptFilterEne = 0.0;
+  }
+
+  for (i = 0; i < digits_size; i++) {
+    if (OptFilterDigits[i] > 0)
+      OptFilterChi2 += 50 * fabs(OptFilterDigits[i] - (OptFilterEne * g[i] + OptFilterPed))
+          / OptFilterDigits[i];
+    else
+      OptFilterChi2 += 50 * fabs(OptFilterDigits[i] - (OptFilterEne * g[i] + OptFilterPed)) / 1024.;
+  }
+
+  if (fabs(OptFilterChi2) > 1.0e-04 || goodEne) {
+    if (msgLvl(MSG::VERBOSE)) {
+      msg(MSG::VERBOSE) << "OptFilterTime=" << OptFilterTime << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterPed=" << OptFilterPed << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterChi2=" << OptFilterChi2 << endmsg;
+    }
+  } else {
+    if (msgLvl(MSG::VERBOSE)) {
+      msg(MSG::VERBOSE) << "OptFilterTime=" << OptFilterTime << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterPed=" << OptFilterPed << endmsg;
+      msg(MSG::VERBOSE) << "OptFilterChi2=" << OptFilterChi2
+                        << "   ... assuming 0.0" << endmsg;
+    }
+    OptFilterChi2 = 0.0;
+  }
+
+  return OptFilterChi2;
+}
+
+
+void TileRawChannelBuilderOptFilter::BuildPulseShape(vector<double> &m_pulseShape,
+    vector<double> &m_pulseShapeX, vector<double> &m_pulseShapeT, int dignum) {
+
+  ATH_MSG_DEBUG( "TileRawChannelBuilderOptFilter::BuildPulseShape");
+
+//1: set m_pulseShape
+  int shape_size = (dignum - 1) * 25 + 200;
+  m_pulseShape.resize(shape_size);
+  ATH_MSG_DEBUG( "Set dimension of m_pulseShape to shape_sie=" << shape_size);
+
+//2: scan m_pulseShapeT for: tmin, tmax, nt0 and size: m_pulseShapeX[nt0]=1.0;
+  int nt0 = 0, size;
+  double tmin = 10000., tmax = -10000.;
+  size = m_pulseShapeT.size();
+  for (int i = 0; i < size; i++) {
+    if (m_pulseShapeT[i] < tmin) tmin = m_pulseShapeT[i];
+    if (m_pulseShapeT[i] > tmax) tmax = m_pulseShapeT[i];
+    if (m_pulseShapeT[i] == 0) nt0 = i;
+  }
+
+  ATH_MSG_DEBUG( "m_pulseShapeX & m_pulseShapeT size =" << size
+                << ", tmin=" << tmin
+                << ", tmax=" << tmax
+                << "  nt0=" << nt0
+                << " m_pulseShapeT[nt0]=" << m_pulseShapeT[nt0]
+                << " m_pulseShapeX[nt0]=" << m_pulseShapeX[nt0] );
+
+//3: fill m_pulseShape
+  bool exact;
+  int nminn, nminp;
+  double minn, minp, tdist;
+  m_pulseShape[(shape_size) / 2] = m_pulseShapeX[nt0];
+//  for (int i=1;i<(shape_size)/2+1;i++){
+  for (int i = 1; i < (shape_size) / 2; i++) {
+    // negative times: 0->(shape_size-1)/2
+    if (-i < tmin)
+      m_pulseShape[(shape_size) / 2 - i] = 0.;
+    else {
+      exact = false;
+      minn = -10000.;
+      minp = 10000.;
+      nminn = 0;
+      nminp = size - 1;
+      for (int j = 0; j < nt0 + 1 && !exact; j++) {
+        if (m_pulseShapeT[j] == double(-i)) {
+          m_pulseShape[(shape_size) / 2 - i] = m_pulseShapeX[j];
+          exact = true;
+        } else {
+          tdist = m_pulseShapeT[j] - double(-i);
+          if (tdist < 0. && tdist > minn) {
+            minn = tdist;
+            nminn = j;
+          }
+          if (tdist > 0. && tdist < minp) {
+            minp = tdist;
+            nminp = j;
+          }
+        }
+      }
+
+      if (exact) {
+        ATH_MSG_VERBOSE( "exact value found for time=" << -i
+                        << " m_pulseShape=" << m_pulseShape[(shape_size) / 2 - i]);
+      } else {
+        ATH_MSG_VERBOSE( "exact value NOT found for time=" << -i
+                        << " nminn=" << nminn
+                        << " m_pulseShapeT="
+                        << m_pulseShapeT[nminn]
+                        << " m_pulseShapeX=" << m_pulseShapeX[nminn] << std::endl
+                        << " nminp=" << nminp
+                        << " m_pulseShapeT="
+                        << m_pulseShapeT[nminp]
+                        << " m_pulseShapeX=" << m_pulseShapeX[nminp]);
+
+        m_pulseShape[(shape_size) / 2 - i] = m_pulseShapeX[nminn]
+            + (m_pulseShapeX[nminp] - m_pulseShapeX[nminn])
+                / (m_pulseShapeT[nminp] - m_pulseShapeT[nminn]) * (-i - m_pulseShapeT[nminn]);
+      }
+
+    }
+
+    // positive times: (shape_size-1)/2->shape_size
+    if (i > tmax)
+      m_pulseShape[(shape_size) / 2 + i] = 0.;
+    else {
+      exact = false;
+      minn = -10000.;
+      minp = 10000.;
+      nminn = 0;
+      nminp = size;
+      for (int j = nt0; j < size && !exact; j++) {
+        if (m_pulseShapeT[j] == double(i)) {
+          m_pulseShape[(shape_size) / 2 + i] = m_pulseShapeX[j];
+          exact = true;
+        } else {
+          tdist = m_pulseShapeT[j] - double(i);
+          if (tdist < 0) if (tdist > minn) {
+            minn = tdist;
+            nminn = j;
+          }
+          if (tdist > 0) if (tdist < minp) {
+            minp = tdist;
+            nminp = j;
+          }
+        }
+      }
+      if (exact) {
+        ATH_MSG_VERBOSE( "exact value found for time=" << i
+                        << " m_pulseShape=" << m_pulseShape[(shape_size) / 2 + i]);
+      } else {
+        ATH_MSG_VERBOSE( "exact value NOT found for time=" << i
+                        << " nminn=" << nminn
+                        << " m_pulseShapeT=" << m_pulseShapeT[nminn]
+                        << " m_pulseShapeX=" << m_pulseShapeX[nminn] << std::endl
+                        << " nminp=" << nminp
+                        << " m_pulseShapeT=" << m_pulseShapeT[nminp]
+                        << " m_pulseShapeX=" << m_pulseShapeX[nminp] );
+
+        m_pulseShape[(shape_size) / 2 + i] = m_pulseShapeX[nminn]
+            + (m_pulseShapeX[nminp] - m_pulseShapeX[nminn])
+                / (m_pulseShapeT[nminp] - m_pulseShapeT[nminn]) * (i - m_pulseShapeT[nminn]);
+      }
+    }
+  }
+
+//   if (msgLvl(MSG::VERBOSE)) {
+//    for (int i = 1; i < shape_size; i++) {
+//      msg(MSG::VERBOSE) << "shape[" << i << "]=" << m_pulseShape[i] << endmsg;
+//    }
+//  }
+}
+
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelMaker.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelMaker.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..9b06f92505b1b4426feafab72ff8afddd4f25ae1
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelMaker.cxx
@@ -0,0 +1,217 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+#include "GaudiKernel/ListItem.h"
+#include "GaudiKernel/ToolHandle.h"
+
+// Atlas includes
+#include "AthenaKernel/errorcheck.h"
+
+// access all RawChannels inside container
+#include "EventContainers/SelectAllObject.h"
+
+// Tile includes
+#include "TileEvent/TileDigitsContainer.h"
+#include "TileRecUtils/TileRawChannelBuilder.h"
+#include "TileRecUtils/TileRawChannelMaker.h"
+
+#include <algorithm>
+#include <set>
+
+/**
+ * Standard constructor
+ * @param name Name of algorithm
+ * @param pSvcLocator Service locator
+ */
+TileRawChannelMaker::TileRawChannelMaker(const std::string& name,
+    ISvcLocator* pSvcLocator)
+    : AthAlgorithm(name, pSvcLocator)
+    , m_TileDigitsContainerID("TileDigitsCnt")
+    , m_tileRawChannelBuilderList()
+    , m_fitOverflow(false)
+{
+  // declareProperty("TileRawChannelBuilder",m_TileRawChannelBuilderIDVec);
+  declareProperty("TileDigitsContainer", m_TileDigitsContainerID);
+  declareProperty("TileRawChannelBuilder", m_tileRawChannelBuilderList, "List Of Tools");
+  declareProperty("FitOverflow", m_fitOverflow, "Fit or not overflows");
+  declareProperty("TileRawChannelBuilderFitOverflow", m_tileRawChannelBuilderFitOverflow, "Tool to fit overflows");
+}
+
+/**
+ * Destructor
+ */
+TileRawChannelMaker::~TileRawChannelMaker() {
+}
+
+/**
+ * Initialize algorithm
+ */
+StatusCode TileRawChannelMaker::initialize() {
+
+  ATH_MSG_DEBUG( "starting to retrieve list " << m_tileRawChannelBuilderList);
+  CHECK( m_tileRawChannelBuilderList.retrieve());
+
+  ATH_MSG_DEBUG( m_tileRawChannelBuilderList << "retrieved");
+
+  if (m_tileRawChannelBuilderList.begin() == m_tileRawChannelBuilderList.end() ) {
+    ATH_MSG_INFO( "TileRawChannelBuilder list is empty - will not do anything");
+    m_fitOverflow = false;
+  }
+  
+  if (m_fitOverflow) {
+    CHECK( m_tileRawChannelBuilderFitOverflow.retrieve() );
+  }
+
+  m_overflowReplaceTimeCut = 50.0;
+  m_overflowReplacePedestalCut  = 170.0;
+  m_overflowReplaceChi2Cut = 40000.0;  
+
+  ATH_MSG_INFO( "Initialization completed successfully");
+
+  return StatusCode::SUCCESS;
+}
+
+/**
+ * Execute
+ */
+StatusCode TileRawChannelMaker::execute() {
+
+  // get named TileDigitsContaner from TES
+  const TileDigitsContainer *digiCnt;
+  if (evtStore()->retrieve(digiCnt, m_TileDigitsContainerID).isFailure()) {
+    ATH_MSG_WARNING( "Can't retrieve TileDigitsContainer '"
+                    << m_TileDigitsContainerID << "' from TDS" );
+
+    return StatusCode::SUCCESS;
+  }
+
+  ATH_MSG_DEBUG( "Got TileDigitsContainer '" << m_TileDigitsContainerID << "'" );
+
+  ToolHandleArray<TileRawChannelBuilder>::const_iterator itRChB =
+      m_tileRawChannelBuilderList.begin();
+  ToolHandleArray<TileRawChannelBuilder>::const_iterator itRChBEnd =
+      m_tileRawChannelBuilderList.end();
+
+  // create  RawChannel Containers for all sub-algs
+  for (; itRChB != itRChBEnd; ++itRChB) {
+    CHECK( (*itRChB)->createContainer() );
+  }
+
+  //make sure that we clean memory about errors in a drawer
+  TileRawChannelBuilder::resetDrawer();
+
+  // clean memory about overflows
+  if (m_fitOverflow) {
+    itRChB = m_tileRawChannelBuilderList.begin();
+    for (; itRChB != itRChBEnd; ++itRChB) {
+      (*itRChB)->resetOverflows();
+    }
+  }
+
+  // Iterate over all collections (drawers) with digits
+  TileDigitsContainer::const_iterator collItr = digiCnt->begin();
+  TileDigitsContainer::const_iterator lastColl = digiCnt->end();
+
+  for (; collItr != lastColl; ++collItr) {
+    const TileDigitsCollection * coll = (*collItr);
+
+    // Iterate over all sub-algs
+    itRChB = m_tileRawChannelBuilderList.begin();
+    for (; itRChB != itRChBEnd; ++itRChB) {
+      // reconstruct all channels in one drawer
+      (*itRChB)->build(coll);
+    }
+  }
+
+  if (m_fitOverflow
+      && !(*m_tileRawChannelBuilderList.begin())->getOverflowedChannels().empty()) {
+    fitOverflowedChannels();
+  }
+
+  // commit RawChannel Containers for all sub-algs
+  itRChB = m_tileRawChannelBuilderList.begin();
+  for (; itRChB != itRChBEnd; ++itRChB) {
+    CHECK( (*itRChB)->commitContainer() );
+  }
+
+  ATH_MSG_DEBUG( "execute completed successfully" );
+
+  return StatusCode::SUCCESS;
+}
+
+/**
+ *  Finalize
+ */
+StatusCode TileRawChannelMaker::finalize() {
+
+  ATH_MSG_INFO(" finalize completed successfully" );
+
+  return StatusCode::SUCCESS;
+}
+
+void TileRawChannelMaker::fitOverflowedChannels() {
+
+  ToolHandleArray<TileRawChannelBuilder>::const_iterator itRChB =
+      m_tileRawChannelBuilderList.begin();
+  ToolHandleArray<TileRawChannelBuilder>::const_iterator itRChBEnd =
+      m_tileRawChannelBuilderList.end();
+
+  // Iterate over all sub-algs  
+	itRChB = m_tileRawChannelBuilderList.begin();
+  for (; itRChB != itRChBEnd; ++itRChB) {
+
+    Overflows_t overflows = (*itRChB)->getOverflowedChannels();
+    Overflows_t::const_iterator itOverflow = overflows.begin();
+    for (; itOverflow != overflows.end(); ++itOverflow) {
+      TileRawChannel* rwCh = (*itOverflow).first;
+      TileDigits* pDigits = (*itOverflow).second;
+      TileRawChannel* fittedRwCh = m_tileRawChannelBuilderFitOverflow->rawChannel(pDigits);
+
+      bool fitOK = ( ( fabs(fittedRwCh->time()) < m_overflowReplaceTimeCut     ) &&
+                     ( fittedRwCh->pedestal()   < m_overflowReplacePedestalCut ) && 
+                     ( fittedRwCh->quality()    < m_overflowReplaceChi2Cut     ) );
+
+      int nSatSamples = 0;
+      std::vector<double> digits = pDigits->get_digits();
+      for (size_t ii = 0; ii<digits.size(); ii++) {
+        if (digits[ii] > 1022.9) nSatSamples++;
+      }
+                
+      if ( !fitOK || nSatSamples > 2)  {
+          // If the fit is bad, reset the energy. 
+          //The same if the number of saturated samples is 3 (or bigger)
+          
+          //If we reject pulse, the quality must be above 9999 in order to mask the
+          // channel. So we set it at 10000 * nSatSamples. But if the fit completely fails, 
+          // the quality is 100000, and we can keep this info in quality as well.
+          float quality = 10000. * ( (nSatSamples) ? nSatSamples : 9);
+          if (fittedRwCh->quality() > 99999.9) quality += 100000.;
+          rwCh->insert(0.0, 0.0, quality);
+
+          // 20000 - Indicates overflow, 6000 - indicates bad fit or >2 saturations.
+          // 30000 - Indicates overflow + underflow, 6000 - indicates bad fit or >2 saturations.
+          float pedestal = (rwCh->pedestal() < 29500.) ? (26000.)
+                                                       : (36000.);
+          rwCh->setPedestal(pedestal); 
+      } else {
+          //If the fit is OK replace
+          
+          //The range of the quality factor is very different in the fit (0-100k)
+          //than in the OF (0-255). So we rescale by a factor of 400.          
+          rwCh->insert(fittedRwCh->amplitude(), 
+                       fittedRwCh->time(), 
+                       fittedRwCh->quality()/400.);
+          // 20000 - Indicates overflow, 3000 - indicates fitted
+          // 30000 - Indicates overflow + underflow, 3000 - indicates fitted.
+          float pedestal = (rwCh->pedestal() < 29500.) ? (fittedRwCh->pedestal() + 23000.)
+                                                       : (fittedRwCh->pedestal() + 33000.);
+          rwCh->setPedestal(pedestal); 
+      }
+		
+    }
+  }
+
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelNoiseFilter.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelNoiseFilter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..bf6003fa7afb75a73420641a90ebf4f7669228b2
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelNoiseFilter.cxx
@@ -0,0 +1,328 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// Atlas includes
+#include "AthenaKernel/errorcheck.h"
+#include "GeoModelInterfaces/IGeoModelSvc.h"
+#include "Identifier/Identifier.h"
+
+// Tile includes
+#include "TileIdentifier/TileHWID.h"
+#define private public
+#include "TileEvent/TileRawChannel.h"
+#undef private
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileCalibBlobObjs/TileCalibUtils.h"
+#include "TileRecUtils/TileRawChannelNoiseFilter.h"
+
+static const InterfaceID IID_ITileRawChannelNoiseFilter(
+    "TileRawChannelNoiseFilter", 1, 0);
+
+const InterfaceID& TileRawChannelNoiseFilter::interfaceID() {
+  return IID_ITileRawChannelNoiseFilter;
+}
+
+//========================================================
+// constructor
+TileRawChannelNoiseFilter::TileRawChannelNoiseFilter(const std::string& type,
+    const std::string& name, const IInterface* parent)
+    : AthAlgTool(type, name, parent)
+    , m_tileHWID(0)
+    , m_tileToolEmscale("TileCondToolEmscale")
+    , m_tileToolNoiseSample("TileCondToolNoiseSample")
+    , m_tileBadChanTool("TileBadChanTool")
+    , m_beamInfo( "TileBeamInfoProvider/TileBeamInfoProvider")
+    , m_truncationThresholdOnAbsEinSigma(4.0) // 4 sigma of ADC HF noise by default
+    , m_minimumNumberOfTruncatedChannels(0.6) // at least 60% of channels should be below threshold
+    , m_useTwoGaussNoise(false) // do not use 2G - has no sense for ADC HF noise for the moment
+    , m_useGapCells(true) // use gap cells for noise filter as all normal cells
+{
+  declareInterface<ITileRawChannelTool>(this);
+  declareInterface<TileRawChannelNoiseFilter>(this);
+
+  declareProperty("TileCondToolEmscale", m_tileToolEmscale);
+  declareProperty("TileCondToolNoiseSample", m_tileToolNoiseSample);
+  declareProperty("TileBadChanTool", m_tileBadChanTool);
+  declareProperty("BeamInfo", m_beamInfo);
+
+  declareProperty("TruncationThresholdOnAbsEinSigma", m_truncationThresholdOnAbsEinSigma);
+  declareProperty("MinimumNumberOfTruncatedChannels", m_minimumNumberOfTruncatedChannels);
+  declareProperty("UseTwoGaussNoise", m_useTwoGaussNoise);
+  declareProperty("UseGapCells", m_useGapCells);
+}
+
+//========================================================
+// Initialize
+StatusCode TileRawChannelNoiseFilter::initialize() {
+  ATH_MSG_INFO("Initializing...");
+
+  if (msgLvl(MSG::DEBUG)) {
+      msg(MSG::DEBUG) << "TruncationThresholdOnAbsEinSigma = " 
+                      << m_truncationThresholdOnAbsEinSigma << endmsg;
+      msg(MSG::DEBUG) << "MinimumNumberOfTruncatedChannels = " 
+                      << m_minimumNumberOfTruncatedChannels << endmsg;
+      msg(MSG::DEBUG) << "UseTwoGaussNoise = " 
+                      << ((m_useTwoGaussNoise)?"true":"false") << endmsg;
+      msg(MSG::DEBUG) << "UseGapCells = " 
+                      << ((m_useGapCells)?"true":"false") << endmsg;
+  }
+
+  const IGeoModelSvc *geoModel = 0;
+  CHECK( service("GeoModelSvc", geoModel) );
+
+  // dummy parameters for the callback:
+  int dummyInt = 0;
+  std::list<std::string> dummyList;
+
+  if (geoModel->geoInitialized()) {
+    return geoInit(dummyInt, dummyList);
+  } else {
+    CHECK( detStore()->regFcn(&IGeoModelSvc::geoInit, geoModel
+          , &TileRawChannelNoiseFilter::geoInit, this));
+
+    ATH_MSG_INFO( "geoInit callback registered");
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// ============================================================================
+// delayed initialize
+StatusCode TileRawChannelNoiseFilter::geoInit(IOVSVC_CALLBACK_ARGS) {
+  ATH_MSG_INFO("Entering GeoInit");
+
+  CHECK( detStore()->retrieve(m_tileHWID) );
+
+  //=== get TileCondToolEmscale
+  CHECK( m_tileToolEmscale.retrieve() );
+
+  //=== get TileCondToolNoiseSample
+  CHECK( m_tileToolNoiseSample.retrieve() );
+
+  //=== get TileBadChanTool
+  CHECK( m_tileBadChanTool.retrieve() );
+
+  //=== get TileBeamInfo
+  CHECK( m_beamInfo.retrieve() );
+
+  ATH_MSG_INFO("geoInit() end");
+
+  return StatusCode::SUCCESS;
+}
+
+// ============================================================================
+// process container
+StatusCode TileRawChannelNoiseFilter::process(
+    const TileRawChannelContainer *rchCont) {
+
+  ATH_MSG_DEBUG("in process()");
+
+  TileRawChannelUnit::UNIT rChUnit = rchCont->get_unit();
+  std::string units[8] = { "ADC counts", "pC", "CspC", "MeV",
+      "online ADC counts", "online pC", "online CspC", "online MeV" };
+
+  if (rChUnit > TileRawChannelUnit::ADCcounts
+      && rChUnit < TileRawChannelUnit::OnlineADCcounts) {
+
+    ATH_MSG_ERROR( "Units in container is " << units[rChUnit] );
+    ATH_MSG_ERROR( "Due to non-linear CIS constants noise filter is possible only with ADC counts ");
+    ATH_MSG_ERROR( "Please, disable CIS calibration in optimal filter " );
+
+    return StatusCode::FAILURE;
+  }
+
+  bool undoOnlCalib = (rChUnit > TileRawChannelUnit::OnlineADCcounts);
+  ATH_MSG_VERBOSE( "Units in container is " << units[rChUnit] );
+
+  // Now retrieve the TileDQStatus
+  const TileDQstatus* DQstatus = m_beamInfo->getDQstatus();
+
+  TileRawChannelContainer::const_iterator collItr = rchCont->begin();
+  TileRawChannelContainer::const_iterator lastColl = rchCont->end();
+  for (; collItr != lastColl; ++collItr) {
+
+    /* Get drawer ID and build drawer index. */
+    HWIdentifier drawer_id = m_tileHWID->drawer_id((*collItr)->identify());
+    int ros = m_tileHWID->ros(drawer_id);
+    int drawer = m_tileHWID->drawer(drawer_id);
+    unsigned int drawerIdx = TileCalibUtils::getDrawerIdx(ros, drawer);
+    bool eb = (ros > 2);
+    bool ebspD4 = ((ros == 3 && drawer == 14) || (ros == 4 && drawer == 17));
+    bool ebNsp  = !ebspD4 && eb;
+    bool ebspC10 = (ebNsp && ((drawer>37 && drawer<42) || (drawer>53 && drawer<58) ) );
+
+    static const int maxChannelDrawer = 48; // number of channels in one drawer
+    static const int maxChannel = 12; // number of channels per motherboard
+    static const int maxMOB = 4; // number of motherboards in one drawer
+
+    float calib[maxChannelDrawer];
+    float commonmode[maxMOB];
+    int nemptychan[maxMOB];
+    int ngoodchan[maxMOB];
+    int chanmap[maxChannelDrawer];
+    memset(calib, 0, sizeof(calib));
+    memset(commonmode, 0, sizeof(commonmode));
+    memset(nemptychan, 0, sizeof(nemptychan));
+    memset(ngoodchan, 0, sizeof(ngoodchan));
+    memset(chanmap, 0, sizeof(chanmap));
+
+    // iterate over all channels in a collection
+    TileRawChannelCollection::const_iterator rchItr = (*collItr)->begin();
+    TileRawChannelCollection::const_iterator lastRch = (*collItr)->end();
+
+    for (; rchItr != lastRch; ++rchItr) {
+      TileRawChannel* rch = (*rchItr);
+
+      HWIdentifier adc_id = rch->adc_HWID();
+      //int index,pmt;
+      //Identifier cell_id = rch->cell_ID_index(index,pmt);
+      //if ( index == -1 ) continue; // this is to ignore disconnected channels - just for tests
+      //if ( index < 0 )   continue; // this is to ingnore disconnected channels and MBTS - just for tests
+
+      int chan = m_tileHWID->channel(adc_id);
+      int gain = m_tileHWID->adc(adc_id);
+      int mob = chan / maxChannel;
+      bool empty = (eb && ( (chan > 41) || (chan > 23 && chan < 30) || (ebspD4 && chan < 3) ) );
+
+      // use only good channel
+      float ped=rch->pedestal();
+      if (empty || ped > 55000. || (ped>2000. && ped < 39500.) // all bad patterns, ped=2047, underflow, overflow
+          || m_tileBadChanTool->getAdcStatus(drawerIdx, chan, gain).isBad()
+          || (!DQstatus->isAdcDQgood(ros, drawer, chan, gain))) continue;
+
+      bool usechan = m_useGapCells ||  // always true if we want to use gap cells
+                   ( ! ( ( ebNsp && (chan==0 || chan==1  || chan==12 || chan==13)) ||
+                         ( ebspC10 && (chan==4 || chan==5)) ||
+                         ( ebspD4 && (chan==18 || chan==19 || chan==12 || chan==13)) ) );
+
+      ++chanmap[chan];
+      // do not count good channels twice
+      if (chanmap[chan] < 2 && usechan) ++ngoodchan[mob];
+      // use only high gain
+      if (gain != TileHWID::HIGHGAIN) continue;
+
+      float amp = rch->amplitude();
+      if (undoOnlCalib) {
+        calib[chan] = m_tileToolEmscale->undoOnlCalib(drawerIdx, chan, gain, 1.0, rChUnit);
+        amp *= calib[chan];
+      } else {
+        calib[chan] = 1.0;
+      }
+
+      if (usechan) {
+
+        float noise_sigma = 1.5; // default value of HFN in high gain channel
+        if (m_useTwoGaussNoise) {
+          //float sigma1 = m_tileToolNoiseSample->getHfn1(drawerIdx, chan, gain);
+          //float sigma2 = m_tileToolNoiseSample->getHfn2(drawerIdx, chan, gain);
+          //float norm   = m_tileToolNoiseSample->getHfnNorm(drawerIdx, chan, gain);
+          // still need to define noise_sigma in this case 
+          // noise_sigma = ...
+        } else {
+          // take single gauss noise sigma from DB (high frequency noise)
+          noise_sigma = m_tileToolNoiseSample->getHfn(drawerIdx, chan, gain);
+        }
+
+        float significance = 999.999;
+        if (noise_sigma != 0.0) {
+          significance = fabs(amp / noise_sigma); // caluclate signal/noise ratio
+        } else {
+          --ngoodchan[mob]; // ignore completely channels with zero sigma
+        }
+
+        ATH_MSG_VERBOSE( "HWID " << m_tileHWID->to_string(adc_id)
+                         << " calib " << 1. / calib[chan]
+                         << " amp " << amp
+                         << " noise " << noise_sigma
+                         << " significance " << significance );
+
+        if (significance > m_truncationThresholdOnAbsEinSigma) continue;
+
+        commonmode[mob] += amp;
+        ++nemptychan[mob];
+
+      } else {
+
+        ATH_MSG_VERBOSE( "HWID " << m_tileHWID->to_string(adc_id)
+                         << " calib " << 1. / calib[chan]
+                         << " amp " << amp
+                         << " channel is not used" );
+      }
+      
+    }
+
+    int ncorr = 0;
+    int nchmin = m_minimumNumberOfTruncatedChannels;
+
+    for (int k = 0; k < maxMOB; k++) {
+
+      if (m_minimumNumberOfTruncatedChannels < 1.0) {
+        nchmin = ceil(m_minimumNumberOfTruncatedChannels * ngoodchan[k]);
+        if (nchmin < 2) nchmin = 2;
+      }
+
+      if (nemptychan[k] >= nchmin) {
+        commonmode[k] /= nemptychan[k];
+        ++ncorr;
+
+        ATH_MSG_VERBOSE( "ros " << ros
+                        << " drawer " << std::setw(2) << drawer
+                        << " mb " << k << " mean " << commonmode[k]
+                        << " taken from " << nemptychan[k] << " channels"
+                        << " nchgood " << ngoodchan[k]
+                        << " nchmin " << nchmin );
+
+      } else {
+        if (msgLvl(MSG::VERBOSE)) {
+          if (commonmode[k] != 0.0) {
+            msg(MSG::VERBOSE) << "ros " << ros
+                              << " drawer " << std::setw(2) << drawer
+                              << " mb " << k
+                              << " mean is zero instead of " << commonmode[k] << " / " << nemptychan[k]
+                              << " nchgood " << ngoodchan[k]
+                              << " nchmin " << nchmin
+                              << endmsg;
+          } else {
+            msg(MSG::VERBOSE) << "ros "
+                              << ros << " drawer " << std::setw(2) << drawer
+                              << " mb " << k
+                              << " mean is zero - nothing to correct"
+                              << " nchgood " << ngoodchan[k]
+                              << " nchmin " << nchmin
+                              << endmsg;
+          }
+        }
+        commonmode[k] = 0.0;
+      }
+    }
+
+    if (ncorr == 0) continue; // nothing to correct
+
+    // iterate over all channels in a collection again
+    for (rchItr = (*collItr)->begin(); rchItr != lastRch; ++rchItr) {
+      TileRawChannel* rch = (*rchItr);
+      int chan = m_tileHWID->channel(rch->adc_HWID());
+      int gain = m_tileHWID->adc(rch->adc_HWID());
+
+      // use only good channel and high gain - for them calib was set to non-zero value above
+      if (calib[chan] > 0.0 && gain == TileHWID::HIGHGAIN) {
+        // correct amplitude directly in channel
+        // (will change this to set() method once it is available in TileRawChannel)
+        int mob = chan/maxChannel;
+        if (undoOnlCalib) rch->m_amplitude[0] -= commonmode[mob] / calib[chan];
+        else rch->m_amplitude[0] -= commonmode[mob];
+        rch->m_pedestal += commonmode[mob];
+      }
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+// ============================================================================
+// finalize
+StatusCode TileRawChannelNoiseFilter::finalize() {
+  return StatusCode::SUCCESS;
+}
+
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelVerify.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelVerify.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..51024e36bae8441e42465f2edb7067983134c7f9
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelVerify.cxx
@@ -0,0 +1,208 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//*****************************************************************************
+//  Filename : TileRawChannelVerify.cxx
+//  Author   : Zhifang
+//  Created  : May, 2002
+//
+//  DESCRIPTION:
+//     Implement the TileRawChannelVerify class
+//
+//  HISTORY:
+//
+//  BUGS:
+//
+//*****************************************************************************
+
+// Gaudi includes
+#include "GaudiKernel/Bootstrap.h"
+#include "GaudiKernel/ISvcLocator.h"
+
+// Atlas includes
+// access all RawChannels inside container
+#include "EventContainers/SelectAllObject.h" 
+#include "AthenaKernel/errorcheck.h"
+
+// Tile includes
+#include "TileIdentifier/TileHWID.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileRecUtils/TileRawChannelVerify.h"
+
+// C++ STL includes
+#include <vector>
+#include <algorithm>
+
+// C includes
+#include <cmath>
+
+using namespace std;
+
+
+/**
+@class CompRawChannel
+@brief Small class holding a single method to compare two different TileRawChannel  
+ 
+*/
+class CompRawChannel: public binary_function<const TileRawChannel*, const TileRawChannel*, bool> {
+  public:
+    bool operator()(const TileRawChannel* p1, const TileRawChannel* p2) {
+      return p1->amplitude() < p2->amplitude();
+    }
+};
+
+//==========================================================================
+// TileRawChannelVerify's implementations
+//==========================================================================
+
+// Constructor
+TileRawChannelVerify::TileRawChannelVerify(string name, ISvcLocator* pSvcLocator)
+  : AthAlgorithm(name, pSvcLocator)
+  , m_tileHWID(0)
+{
+  declareProperty("TileRawChannelContainer1", m_rawChannelContainer1 = "TileRawChannelContainer1");
+  declareProperty("TileRawChannelContainer2", m_rawChannelContainer2 = "TileRawChannelContainer2");
+  declareProperty("Precision", m_precision = 0);
+  declareProperty("DumpRawChannels", m_dumpRawChannels = false);
+  declareProperty("SortFlag", m_sortFlag = false);
+}
+
+TileRawChannelVerify::~TileRawChannelVerify() {
+}
+
+// Alg standard interfacw function
+StatusCode TileRawChannelVerify::initialize() {
+
+  // retrieve TileHWID helper from det store
+  CHECK( detStore()->retrieve(m_tileHWID) );
+
+  ATH_MSG_INFO( "TileRawChannelVerify initialization completed" );
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TileRawChannelVerify::execute() {
+
+  // step1: read two cell containers from TES
+  const TileRawChannelContainer* pRawChannels1;
+  const TileRawChannelContainer* pRawChannels2;
+  CHECK( evtStore()->retrieve(pRawChannels1, m_rawChannelContainer1) );
+  CHECK( evtStore()->retrieve(pRawChannels2, m_rawChannelContainer2) );
+
+  SelectAllObject<TileRawChannelContainer> selAll1(pRawChannels1);
+  SelectAllObject<TileRawChannelContainer>::const_iterator rawItr1 = selAll1.begin();
+  SelectAllObject<TileRawChannelContainer>::const_iterator end1 = selAll1.end();
+
+  SelectAllObject<TileRawChannelContainer> selAll2(pRawChannels2);
+  SelectAllObject<TileRawChannelContainer>::const_iterator rawItr2 = selAll2.begin();
+  SelectAllObject<TileRawChannelContainer>::const_iterator end2 = selAll2.end();
+
+  // step2: first compare the number of cells in the two containers 
+  int nSize1 = 0;
+  for (; rawItr1 != end1; ++rawItr1) ++nSize1;
+
+  int nSize2 = 0;
+  for (; rawItr2 != end2; ++rawItr2) ++nSize2;
+
+  ATH_MSG_INFO( "The number of cells in " << m_rawChannelContainer1 << " is " << nSize1 );
+  ATH_MSG_INFO( "The number of cells in " << m_rawChannelContainer2 << " is " << nSize2 );
+
+  if (nSize1 != nSize2) {
+    ATH_MSG_ERROR( "The number of rawChannels is not equal in the two containers" );
+    return (StatusCode::SUCCESS);
+  }
+
+  // step3: to sort the cells in the containers by amplitude
+  vector<const TileRawChannel*> v1;
+  vector<const TileRawChannel*> v2;
+  const TileRawChannel* p1;
+  const TileRawChannel* p2;
+  if (m_sortFlag) {
+    rawItr1 = selAll1.begin();
+    end1 = selAll1.end();
+    for (; rawItr1 != end1; ++rawItr1)
+      v1.push_back((*rawItr1));
+
+    sort(v1.begin(), v1.end(), CompRawChannel());
+
+    rawItr2 = selAll2.begin();
+    end2 = selAll2.end();
+    for (; rawItr2 != end2; ++rawItr2)
+      v2.push_back((*rawItr2));
+
+    sort(v2.begin(), v2.end(), CompRawChannel());
+  }
+
+  rawItr1 = selAll1.begin();
+  end1 = selAll1.end();
+
+  rawItr2 = selAll2.begin();
+  end2 = selAll2.end();
+
+  // step4: then compare every cell-pair in the containers
+  bool bErrorFlag = false;
+  bool bHeaderFlag = true;
+  for (int i = 0; i < nSize1; ++i) {
+    if (m_sortFlag) {
+      p1 = v1[i];
+      p2 = v2[i];
+    } else {
+      p1 = (*rawItr1);
+      ++rawItr1;
+      p2 = (*rawItr2);
+      ++rawItr2;
+    }
+    HWIdentifier id1 = p1->adc_HWID();
+    HWIdentifier id2 = p2->adc_HWID();
+    double amp1 = p1->amplitude();
+    double amp2 = p2->amplitude();
+    double diff = fabs(amp1 - amp2);
+    if (id1 != id2 || diff > m_precision) bErrorFlag = true;
+    if (msgLvl(MSG::VERBOSE) && (m_dumpRawChannels || bErrorFlag)) {
+      if (bHeaderFlag) {
+        msg(MSG::VERBOSE) << "             ===" << m_rawChannelContainer1 << "===      ===" << m_rawChannelContainer2 << "===" << endmsg;
+        msg(MSG::VERBOSE) << "  Index      e1            id1        |        e2           id2" << endmsg;
+        msg(MSG::VERBOSE) << "--------------------------------------------------------------------------------" << endmsg;
+        bHeaderFlag = false;
+      }
+      msg(MSG::VERBOSE) << setw(5) << i
+                        << "   " << setw(12)  << amp1
+                        << "   [" << m_tileHWID->to_string(id1) << "]"
+                        << "  |  " << setw(12) << amp2
+                        << "   [" << m_tileHWID->to_string(id2) << "]";
+      if (diff > m_precision) {
+        msg(MSG::VERBOSE) << " A* ";
+      }
+      if (id1 != id2) {
+        msg(MSG::VERBOSE) << " I* ";
+      }
+      msg(MSG::VERBOSE) << endmsg;
+    } else if (bErrorFlag) {
+      break;
+    }
+  }
+  if (!bHeaderFlag) {
+    msg(MSG::VERBOSE) << "--------------------------------------------------------------------------------" << endmsg;
+  }
+  if (!bErrorFlag) {
+    ATH_MSG_INFO( "The two cellContainers (" << m_rawChannelContainer1
+                 << " and " << m_rawChannelContainer2 << ") are the same!!!" );
+  } else {
+    ATH_MSG_INFO( "The two cellContainers (" << m_rawChannelContainer1
+                 << " and " << m_rawChannelContainer2 << ") are not the same!!!" );
+  }
+
+  // Execution completed.
+  ATH_MSG_INFO( "TileRawChannelVerify execution completed successfully" );
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TileRawChannelVerify::finalize() {
+
+  ATH_MSG_INFO( "TileRawChannelVerify finalized successfully" );
+
+  return StatusCode::SUCCESS;
+}
+
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawCorrelatedNoise.cxx b/TileCalorimeter/TileRecUtils/src/TileRawCorrelatedNoise.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..32a7af3611d64baf84b1d46bfb9f2035087d4f18
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawCorrelatedNoise.cxx
@@ -0,0 +1,429 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+#include "GaudiKernel/ListItem.h"
+
+// Atlas includes
+// access all RawChannels inside container
+#include "EventContainers/SelectAllObject.h"
+#include "AthenaKernel/errorcheck.h"
+
+// Tile includes
+#include "TileRecUtils/TileRawCorrelatedNoise.h"
+#include "TileEvent/TileDigitsContainer.h"
+#include "TileEvent/TileDigits.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileConditions/TileInfo.h"
+
+#include "PathResolver/PathResolver.h"
+
+// #############################################################################
+TileRawCorrelatedNoise::TileRawCorrelatedNoise(const std::string& name, ISvcLocator* pSvcLocator)
+  : AthAlgorithm(name, pSvcLocator)
+  , m_TileDigitsInputContainer("TileDigitsCnt")
+  , m_TileDigitsOutputContainer("NewDigitsContainer")
+{
+// #############################################################################
+
+  declareProperty("TileDigitsContainer",m_TileDigitsInputContainer);
+  declareProperty("TileDigitsOutputContainer",m_TileDigitsOutputContainer);
+  declareProperty("nRMSThreshold", m_nRMS_threshold = 2.);
+  declareProperty("AlphaMatrixFilePrefix", m_AlphaMatrixFilePrefix  = "AlphaMatrix");
+  declareProperty("MeanFilePrefix", m_MeanFilePrefix  = "Mean");
+  declareProperty("Sample3RMSFilePrefix", m_Sample3RMSFilePrefix  = "RMS");
+  declareProperty("UseMeanFiles", m_useMeanFiles = true);
+  declareProperty("PMTOrder", m_pmtOrder = false);
+}
+
+// #############################################################################
+TileRawCorrelatedNoise::~TileRawCorrelatedNoise() {
+// #############################################################################
+
+}
+
+// #############################################################################
+StatusCode TileRawCorrelatedNoise::initialize() {
+// #############################################################################
+
+  int PmtToChannelBarrel[48] = {
+   1,  2,  3,  4,  5,  6,  7,  8,
+   9, 10, 11, 12, 13, 14, 15, 16,
+  17, 18, 19, 20, 21, 22, 23, 24,
+  27, 26, 25, 30, 29, 28, 33, 32,
+  31, 36, 35, 34, 39, 38, 37, 42,
+  41, 40, 45, 44, 43, 48, 47, 46 };
+
+  int PmtToChannelExtendedBarrel[48] = {
+   1,  2,  3,  4,  5,  6,  7,  8,
+   9, 10, 11, 12, 13, 14, 15, 16,
+  17, 18, 19, 20, 21, 22, 23, 24,
+  27, 26, 25, 31, 32, 28, 33, 29,
+  30, 36, 35, 34, 44, 38, 37, 43,
+  42, 41, 45, 39, 40, 48, 47, 46 };
+
+  // read alpha matrix
+  FILE* AlphaMatrixFile[4][64];
+  char Rosstr[10];
+  char buff[1000];
+  // Cycle over 4 partitions and 64 modules
+  for (int Ros = 1; Ros < 5; ++Ros) {
+    for (int Drawer = 0; Drawer < 64; ++Drawer) {
+      // open file which corresponds to AlphaMatrix[Ros][Drawer]
+      if (Ros == 1) sprintf(Rosstr, "LBA");
+      else if (Ros == 2) sprintf(Rosstr, "LBC");
+      else if (Ros == 3) sprintf(Rosstr, "EBA");
+      else sprintf(Rosstr, "EBC");
+
+      sprintf(buff, "%s%s%02d.txt", m_AlphaMatrixFilePrefix.c_str(), Rosstr, Drawer + 1);
+      std::string filestr(buff);
+      std::string file = PathResolver::find_file(buff, "DATAPATH");
+//      ATH_MSG_INFO( "  buff=" << buff
+//                   << " filestr=" << filestr.c_str()
+//                   << " file=" << file.c_str() );
+      if (file.size() == 0) {
+        ATH_MSG_WARNING( "Could not find input file " << buff );
+      } else {
+        ATH_MSG_INFO( "Reading file  " << file );
+        AlphaMatrixFile[Ros - 1][Drawer] = fopen(file.c_str(), "r");
+      }
+      if (AlphaMatrixFile[Ros - 1][Drawer] == NULL) {
+        ATH_MSG_ERROR( "Can't read input Alpha Matrix files." );
+        return StatusCode::FAILURE;
+      }
+      ATH_MSG_DEBUG( " **** Start of Alpha Matrix Read Out" );
+      FILE* alpha_file = AlphaMatrixFile[Ros - 1][Drawer];
+//      if(fgets(buff, sizeof(buff), alpha_file) != NULL) {
+//        ATH_MSG_DEBUG( "Matrix is being loaded: " << buff );
+//      }
+      // load tokens to be searched for in a string
+      char* word;
+      const char* TOKENS = { " \t\n" };
+      // read Matrix
+      int dima = 48;
+      for (int line = 0; line < dima; line++) {
+        if (fgets(buff, sizeof(buff), alpha_file) != NULL) {
+          ATH_MSG_DEBUG( "line " << line << " is " << buff );
+          for (int column = 0; column < dima; column++) {
+            // Check for comment lines
+            if (*buff == '!' || *buff == '*') continue;
+            // read value
+            int error = 0;
+            if (column == 0) {
+              if ((word = strtok(buff, TOKENS)) == NULL) error = 1;
+            } else {
+              if ((word = strtok(NULL, TOKENS)) == NULL) error = 1;
+            }
+
+            double pippo;
+            if (error) pippo = 0.;
+            else pippo = atof(word);
+
+            ATH_MSG_VERBOSE ( "elem " << column << " is " << pippo );
+            int chline = line;
+            int chcolumn = column;
+            // read alpha matrix in pmt order but save it in channel order if m_pmtOrder is true
+            if (m_pmtOrder) {
+              if (Ros < 3) {
+                chline = PmtToChannelBarrel[line] - 1;
+                chcolumn = PmtToChannelBarrel[column] - 1;
+              } else {
+                chline = PmtToChannelExtendedBarrel[line] - 1;
+                chcolumn = PmtToChannelExtendedBarrel[column] - 1;
+              }
+            }
+            AlphaMatrix[Ros - 1][Drawer][chline][chcolumn] = pippo;
+          }
+        }
+      }
+      fclose(alpha_file);
+    }
+  }
+
+  if (m_useMeanFiles) {
+    // read mean
+    int nSamples = 7;
+    FILE* MeanFile[4][64];
+    // cicle over 4 partitions and 64 modules
+    for (int Ros = 1; Ros < 5; ++Ros) {
+      for (int Drawer = 0; Drawer < 64; ++Drawer) {
+        // open file which corresponds to Mean[Ros][Drawer]
+        if (Ros == 1)
+          sprintf(Rosstr, "LBA");
+        else if (Ros == 2)
+          sprintf(Rosstr, "LBC");
+        else if (Ros == 3)
+          sprintf(Rosstr, "EBA");
+        else
+          sprintf(Rosstr, "EBC");
+        sprintf(buff, "%s%s%02d.txt", m_MeanFilePrefix.c_str(), Rosstr, Drawer + 1);
+        std::string filestr(buff);
+        std::string file = PathResolver::find_file(buff, "DATAPATH");
+        if (file.size() == 0) {
+          ATH_MSG_VERBOSE ( "Could not find input file " << buff );
+        } else {
+          ATH_MSG_INFO( "Reading file  " << file );
+          MeanFile[Ros - 1][Drawer] = fopen(file.c_str(), "r");
+        }
+        if (MeanFile[Ros - 1][Drawer] == NULL) {
+          ATH_MSG_ERROR( "Can't read input Mean files." );
+          return StatusCode::FAILURE;
+        }
+
+        ATH_MSG_DEBUG( " **** Start of Means Read Out" );
+        FILE* mean_file = MeanFile[Ros - 1][Drawer];
+        //if(fgets(buff, sizeof(buff), mean_file) != NULL) {
+        //  if (lDebug)
+        //    log << MSG::DEBUG << "Vector is being loaded: "<< buff << endreq;
+        //}
+        // load tokens to be searched for in a string
+        char* word;
+        const char* TOKENS = { " \t\n" };
+        // read Vector
+        int dima = 48;
+        for (int line = 0; line < dima; line++) {
+          if (fgets(buff, sizeof(buff), mean_file) != NULL) {
+            ATH_MSG_DEBUG( "line " << line << " is " << buff );
+            for (int Sample = 0; Sample < nSamples; Sample++) {
+              // Check for comment lines
+              if (*buff == '!' || *buff == '*') continue;
+              // read value
+              int error = 0;
+              if (Sample == 0) {
+                if ((word = strtok(buff, TOKENS)) == NULL) error = 1;
+              } else {
+                if ((word = strtok(NULL, TOKENS)) == NULL) error = 1;
+              }
+              double pippo;
+              if (error)
+                pippo = 0.;
+              else
+                pippo = atof(word);
+              ATH_MSG_VERBOSE ( "elem " << Sample << " is " << pippo );
+              int chline = line;
+              // read lines of mean matrix in pmt order but save it in channel order if m_pmtOrder is true
+              if (m_pmtOrder) {
+                if (Ros < 3) chline = PmtToChannelBarrel[line] - 1;
+                else chline = PmtToChannelExtendedBarrel[line] - 1;
+              }
+              MeanSamples[Ros - 1][Drawer][chline][Sample] = pippo;
+            }
+          }
+        }
+        fclose(mean_file);
+      }
+    }
+  } else {
+    // Initialize mean
+    int nSamples = 7;
+    for (int Ros = 1; Ros < 5; ++Ros) {
+      for (int Drawer = 0; Drawer < 64; ++Drawer) {
+        for (int Channel = 0; Channel < 48; ++Channel) {
+          for (int Sample = 0; Sample < nSamples; ++Sample) {
+            //MeanSamples[Ros-1][Drawer][Channel][Sample]=50.;
+            MeanSamples[Ros - 1][Drawer][Channel][Sample] = -1.;
+          }
+        }
+      }
+    }
+  }
+
+  // read sample 3 RMS
+  FILE* Sample3RMSFile[4][64];
+  // cicle over 4 partitions and 64 modules
+  for (int Ros = 1; Ros < 5; ++Ros) {
+    for (int Drawer = 0; Drawer < 64; ++Drawer) {
+      // open file which corresponds to Sample3RMS[Ros][Drawer]
+      if (Ros == 1) sprintf(Rosstr, "LBA");
+      else if (Ros == 2) sprintf(Rosstr, "LBC");
+      else if (Ros == 3) sprintf(Rosstr, "EBA");
+      else sprintf(Rosstr, "EBC");
+
+      sprintf(buff, "%s%s%02d.txt", m_Sample3RMSFilePrefix.c_str(), Rosstr, Drawer + 1);
+      std::string filestr(buff);
+      std::string file = PathResolver::find_file(buff, "DATAPATH");
+      if (file.size() == 0) {
+        ATH_MSG_VERBOSE ( "Could not find input file " << buff );
+      } else {
+        ATH_MSG_INFO( "Reading file  " << file );
+        Sample3RMSFile[Ros - 1][Drawer] = fopen(file.c_str(), "r");
+      }
+      if (Sample3RMSFile[Ros - 1][Drawer] == NULL) {
+        ATH_MSG_ERROR( "Can't read input sample 3 RMS files." );
+        return StatusCode::FAILURE;
+      }
+
+      ATH_MSG_DEBUG( " **** Start of sample 3 RMS Read Out" );
+      FILE* rms_file = Sample3RMSFile[Ros - 1][Drawer];
+      //if(fgets(buff, sizeof(buff), rms_file) != NULL) {
+      //  if (lDebug)
+      //    log << MSG::DEBUG << "Vector is being loaded: "<< buff << endreq;
+      //}
+      // load tokens to be searched for in a string
+      char* word;
+      const char* TOKENS = { " \t\n" };
+      // read Vector
+      int dima = 48;
+      for (int line = 0; line < dima; line++) {
+        if (fgets(buff, sizeof(buff), rms_file) != NULL) {
+          ATH_MSG_DEBUG(  "line " << line << " is " << buff );
+          // Check for comment lines
+          if (*buff == '!' || *buff == '*') continue;
+          // read value
+          int error = 0;
+          if ((word = strtok(buff, TOKENS)) == NULL) error = 1;
+          double pippo;
+          if (error) pippo = 0.;
+          else pippo = atof(word);
+          // read value
+          ATH_MSG_VERBOSE ( "elem is " << pippo );
+          int chline = line;
+          // read rms vector in pmt order but save it in channel order if m_pmtOrder is true
+          if (m_pmtOrder) {
+            if (Ros < 3) chline = PmtToChannelBarrel[line] - 1;
+            else chline = PmtToChannelExtendedBarrel[line] - 1;
+          }
+          Sample3RMS[Ros - 1][Drawer][chline] = pippo;
+        }
+      }
+      fclose(rms_file);
+    }
+  }
+
+  ATH_MSG_INFO( "Initialization completed successfully" );
+
+  return StatusCode::SUCCESS;
+}
+
+// #############################################################################
+StatusCode TileRawCorrelatedNoise::execute() {
+// #############################################################################
+
+  // get named TileDigitsContaner from TES
+  const TileDigitsContainer *digiCnt;
+  if (evtStore()->retrieve(digiCnt, m_TileDigitsInputContainer).isFailure()) {
+    ATH_MSG_WARNING( "Can't retrieve TileDigitsContainer '" << m_TileDigitsInputContainer << "' from TDS" );
+    return StatusCode::SUCCESS;
+  }
+
+  ATH_MSG_DEBUG( "Got TileDigitsContainer '" << m_TileDigitsInputContainer << "'" );
+
+  const TileHWID* tileHWID;
+  CHECK( detStore()->retrieve(tileHWID, "TileHWID") );
+
+  const TileInfo* tileInfo = 0;
+  if (!m_useMeanFiles) {
+    CHECK( detStore()->retrieve(tileInfo, "TileInfo") );
+  }
+
+  const TileDigits* OriginalDigits[4][64][48];
+
+  // go through ALL TileDigits in container
+  SelectAllObject<TileDigitsContainer> selAll(digiCnt);
+  SelectAllObject<TileDigitsContainer>::const_iterator digItr = selAll.begin();
+  SelectAllObject<TileDigitsContainer>::const_iterator lastDig = selAll.end();
+
+  // read digits
+  for (; digItr != lastDig; ++digItr) {
+    const HWIdentifier adc_HWID = (*digItr)->adc_HWID();
+    int Ros = tileHWID->ros(adc_HWID);
+    int Drawer = tileHWID->drawer(adc_HWID);
+    int Channel = tileHWID->channel(adc_HWID);
+    OriginalDigits[Ros - 1][Drawer][Channel] = (*digItr);
+
+    if (!m_useMeanFiles) {
+      // read pedestal value and use it as mean
+      double ped = tileInfo->DigitsPedLevel(adc_HWID);
+      int nSamples = 7;
+      for (int Sample = 0; Sample < nSamples; ++Sample) {
+        MeanSamples[Ros - 1][Drawer][Channel][Sample] = ped;
+      }
+    }
+  }
+
+  // prepare new samples
+  const int nSamples = 7;
+  float NewSamples[4][64][48][nSamples];
+  for (int Ros = 1; Ros < 5; ++Ros) {
+    for (int Drawer = 0; Drawer < 64; ++Drawer) {
+      for (int Channel = 0; Channel < 48; ++Channel) {
+        for (int Sample = 0; Sample < nSamples; ++Sample) {
+          NewSamples[Ros - 1][Drawer][Channel][Sample] =
+              ((OriginalDigits[Ros - 1][Drawer][Channel])->samples())[Sample];
+        }
+      }
+    }
+  }
+
+  // apply method
+  for (int Ros = 1; Ros < 5; ++Ros) {
+    for (int Drawer = 0; Drawer < 64; ++Drawer) {
+      for (int Channel = 0; Channel < 48; ++Channel) {
+        if (OriginalDigits[Ros - 1][Drawer][Channel]) {
+          int nSamples = (OriginalDigits[Ros - 1][Drawer][Channel])->nsamples();
+          std::vector<float> digits(nSamples);
+          for (int jCh = 0; jCh < 48; ++jCh) {
+            if (OriginalDigits[Ros - 1][Drawer][jCh]) {
+              if (Channel != jCh
+                  && fabs(((OriginalDigits[Ros - 1][Drawer][jCh])->samples())[3]
+                          - MeanSamples[Ros - 1][Drawer][jCh][3])
+                      < m_nRMS_threshold * Sample3RMS[Ros - 1][Drawer][jCh]) {
+
+                for (int Sample = 0; Sample < nSamples; ++Sample)
+                  NewSamples[Ros - 1][Drawer][Channel][Sample] =
+                      NewSamples[Ros - 1][Drawer][Channel][Sample]
+                          - AlphaMatrix[Ros - 1][Drawer][Channel][jCh]
+                              * (((OriginalDigits[Ros - 1][Drawer][jCh])->samples())[Sample]
+                                  - MeanSamples[Ros - 1][Drawer][jCh][Sample]);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // create new container
+  TileDigits * NewDigits[4][64][48];
+  TileDigitsContainer* NewDigitsContainer = new TileDigitsContainer();
+
+  // fill new container
+  for (int Ros = 1; Ros < 5; ++Ros) {
+    for (int Drawer = 0; Drawer < 64; ++Drawer) {
+      for (int Channel = 0; Channel < 48; ++Channel) {
+        if (OriginalDigits[Ros - 1][Drawer][Channel]) {
+          int nSamples = (OriginalDigits[Ros - 1][Drawer][Channel])->nsamples();
+          std::vector<float> digits(nSamples);
+          for (int Sample = 0; Sample < nSamples; ++Sample)
+            digits[Sample] = NewSamples[Ros - 1][Drawer][Channel][Sample];
+            NewDigits[Ros - 1][Drawer][Channel] = new TileDigits(
+                (OriginalDigits[Ros - 1][Drawer][Channel])->adc_HWID(), digits);
+            NewDigitsContainer->push_back(NewDigits[Ros - 1][Drawer][Channel]);
+        }
+      }
+    }
+  }
+
+  // register new container in TES
+  CHECK( evtStore()->record(NewDigitsContainer, "NewDigitsContainer") );
+
+  // lock new container
+  CHECK( evtStore()->setConst(NewDigitsContainer) );
+
+  NewDigitsContainer = NULL;
+
+  ATH_MSG_DEBUG( "execute completed successfully" );
+
+  return StatusCode::SUCCESS;
+}
+
+// #############################################################################
+StatusCode TileRawCorrelatedNoise::finalize() {
+// #############################################################################
+
+  ATH_MSG_INFO( " finalize completed successfully" );
+
+  return StatusCode::SUCCESS;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/TileTowerBuilderTool.cxx b/TileCalorimeter/TileRecUtils/src/TileTowerBuilderTool.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..9b0f3cc8041d7b2bc65b26071260475b3bb00b51
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileTowerBuilderTool.cxx
@@ -0,0 +1,39 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// Gaudi includes
+#include "GaudiKernel/MsgStream.h"
+
+// Calo includes
+#include "CaloIdentifier/CaloCell_ID.h"
+#include "CaloUtils/CaloTowerBuilderTool.h"
+
+// Tile includes
+#include "TileRecUtils/TileTowerBuilderTool.h"
+
+#include <string>
+
+TileTowerBuilderTool::TileTowerBuilderTool(const std::string& name, const std::string& type,
+    const IInterface* parent)
+    : CaloTowerBuilderTool(name, type, parent)
+{
+  declareProperty("DumpTowers", m_dumpTowers = false);
+  declareProperty("DumpWeightMap", m_dumpWeightMap = false);
+}
+
+TileTowerBuilderTool::~TileTowerBuilderTool() {
+}
+
+StatusCode TileTowerBuilderTool::initializeTool() {
+
+  // allow only TILE cells!
+  for (size_t iCalos = 0; iCalos < m_includedCalos.size(); iCalos++) {
+    if (m_includedCalos[iCalos] == "TILE") {
+      m_caloIndices.push_back(CaloCell_ID::TILE);
+    }
+  }
+
+  // check setup
+  return this->checkSetup(msg());
+}
diff --git a/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_entries.cxx b/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_entries.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..af152250020eb958eb317db41465497394fd121c
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_entries.cxx
@@ -0,0 +1,57 @@
+#include "GaudiKernel/DeclareFactoryEntries.h"
+
+#include "TileRecUtils/TileRawChannelBuilderFlatFilter.h"
+#include "TileRecUtils/TileRawChannelBuilderFitFilter.h"
+#include "TileRecUtils/TileRawChannelBuilderFitFilterCool.h"
+#include "TileRecUtils/TileRawChannelBuilderOptFilter.h"
+#include "TileRecUtils/TileRawChannelBuilderOpt2Filter.h"
+#include "TileRecUtils/TileRawChannelBuilderManyAmps.h"
+#include "TileRecUtils/TileRawChannelBuilderMF.h"
+#include "TileRecUtils/TileBeamInfoProvider.h"
+#include "TileRecUtils/TileCellBuilder.h"
+#include "TileRecUtils/TileCellFakeProb.h"
+#include "TileRecUtils/TileCellMaskingTool.h"
+#include "TileRecUtils/TileRawChannelMaker.h"
+#include "TileRecUtils/TileRawChannelVerify.h"
+#include "TileRecUtils/TileRawCorrelatedNoise.h"
+#include "TileRecUtils/TileTowerBuilderTool.h"
+#include "TileRecUtils/TileCellNoiseFilter.h"
+#include "TileRecUtils/TileRawChannelNoiseFilter.h"
+
+DECLARE_TOOL_FACTORY( TileRawChannelBuilderFlatFilter )
+DECLARE_TOOL_FACTORY( TileRawChannelBuilderFitFilter )
+DECLARE_TOOL_FACTORY( TileRawChannelBuilderFitFilterCool )
+DECLARE_TOOL_FACTORY( TileRawChannelBuilderOptFilter )
+DECLARE_TOOL_FACTORY( TileRawChannelBuilderOpt2Filter )
+DECLARE_TOOL_FACTORY( TileRawChannelBuilderManyAmps )
+DECLARE_TOOL_FACTORY( TileRawChannelBuilderMF )
+DECLARE_TOOL_FACTORY( TileBeamInfoProvider )
+DECLARE_TOOL_FACTORY( TileCellBuilder )
+DECLARE_TOOL_FACTORY( TileCellFakeProb )
+DECLARE_TOOL_FACTORY( TileCellMaskingTool )
+DECLARE_TOOL_FACTORY( TileTowerBuilderTool )
+DECLARE_TOOL_FACTORY( TileCellNoiseFilter )
+DECLARE_TOOL_FACTORY( TileRawChannelNoiseFilter )
+DECLARE_ALGORITHM_FACTORY( TileRawChannelMaker )
+DECLARE_ALGORITHM_FACTORY( TileRawChannelVerify )
+DECLARE_ALGORITHM_FACTORY( TileRawCorrelatedNoise )
+
+DECLARE_FACTORY_ENTRIES(TileRecUtils) {
+  DECLARE_TOOL( TileRawChannelBuilderFlatFilter )
+  DECLARE_TOOL( TileRawChannelBuilderFitFilter )
+  DECLARE_TOOL( TileRawChannelBuilderFitFilterCool )
+  DECLARE_TOOL( TileRawChannelBuilderOptFilter )
+  DECLARE_TOOL( TileRawChannelBuilderOpt2Filter )
+  DECLARE_TOOL( TileRawChannelBuilderManyAmps )
+  DECLARE_TOOL( TileRawChannelBuilderMF )
+  DECLARE_TOOL( TileBeamInfoProvider )
+  DECLARE_TOOL( TileCellBuilder )
+  DECLARE_TOOL( TileCellFakeProb )
+  DECLARE_TOOL( TileCellMaskingTool )
+  DECLARE_TOOL( TileTowerBuilderTool )
+  DECLARE_TOOL( TileCellNoiseFilter )
+  DECLARE_TOOL( TileRawChannelNoiseFilter )
+  DECLARE_ALGORITHM( TileRawChannelMaker )
+  DECLARE_ALGORITHM( TileRawChannelVerify )
+  DECLARE_ALGORITHM( TileRawCorrelatedNoise )
+}
diff --git a/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_load.cxx b/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_load.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..508906269ee69b2ed633ab069ceadcc048a8fbd8
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_load.cxx
@@ -0,0 +1,3 @@
+#include "GaudiKernel/LoadFactoryEntries.h"
+
+LOAD_FACTORY_ENTRIES(TileRecUtils)