diff --git a/Scintillator/ScintEventCnv/ScintByteStream/src/ScintWaveformDecoderTool.cxx b/Scintillator/ScintEventCnv/ScintByteStream/src/ScintWaveformDecoderTool.cxx
index de415ef295d11ca6ca9fec2724edfa231f852560..55c9bd960477cb6fbdf7871098a8a5cae9f9c3a8 100644
--- a/Scintillator/ScintEventCnv/ScintByteStream/src/ScintWaveformDecoderTool.cxx
+++ b/Scintillator/ScintEventCnv/ScintByteStream/src/ScintWaveformDecoderTool.cxx
@@ -95,6 +95,7 @@ ScintWaveformDecoderTool::convert(const DAQFormats::EventFull* re,
     ATH_MSG_DEBUG("Fragment:\n" << *frag);
 
     digitizer = new DigitizerDataFragment(frag->payload<const uint32_t*>(), frag->payload_size()); 
+
     break;
   }
 
@@ -103,7 +104,12 @@ ScintWaveformDecoderTool::convert(const DAQFormats::EventFull* re,
     return StatusCode::FAILURE;
   }
 
-  // ATH_MSG_DEBUG("Digitizer Fragment:\n" << *digitizer);
+  // Check validity here
+  if (!digitizer->valid()) {
+    ATH_MSG_WARNING("Found invalid digitizer fragment:\n" << *digitizer);
+  } else {
+    ATH_MSG_DEBUG("Found valid digitizer fragment");
+  }
 
   std::vector<unsigned int>* channelList;
 
@@ -127,13 +133,18 @@ ScintWaveformDecoderTool::convert(const DAQFormats::EventFull* re,
   for (unsigned int channel: *channelList) {
     ATH_MSG_DEBUG("Converting channel "+std::to_string(channel)+" for "+key);
 
+    // Check if this has data
+    if (!digitizer->channel_has_data(channel)) {
+      ATH_MSG_INFO("Channel " << channel << " has no data - skipping!");
+      continue;
+    } 
+
     ScintWaveform* wfm = new ScintWaveform();
 
-    // Finally, set values in Waveform object from Digitizer fragment
     try {
       wfm->setWaveform( channel, digitizer->channel_adc_counts( channel ) );
     } catch ( DigitizerDataException& e ) {
-      ATH_MSG_INFO("ScintWaveformDecoderTool:\n"
+      ATH_MSG_WARNING("ScintWaveformDecoderTool:\n"
 		   <<e.what()
 		   << "\nChannel "
 		   << channel
@@ -151,12 +162,19 @@ ScintWaveformDecoderTool::convert(const DAQFormats::EventFull* re,
     }
 
     container->push_back(wfm);    
+
+    // Sanity check
+    if (wfm->adc_counts().size() != wfm->n_samples()) {
+      ATH_MSG_WARNING("Created waveform channel" << channel << "with length " << wfm->adc_counts().size() << " but header reports n_samples = " << wfm->n_samples() << "!");
+      ATH_MSG_WARNING(*wfm);
+    }
+
   }
 
   // Don't spring a leak
   delete digitizer;
 
-ATH_MSG_DEBUG( "ScintWaveformDecoderTool created container " << key 
-<< " with size=" << container->size());
+  ATH_MSG_DEBUG( "ScintWaveformDecoderTool created container " << key 
+		 << " with size=" << container->size());
   return StatusCode::SUCCESS; 
 }
diff --git a/Scintillator/ScintRawEvent/src/ScintWaveform.cxx b/Scintillator/ScintRawEvent/src/ScintWaveform.cxx
index b525f2e0b51f7ea6e41ee283633ab0c55e3c085e..bc9f5eb912e4bd7cd75fefda9b4a829699f92578 100644
--- a/Scintillator/ScintRawEvent/src/ScintWaveform.cxx
+++ b/Scintillator/ScintRawEvent/src/ScintWaveform.cxx
@@ -52,14 +52,15 @@ ScintWaveform::setWaveform(unsigned int channel, const std::vector<uint16_t> wav
 
 std::ostream 
 &operator<<(std::ostream &out, const ScintWaveform &wfm) {
-  out << "Waveform data:" << std::endl
-  << std::setw(30) << " board_id:             "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.board_id()<<std::setfill(' ')<<std::endl
-  << std::setw(30) << " board_fail_flag:      "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.board_fail_flag()<<std::setfill(' ')<<std::endl
-  << std::setw(30) << " board_trig_options:   "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.pattern_trig_options()<<std::setfill(' ')<<std::endl
-  << std::setw(30) << " channel_mask:         "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.channel_mask()<<std::setfill(' ')<<std::endl
-  << std::setw(30) << " event_counter:        "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.event_counter()<<std::setfill(' ')<<std::endl
-  << std::setw(30) << " trigger_time_tag:     "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.trigger_time_tag()<<std::setfill(' ')<<std::endl
-      << std::setw(30) << " n_samples:            "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.n_samples()<<std::setfill(' ')<<std::endl;
+  out << "Waveform data channel:" << std::dec << wfm.channel() << std::endl;
+  out << std::setw(30) << " board_id:             "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.board_id()<<std::setfill(' ')<<std::endl;
+  out << std::setw(30) << " board_fail_flag:      "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.board_fail_flag()<<std::setfill(' ')<<std::endl;
+  out << std::setw(30) << " board_trig_options:   "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.pattern_trig_options()<<std::setfill(' ')<<std::endl;
+  out << std::setw(30) << " channel_mask:         "<<std::setfill(' ')<<std::setw(32)<<std::hex<<wfm.channel_mask()<<std::setfill(' ')<<std::endl;
+  out << std::setw(30) << " event_counter:        "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.event_counter()<<std::setfill(' ')<<std::endl;
+  out << std::setw(30) << " trigger_time_tag:     "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.trigger_time_tag()<<std::setfill(' ')<<std::endl;
+  out << std::setw(30) << " n_samples:            "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.n_samples()<<std::setfill(' ')<<std::endl;
+  out << std::setw(30) << " adc_counts.size():    "<<std::setfill(' ')<<std::setw(32)<<std::dec<<wfm.adc_counts().size()<<std::setfill(' ')<<std::endl;
 
   return out;
 }
diff --git a/Scintillator/ScintRecAlgs/CMakeLists.txt b/Scintillator/ScintRecAlgs/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7ad1776170385a4f2203030dbac8fc5f7f5f43ab
--- /dev/null
+++ b/Scintillator/ScintRecAlgs/CMakeLists.txt
@@ -0,0 +1,15 @@
+################################################################################
+# Package: ScintRecAlgs
+################################################################################
+
+# Declare the package name:
+atlas_subdir( ScintRecAlgs )
+
+# Component(s) in the package:
+atlas_add_component( ScintRecAlgs
+                     src/*.cxx src/*.h
+                     src/components/*.cxx
+                     LINK_LIBRARIES AthenaBaseComps StoreGateLib ScintRawEvent xAODFaserWaveform ScintRecToolsLib )
+
+atlas_install_python_modules( python/*.py )
+
diff --git a/Scintillator/ScintRecAlgs/python/ScintRecAlgsConfig.py b/Scintillator/ScintRecAlgs/python/ScintRecAlgsConfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..6319e9a01771a042f162c11f51d4f5b8d3bba181
--- /dev/null
+++ b/Scintillator/ScintRecAlgs/python/ScintRecAlgsConfig.py
@@ -0,0 +1,70 @@
+""" Define methods used to instantiate configured Waveform reconstruction tools and algorithms
+
+Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+"""
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+
+from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+
+WaveformReconstructionTool = CompFactory.WaveformReconstructionTool
+ClockReconstructionTool = CompFactory.ClockReconstructionTool
+
+# One stop shopping for normal FASER data
+def WaveformReconstructionCfg(flags):
+    """ Return all algorithms and tools for Waveform reconstruction """
+    acc = ComponentAccumulator()
+
+    acc.merge(WaveformClockRecCfg(flags, "ClockRecAlg"))
+
+    acc.merge(WaveformHitRecCfg(flags, "VetoWaveformRecAlg", "Veto"))
+    acc.merge(WaveformHitRecCfg(flags, "TimingWaveformRecAlg", "Trigger"))
+    acc.merge(WaveformHitRecCfg(flags, "PreshowerWaveformRecAlg", "Preshower"))
+    acc.merge(WaveformHitRecCfg(flags, "CaloWaveformRecAlg", "Calo"))
+    return acc
+
+# Return configured WaveformClock reconstruction algorithm
+def WaveformClockRecCfg(flags, name="ClockRecAlg", **kwargs):
+
+    acc = ComponentAccumulator()
+
+    tool = ClockReconstructionTool(name="ClockReconstructionTool")
+
+    kwargs.setdefault("ClockReconstructionTool", tool)
+
+    recoAlg = CompFactory.ScintClockRecAlg(name, **kwargs)
+    recoAlg.ClockReconstructionTool = tool
+    acc.addEventAlgo(recoAlg)
+
+    return acc
+
+# Return configured WaveformHit reconstruction algorithm
+# Specify data source (Veto, Trigger, Preshower, Calo, Test)
+def WaveformHitRecCfg(flags, name="WaveformRecAlg", source="", **kwargs):
+
+    acc = ComponentAccumulator()
+
+    tool = WaveformReconstructionTool(name=source+"WaveformRecTool", **kwargs)
+    kwargs.setdefault("WaveformContainerKey", source+"Waveforms")
+    kwargs.setdefault("WaveformHitContainerKey", source+"WaveformHits")
+    kwargs.setdefault("WaveformReconstructionTool", tool)
+              
+    recoAlg = CompFactory.ScintWaveformRecAlg(name, **kwargs)
+    recoAlg.WaveformReconstructionTool = tool
+    acc.addEventAlgo(recoAlg)
+
+    return acc
+
+def WaveformReconstructionOutputCfg(flags, **kwargs):
+    """ Return ComponentAccumulator with output for Waveform Reco"""
+    acc = ComponentAccumulator()
+    ItemList = [
+        "xAOD::WaveformHitContainer#*"
+        , "xAOD::WaveformHitAuxContainer#*"
+        , "xAOD::WaveformClock#*"
+        , "xAOD::WaveformClockAuxInfo#*"
+    ]
+    acc.merge(OutputStreamCfg(flags, "xAOD", ItemList))
+    # ostream = acc.getEventAlgo("OutputStreamRDO")
+    # ostream.TakeItemsFromInput = True # Don't know what this does
+    return acc
diff --git a/Scintillator/ScintRecAlgs/src/ScintClockRecAlg.cxx b/Scintillator/ScintRecAlgs/src/ScintClockRecAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..45ceaa30ba91d814072a02d312755d03d3bd1b85
--- /dev/null
+++ b/Scintillator/ScintRecAlgs/src/ScintClockRecAlg.cxx
@@ -0,0 +1,94 @@
+#include "ScintClockRecAlg.h"
+
+ScintClockRecAlg::ScintClockRecAlg(const std::string& name, 
+				   ISvcLocator* pSvcLocator)
+  : AthReentrantAlgorithm(name, pSvcLocator) { 
+}
+
+StatusCode 
+ScintClockRecAlg::initialize() {
+  ATH_MSG_INFO(name() << "::initalize()" );
+
+  // Initalize tools
+  ATH_CHECK( m_recoTool.retrieve() );
+
+  // Set key to read waveform from
+  ATH_CHECK( m_waveformContainerKey.initialize() );
+
+  // Set key to write container
+  ATH_CHECK( m_waveformClockKey.initialize() );
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode 
+ScintClockRecAlg::finalize() {
+  ATH_MSG_INFO(name() << "::finalize()");
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode 
+ScintClockRecAlg::execute(const EventContext& ctx) const {
+  ATH_MSG_INFO("Executing");
+
+  ATH_MSG_DEBUG("Run: " << ctx.eventID().run_number() 
+		<< " Event: " << ctx.eventID().event_number());
+
+  // Find the input waveform container
+  SG::ReadHandle<ScintWaveformContainer> waveformHandle(m_waveformContainerKey, ctx);
+
+  ATH_CHECK( waveformHandle.isValid() );
+  ATH_MSG_DEBUG("Found ReadHandle for Waveforms");
+
+  if (waveformHandle->size() == 0) {
+    ATH_MSG_INFO("Waveform container found with zero length!");
+    return StatusCode::SUCCESS;
+  }
+
+  // Check length (should be one)
+  if (waveformHandle->size() != 1) {
+    ATH_MSG_WARNING("Found waveform container " << m_waveformContainerKey 
+		    << " with size " << waveformHandle->size() << "!");
+  }
+
+  // Create the output clock container
+  SG::WriteHandle<xAOD::WaveformClock> clockHandle(m_waveformClockKey, ctx);
+  //ATH_CHECK( clockHandle.record( std::make_unique<WaveformClock>() ) );
+  ATH_CHECK( clockHandle.record( std::make_unique<xAOD::WaveformClock>(),
+				 std::make_unique<xAOD::WaveformClockAuxInfo>() ) );
+
+  auto clock = clockHandle.ptr();
+
+  // Reconstruct first element
+  for(const auto& wave : *waveformHandle) {
+
+    ATH_MSG_DEBUG("Reconstruct waveform for channel " << wave->channel());
+
+    // Make some sanity checks before going further
+    if (wave->adc_counts().size() == 0) {
+      ATH_MSG_ERROR( "Found waveform for channel " << wave->channel() 
+		     << " with size " << wave->adc_counts().size() << "!");
+      // Create dummy hit here, or just skip?
+      return StatusCode::FAILURE;
+    }
+    
+    if (wave->adc_counts().size() != wave->n_samples()) {
+      ATH_MSG_WARNING( "Found waveform for channel " << wave->channel() 
+		       << " with size " << wave->adc_counts().size() 
+		       << " not equal to number of samples " << wave->n_samples());
+      return StatusCode::SUCCESS;
+    }
+
+    // Reconstruct the hits
+    CHECK( m_recoTool->reconstruct(*wave, clock) );
+
+    // Only do one if there happen to be more
+    break;
+  }
+
+  ATH_MSG_DEBUG(waveformHandle.name() << " created");
+
+  return StatusCode::SUCCESS;
+}
+
diff --git a/Scintillator/ScintRecAlgs/src/ScintClockRecAlg.h b/Scintillator/ScintRecAlgs/src/ScintClockRecAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..3fde8295da9b5ca4cdecf68bceae205df2a7ef8a
--- /dev/null
+++ b/Scintillator/ScintRecAlgs/src/ScintClockRecAlg.h
@@ -0,0 +1,72 @@
+#ifndef SCINTRECALGS_SCINTCLOCKRECALG_H
+#define SCINTRECALGS_SCINTCLOCKRECALG_H
+
+// Base class
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+
+// Input data
+#include "ScintRawEvent/ScintWaveformContainer.h"
+
+// Output data
+// #include "ScintRecEvent/WaveformClock.h"
+#include "xAODFaserWaveform/WaveformClock.h"
+#include "xAODFaserWaveform/WaveformClockAuxInfo.h"
+#include "ScintRecTools/IClockReconstructionTool.h"
+
+#include "StoreGate/ReadHandleKey.h"
+
+// Gaudi
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+// STL
+#include <string>
+
+class ScintClockRecAlg : public AthReentrantAlgorithm {
+
+ public:
+  // Constructor
+  ScintClockRecAlg(const std::string& name, ISvcLocator* pSvcLocator);
+  virtual ~ScintClockRecAlg() = default;
+
+  /** @name Usual algorithm methods */
+  //@{
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext& ctx) const override;
+  virtual StatusCode finalize() override;
+  //@}
+
+ private:
+
+  /** @name Disallow default instantiation, copy, assignment */
+  //@{
+  ScintClockRecAlg() = delete;
+  ScintClockRecAlg(const ScintClockRecAlg&) = delete;
+  ScintClockRecAlg &operator=(const ScintClockRecAlg&) = delete;
+  //@}
+
+  /**
+   * @name Reconstruction tool
+   */
+  ToolHandle<IClockReconstructionTool> m_recoTool
+    {this, "ClockReconstructionTool", "ClockReconstructionTool"};
+
+  /**
+   * @name Input data using SG::ReadHandleKey
+   */
+  //@{
+  SG::ReadHandleKey<ScintWaveformContainer> m_waveformContainerKey
+    {this, "WaveformContainerKey", "ClockWaveforms"};
+  //@}
+
+  /**
+   * @name Output data using SG::WriteHandleKey
+   */
+  //@{
+  SG::WriteHandleKey<xAOD::WaveformClock> m_waveformClockKey
+    {this, "WaveformClockKey", "WaveformClock"};
+  //@}
+
+};
+
+#endif // SCINTRECALGS_SCINTCLOCKRECALG_H
diff --git a/Scintillator/ScintRecAlgs/src/ScintWaveformRecAlg.cxx b/Scintillator/ScintRecAlgs/src/ScintWaveformRecAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e3d42247e30d9b295e037338f93f092bcee32281
--- /dev/null
+++ b/Scintillator/ScintRecAlgs/src/ScintWaveformRecAlg.cxx
@@ -0,0 +1,81 @@
+#include "ScintWaveformRecAlg.h"
+
+ScintWaveformRecAlg::ScintWaveformRecAlg(const std::string& name, 
+					 ISvcLocator* pSvcLocator)
+  : AthReentrantAlgorithm(name, pSvcLocator) { 
+
+}
+
+StatusCode 
+ScintWaveformRecAlg::initialize() {
+  ATH_MSG_INFO(name() << "::initalize()" );
+
+  // Initalize tools
+  ATH_CHECK( m_recoTool.retrieve() );
+
+  // Set key to read waveform from
+  ATH_CHECK( m_waveformContainerKey.initialize() );
+
+  // Set key to read clock info
+  ATH_CHECK( m_clockKey.initialize() );
+
+  // Set key to write container
+  ATH_CHECK( m_waveformHitContainerKey.initialize() );
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode 
+ScintWaveformRecAlg::finalize() {
+  ATH_MSG_INFO(name() << "::finalize()");
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode 
+ScintWaveformRecAlg::execute(const EventContext& ctx) const {
+  ATH_MSG_INFO("Executing");
+
+  ATH_MSG_DEBUG("Run: " << ctx.eventID().run_number() 
+		<< " Event: " << ctx.eventID().event_number());
+
+  // Find the input waveform container
+  SG::ReadHandle<ScintWaveformContainer> waveformHandle(m_waveformContainerKey, ctx);
+
+  ATH_CHECK( waveformHandle.isValid() );
+  ATH_MSG_DEBUG("Found ReadHandle for ScintWaveformContainer " << m_waveformContainerKey);
+
+  if (waveformHandle->size() == 0) {
+    ATH_MSG_INFO("Waveform container found with zero length!");
+    return StatusCode::SUCCESS;
+  }
+
+  // Also find the clock information
+  SG::ReadHandle<xAOD::WaveformClock> clockHandle(m_clockKey, ctx);
+
+  ATH_CHECK( clockHandle.isValid() );
+  ATH_MSG_DEBUG("Found ReadHandle for WaveformClock");
+
+  // Find the output waveform container
+  SG::WriteHandle<xAOD::WaveformHitContainer> hitContainerHandle(m_waveformHitContainerKey, ctx);
+  ATH_CHECK( hitContainerHandle.record( std::make_unique<xAOD::WaveformHitContainer>(),
+					std::make_unique<xAOD::WaveformHitAuxContainer>() ) );
+
+  ATH_MSG_DEBUG("WaveformsHitContainer '" << hitContainerHandle.name() << "' initialized");
+
+  // Reconstruct each waveform
+  for( const auto& wave : *waveformHandle) {
+
+    ATH_MSG_DEBUG("Reconstruct waveform for channel " << wave->channel());
+
+    // Reconstruct the hits, may be more than one, so pass container
+    CHECK( m_recoTool->reconstruct(*wave, clockHandle.ptr(), 
+				   hitContainerHandle.ptr()) );
+
+  }
+
+  ATH_MSG_DEBUG("WaveformsHitContainer '" << hitContainerHandle.name() << "' filled with "<< hitContainerHandle->size() <<" items");
+
+  return StatusCode::SUCCESS;
+}
+
diff --git a/Scintillator/ScintRecAlgs/src/ScintWaveformRecAlg.h b/Scintillator/ScintRecAlgs/src/ScintWaveformRecAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..7d43e97523bdba7ddc8749dddc40108dafa29f35
--- /dev/null
+++ b/Scintillator/ScintRecAlgs/src/ScintWaveformRecAlg.h
@@ -0,0 +1,86 @@
+#ifndef SCINTRECALGS_SCINTWAVEFORMRECALG_H
+#define SCINTRECALGS_SCINTWAVEFORMRECALG_H
+
+// Base class
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+
+// Data classes
+#include "ScintRawEvent/ScintWaveformContainer.h"
+
+#include "xAODFaserWaveform/WaveformClock.h"
+#include "xAODFaserWaveform/WaveformClockAuxInfo.h"
+
+#include "xAODFaserWaveform/WaveformHit.h"
+#include "xAODFaserWaveform/WaveformHitContainer.h"
+#include "xAODFaserWaveform/WaveformHitAuxContainer.h"
+
+// Tool classes
+#include "ScintRecTools/IWaveformReconstructionTool.h"
+
+// Handles
+#include "StoreGate/ReadHandleKey.h"
+#include "StoreGate/WriteHandleKey.h"
+
+// Gaudi
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+// STL
+#include <string>
+
+class ScintWaveformRecAlg : public AthReentrantAlgorithm {
+
+ public:
+  // Constructor
+  ScintWaveformRecAlg(const std::string& name, ISvcLocator* pSvcLocator);
+  virtual ~ScintWaveformRecAlg() = default;
+
+  /** @name Usual algorithm methods */
+  //@{
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext& ctx) const override;
+  virtual StatusCode finalize() override;
+  //@}
+
+ private:
+
+  /** @name Disallow default instantiation, copy, assignment */
+  //@{
+  ScintWaveformRecAlg() = delete;
+  ScintWaveformRecAlg(const ScintWaveformRecAlg&) = delete;
+  ScintWaveformRecAlg &operator=(const ScintWaveformRecAlg&) = delete;
+  //@}
+
+  /**
+   * @name Reconstruction tool
+   */
+  ToolHandle<IWaveformReconstructionTool> m_recoTool
+    {this, "WaveformReconstructionTool", "WaveformReconstructionTool"};
+
+  /**
+   * @name Input raw waveform data using SG::ReadHandleKey
+   */
+  //@{
+  SG::ReadHandleKey<ScintWaveformContainer> m_waveformContainerKey
+    {this, "WaveformContainerKey", ""};
+  //@}
+
+  /**
+   * @name Input WaveformClock data using SG::ReadHandleKey
+   */
+  //@{
+  SG::ReadHandleKey<xAOD::WaveformClock> m_clockKey
+    {this, "WaveformClockKey", "WaveformClock"};
+  //@}
+
+  /**
+   * @name Output data using SG::WriteHandleKey
+   */
+  //@{
+  SG::WriteHandleKey<xAOD::WaveformHitContainer> m_waveformHitContainerKey
+    {this, "WaveformHitContainerKey", ""};
+  //@}
+
+};
+
+#endif // SCINTRECALGS_SCINTWAVEFORMRECALG_H
diff --git a/Scintillator/ScintRecAlgs/src/components/ScintRecAlgs_entries.cxx b/Scintillator/ScintRecAlgs/src/components/ScintRecAlgs_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9634fc8f9a4adbd9d6950b1727fc5f19f72d7ec0
--- /dev/null
+++ b/Scintillator/ScintRecAlgs/src/components/ScintRecAlgs_entries.cxx
@@ -0,0 +1,5 @@
+#include "../ScintWaveformRecAlg.h"
+#include "../ScintClockRecAlg.h"
+
+DECLARE_COMPONENT( ScintWaveformRecAlg )
+DECLARE_COMPONENT( ScintClockRecAlg )
diff --git a/Scintillator/ScintRecTools/CMakeLists.txt b/Scintillator/ScintRecTools/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a128a73845fdfdfea13d2c43e6085d2f5e99b2d1
--- /dev/null
+++ b/Scintillator/ScintRecTools/CMakeLists.txt
@@ -0,0 +1,25 @@
+################################################################################
+# Package: ScintRecTools
+################################################################################
+
+# Declare the package name:
+atlas_subdir( ScintRecTools )
+
+# External dependencies:
+find_package( ROOT )
+
+# Component(s) in the package:
+atlas_add_library( ScintRecToolsLib
+                   ScintRecTools/*.h src/*.cxx src/*.h
+                   PUBLIC_HEADERS ScintRecTools
+                   PRIVATE_INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
+                   LINK_LIBRARIES AthenaBaseComps AthenaKernel GeoPrimitives ScintRawEvent xAODFaserWaveform
+                   PRIVATE_LINK_LIBRARIES ${ROOT_LIBRARIES}
+		   )
+
+atlas_add_component( ScintRecTools
+		     src/components/*.cxx 
+		     INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
+                     LINK_LIBRARIES ${ROOT_LIBRARIES} AthenaBaseComps GaudiKernel ScintRecToolsLib )
+
+
diff --git a/Scintillator/ScintRecTools/ScintRecTools/IClockReconstructionTool.h b/Scintillator/ScintRecTools/ScintRecTools/IClockReconstructionTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..46712a75ca41eceabf1331fd2ae1412d9f7f429b
--- /dev/null
+++ b/Scintillator/ScintRecTools/ScintRecTools/IClockReconstructionTool.h
@@ -0,0 +1,39 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/**
+ * @file IClockReconstructionTool.h
+ * Header file for the IClockReconstructionTool class
+ * @author Eric Torrence, 2021
+ */
+
+
+#ifndef SCINTRECTOOLS_ICLOCKRECONSTRUCTIONTOOL_H
+#define SCINTRECTOOLS_ICLOCKRECONSTRUCTIONTOOL_H
+
+// Base class
+#include "GaudiKernel/IAlgTool.h"
+#include "GaudiKernel/ToolHandle.h"
+
+#include "xAODFaserWaveform/WaveformClock.h"
+
+class ScintWaveform;
+
+///Interface for Clock reco algorithms
+class IClockReconstructionTool : virtual public IAlgTool 
+{
+  public:
+
+  // InterfaceID
+  DeclareInterfaceID(IClockReconstructionTool, 1, 0);
+
+  virtual ~IClockReconstructionTool() = default;
+
+  // Reconstruct all peaks in a raw waveform
+  virtual StatusCode reconstruct(const ScintWaveform& wave, 
+				 xAOD::WaveformClock* clockdata) const = 0;
+
+};
+
+#endif // SCINTRECTOOLS_ICLOCKRECONSTRUCTIONTOOL_H
diff --git a/Scintillator/ScintRecTools/ScintRecTools/IWaveformReconstructionTool.h b/Scintillator/ScintRecTools/ScintRecTools/IWaveformReconstructionTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..00218de1cf251e239be6cd5e066e02721154be61
--- /dev/null
+++ b/Scintillator/ScintRecTools/ScintRecTools/IWaveformReconstructionTool.h
@@ -0,0 +1,41 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/**
+ * @file IWaveformReconstructionTool.h
+ * Header file for the IWaveformReconstructionTool class
+ * @author Eric Torrence, 2021
+ */
+
+
+#ifndef SCINTRECTOOLS_IWAVEFORMRECONSTRUCTIONTOOL_H
+#define SCINTRECTOOLS_IWAVEFORMRECONSTRUCTIONTOOL_H
+
+// Base class
+#include "GaudiKernel/IAlgTool.h"
+#include "GaudiKernel/ToolHandle.h"
+
+#include "xAODFaserWaveform/WaveformHitContainer.h"
+#include "xAODFaserWaveform/WaveformClock.h"
+
+class ScintWaveform;
+
+///Interface for Waveform reco algorithms
+class IWaveformReconstructionTool : virtual public IAlgTool 
+{
+  public:
+
+  // InterfaceID
+  DeclareInterfaceID(IWaveformReconstructionTool, 1, 0);
+
+  virtual ~IWaveformReconstructionTool() = default;
+
+  // Reconstruct all peaks in a raw waveform
+  virtual StatusCode reconstruct(const ScintWaveform& wave, 
+				 const xAOD::WaveformClock* clock, 
+				 xAOD::WaveformHitContainer* container) const = 0;
+
+};
+
+#endif // SCINTRECTOOLS_IWAVEFORMRECONSTRUCTIONTOOL_H
diff --git a/Scintillator/ScintRecTools/src/ClockReconstructionTool.cxx b/Scintillator/ScintRecTools/src/ClockReconstructionTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..92442bed407a6b62d653d803d42ce7c18a7b6ccf
--- /dev/null
+++ b/Scintillator/ScintRecTools/src/ClockReconstructionTool.cxx
@@ -0,0 +1,133 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/**
+ * @file ClockReconstructionTool.cxx
+ * Implementation file for the ClockReconstructionTool.cxx
+ * @ author E. Torrence, 2021
+ **/
+
+#include "ClockReconstructionTool.h"
+
+#include "TVirtualFFT.h"
+
+#include <vector>
+
+// Constructor
+ClockReconstructionTool::ClockReconstructionTool(const std::string& type, const std::string& name, const IInterface* parent) :
+  base_class(type, name, parent)
+{
+}
+
+// Initialization
+StatusCode
+ClockReconstructionTool::initialize() {
+  ATH_MSG_INFO( name() << "::initalize()" );
+
+  return StatusCode::SUCCESS;
+}
+
+// Reconstruction step
+StatusCode
+ClockReconstructionTool::reconstruct(const ScintWaveform& raw_wave,
+				     xAOD::WaveformClock* clockdata) const {
+
+  ATH_MSG_DEBUG("Clock reconstruct called ");
+
+  // Check the output object
+  if (!clockdata) {
+    ATH_MSG_ERROR("WaveformClock passed to reconstruct() is null!");
+    return StatusCode::FAILURE;
+  }
+
+  // Set the trigger time
+  //clockdata->setTriggerTime(raw_wave.trigger_time_tag());
+  //ATH_MSG_DEBUG("Trigger time: " << raw_wave.trigger_time_tag());
+
+  // Digitized clock data, sampled at 500 MHz (2 ns)
+  auto counts = raw_wave.adc_counts();
+
+  // Need a double array for FFT
+  int N = counts.size();
+  std::vector<double> wave(N);
+  wave.assign(counts.begin(), counts.end());
+
+  ATH_MSG_DEBUG("Created double array with length " << wave.size() );
+  ATH_MSG_DEBUG("First 10 elements:");
+  for (unsigned int i=0; i< 10; i++)
+    ATH_MSG_DEBUG(" " << i << " " << wave[i]);
+
+  // delta_nu = 1/T where T is the total waveform length
+  // Multiplying (freqmult * i) gives the frequency of point i in the FFT
+  double freqmult = 1./(0.002 * N); // 2ns per point, so this is in MHz
+
+  TVirtualFFT *fftr2c = TVirtualFFT::FFT(1, &N, "R2C");
+  fftr2c->SetPoints(&wave[0]);
+  fftr2c->Transform();
+
+  // Get the coefficients
+  std::vector<double> re_full(N);
+  std::vector<double> im_full(N);
+  std::vector<double> magnitude(N/2); 
+  fftr2c->GetPointsComplex(&re_full[0],&im_full[0]);
+
+  // Normalize the magnitude (just using the positive complex frequencies)
+  unsigned int i=0;
+  magnitude[i] = sqrt(pow(re_full[i], 2) + pow(im_full[i], 2))/N;
+  for(i=1; i<magnitude.size(); i++) 
+    magnitude[i] = 2*sqrt(pow(re_full[i], 2) + pow(im_full[i], 2))/N;
+
+  // First, look at the DC offset
+  ATH_MSG_DEBUG("DC offset: " << magnitude[0]);
+
+  // Second, find max value (should be primary line at 40 MHz
+  unsigned int imax = max_element(magnitude.begin()+1, magnitude.end()) - magnitude.begin();
+
+  if (((int(imax)-1) < 0) || ((imax+1) >= magnitude.size())) {
+    ATH_MSG_WARNING("Found maximum frequency amplitude at postion " << imax << "!");
+  } else {
+    ATH_MSG_DEBUG("Magnitude array at peak:");
+    for(i = imax-1; i <= imax+1; i++)
+      ATH_MSG_DEBUG("Index: " << i << " Freq: " << i*freqmult << " Mag: " << magnitude[i]);
+  }
+
+  // Store results
+  clockdata->set_dc_offset(magnitude[0]);
+  clockdata->set_frequency(imax * freqmult);
+  clockdata->set_amplitude(magnitude[imax]);
+  clockdata->set_phase(atan2(im_full[imax], re_full[imax]));
+
+  ATH_MSG_DEBUG("Before correcting for finite resolution:");
+  ATH_MSG_DEBUG(*clockdata);
+
+  // Correct for the windowing to find the exact peak frequency
+  // Following: https://indico.cern.ch/event/132526/contributions/128902/attachments/99707/142376/Meeting1-06-11_FFT_corrections_for_tune_measurements.pdf
+
+  double dm;  // Fraction of integer frequency where real peak sits
+  if (magnitude[imax+1] >= magnitude[imax-1]) { // Past ipeak by dm
+    dm = magnitude[imax+1] / (magnitude[imax] + magnitude[imax + 1]);
+  } else { // Before ipeak by dm
+    dm = -magnitude[imax-1] / (magnitude[imax-1] + magnitude[imax]);
+  }
+
+  ATH_MSG_DEBUG("Found shift in frequency index: " << dm);
+
+  // Improved values
+
+  double phase = atan2(im_full[imax], re_full[imax]) - dm * M_PI;
+  // Fix any overflows
+  if (phase < M_PI) phase += (2*M_PI);
+  if (phase > M_PI) phase -= (2*M_PI);
+
+  clockdata->set_frequency( (imax+dm) * freqmult );
+  clockdata->set_phase (phase);
+  clockdata->set_amplitude( 2*M_PI*dm*magnitude[imax] / sin(M_PI * dm) );
+
+  ATH_MSG_DEBUG("After correcting for finite resolution:");
+  ATH_MSG_DEBUG(*clockdata);
+
+  delete fftr2c;
+
+  return StatusCode::SUCCESS;
+}
diff --git a/Scintillator/ScintRecTools/src/ClockReconstructionTool.h b/Scintillator/ScintRecTools/src/ClockReconstructionTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..c7f76d6b6934ecd54339e0e762d1245b52d4a0dd
--- /dev/null
+++ b/Scintillator/ScintRecTools/src/ClockReconstructionTool.h
@@ -0,0 +1,50 @@
+/*
+   Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/** @file ClockReconstructionTool.h
+ *  Header file for ClockReconstructionTool.h
+ *
+ */
+#ifndef SCINTRECTOOLS_CLOCKRECONSTRUCTIONTOOL_H
+#define SCINTRECTOOLS_CLOCKRECONSTRUCTIONTOOL_H
+
+//Athena
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ScintRecTools/IClockReconstructionTool.h"
+
+#include "ScintRawEvent/ScintWaveform.h"
+#include "xAODFaserWaveform/WaveformClock.h"
+
+//Gaudi
+#include "GaudiKernel/ToolHandle.h"
+
+//STL
+#include <string>
+#include <vector>
+
+class ClockReconstructionTool: public extends<AthAlgTool, IClockReconstructionTool> {
+ public:
+
+  /// Normal constructor for an AlgTool; 'properties' are also declared here
+  ClockReconstructionTool(const std::string& type, 
+			  const std::string& name, const IInterface* parent);
+
+  /// Retrieve the necessary services in initialize
+  StatusCode initialize();
+
+  /// Reconstruct hits from clock  
+  virtual StatusCode reconstruct(const ScintWaveform& wave,
+				 xAOD::WaveformClock* clockdata) const;
+
+ private:
+
+  //
+  // Baseline Estimation Parameters
+  //BooleanProperty m_useSimpleBaseline{this, "UseSimpleBaseline", false};
+  //IntegerProperty m_samplesForBaselineAverage{this, "SamplesForBaselineAverage", 40};
+  //FloatProperty m_baselineFitWindow{this, "BaselineFitWindow", 2.};
+
+};
+
+#endif // SCINTRECTOOLS_CLOCKRECONSTRUCTIONTOOL_H
diff --git a/Scintillator/ScintRecTools/src/WaveformBaselineData.cxx b/Scintillator/ScintRecTools/src/WaveformBaselineData.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..f2e92625cff312592eeeadbde13715cc04a69775
--- /dev/null
+++ b/Scintillator/ScintRecTools/src/WaveformBaselineData.cxx
@@ -0,0 +1,36 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+#include "WaveformBaselineData.h"
+
+WaveformBaselineData::WaveformBaselineData() {
+  clear();
+}
+
+WaveformBaselineData::WaveformBaselineData(bool isvalid) {
+  clear();
+  valid = isvalid;
+}
+
+void
+WaveformBaselineData::clear() {
+  channel = -1;
+  valid = false;
+  mean = 0.;
+  rms = 0.;
+  fit_status = -1;
+  peak = 0.;
+  chi2n = 0.;
+}
+
+std::ostream
+&operator<<( std::ostream& out, const WaveformBaselineData &base ) {
+  out << "WaveformBaselineData - Mean: " << base.mean << ", RMS: " << base.rms << std::endl;
+  return out;
+}
+
+
+
+
+
diff --git a/Scintillator/ScintRecTools/src/WaveformBaselineData.h b/Scintillator/ScintRecTools/src/WaveformBaselineData.h
new file mode 100644
index 0000000000000000000000000000000000000000..13144a4f92da45982a32a7b25053d075a2a8bb2b
--- /dev/null
+++ b/Scintillator/ScintRecTools/src/WaveformBaselineData.h
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+///////////////////////////////////////////////////////////////////
+// WaveformBaselineData.h
+//   Header file for class WaveformBaselineData
+///////////////////////////////////////////////////////////////////
+// Class to handle the baseline information for a raw waveform
+///////////////////////////////////////////////////////////////////
+// Version 1.0   2/21/2021 Eric Torrence
+///////////////////////////////////////////////////////////////////
+#ifndef SCINTRECTOOLS_WAVEFORMBASELINEDATA_H
+#define SCINTRECTOOLS_WAVEFORMBASELINEDATA_H
+
+#include <iostream>
+
+/**
+ * @class WaveformBaselineData
+ * The baseline is the estimate of the waveform reading for no signal.
+ */
+  
+class WaveformBaselineData {
+      
+ public:
+      
+  /** Default constructor */
+  WaveformBaselineData();
+
+  // Set status directly
+  WaveformBaselineData(bool valid);
+
+  // move assignment defaulted
+  WaveformBaselineData & operator= (WaveformBaselineData &&) = default;
+  //assignment defaulted
+  WaveformBaselineData & operator= (const WaveformBaselineData &) = default;
+  //copy c'tor defaulted
+  WaveformBaselineData(const WaveformBaselineData &) = default;
+
+  /** Destructor */
+  virtual ~WaveformBaselineData() = default;
+
+  /** Clones */
+  virtual WaveformBaselineData* clone() const ;       
+
+  // Clear
+  void clear();
+            
+  // Data members
+  int channel;  // Channel number
+  bool valid;   // Overall valididty flag
+
+  double mean;  // Best value for mean baseline
+  double rms;   // Best value for baseline RMS
+  int fit_status; // Return value from fit
+  double peak;  // Amplitude of baseline fit
+  double chi2n; // Reduced chi2/Ndof from gauss fit
+
+ private:
+
+};
+
+std::ostream
+&operator<<(std::ostream &out, const WaveformBaselineData &wfm);
+
+///////////////////////////////////////////////////////////////////
+// Inline methods:
+///////////////////////////////////////////////////////////////////
+
+inline WaveformBaselineData* 
+WaveformBaselineData::clone() const { 
+  return new WaveformBaselineData(*this);  
+}
+
+#endif // SCINTRECTOOLS_WAVEFORMBASELINEDATA_H
diff --git a/Scintillator/ScintRecTools/src/WaveformFitResult.cxx b/Scintillator/ScintRecTools/src/WaveformFitResult.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..45dcdae23e6760a28617e5507893e44a36be6a38
--- /dev/null
+++ b/Scintillator/ScintRecTools/src/WaveformFitResult.cxx
@@ -0,0 +1,35 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+#include "WaveformFitResult.h"
+
+WaveformFitResult::WaveformFitResult() {
+  clear();
+}
+
+WaveformFitResult::WaveformFitResult(bool isvalid) {
+  clear();
+  valid = isvalid;
+}
+
+void
+WaveformFitResult::clear() {
+  valid = false;
+  peak = 0.;
+  mean = 0.;
+  sigma = 0.;
+  fit_status = -1;
+  chi2n = 0.;
+}
+
+std::ostream
+&operator<<( std::ostream& out, const WaveformFitResult &fit ) {
+  out << "WaveformFitResult - Mean: " << fit.mean << ", Sigma: " << fit.sigma << ", Peak: " << fit.peak << std::endl;
+  return out;
+}
+
+
+
+
+
diff --git a/Scintillator/ScintRecTools/src/WaveformFitResult.h b/Scintillator/ScintRecTools/src/WaveformFitResult.h
new file mode 100644
index 0000000000000000000000000000000000000000..92ecf0fc6e69d1b481ffdbc84b0959be2049fd6a
--- /dev/null
+++ b/Scintillator/ScintRecTools/src/WaveformFitResult.h
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+///////////////////////////////////////////////////////////////////
+// WaveformFitResult.h
+//   Header file for class WaveformFitResult
+///////////////////////////////////////////////////////////////////
+// Class to handle the fit output for a raw waveform
+///////////////////////////////////////////////////////////////////
+// Version 1.0   2/21/2021 Eric Torrence
+///////////////////////////////////////////////////////////////////
+#ifndef SCINTRECTOOLS_WAVEFORMFITRESULT_H
+#define SCINTRECTOOLS_WAVEFORMFITRESULT_H
+
+#include <iostream>
+
+/**
+ * @class WaveformFitResult
+ * The baseline is the estimate of the waveform reading for no signal.
+ */
+  
+class WaveformFitResult {
+      
+ public:
+      
+  /** Default constructor */
+  WaveformFitResult();
+
+  // Set status directly
+  WaveformFitResult(bool valid);
+
+  // move assignment defaulted
+  WaveformFitResult & operator= (WaveformFitResult &&) = default;
+  //assignment defaulted
+  WaveformFitResult & operator= (const WaveformFitResult &) = default;
+  //copy c'tor defaulted
+  WaveformFitResult(const WaveformFitResult &) = default;
+
+  /** Destructor */
+  virtual ~WaveformFitResult() = default;
+
+  // Clear
+  void clear();
+            
+  // Data members
+  bool valid;   // Overall valididty (from fit result == 0)
+
+  double peak;  // Best value for peak
+  double mean;  // Best value for mean
+  double sigma; // Best value for width
+  double alpha; // Crystal ball parameter
+  double nval;  // Crystal ball parameter
+  double chi2ndf; 
+  double integral;
+  double time;  // Time of leading edge of pulse
+
+  int fit_status; // Return value from fit
+
+  double chi2n; // Reduced chi2/Ndof from fit
+
+ private:
+
+};
+
+std::ostream
+&operator<<(std::ostream &out, const WaveformFitResult &wfm);
+
+///////////////////////////////////////////////////////////////////
+// Inline methods:
+///////////////////////////////////////////////////////////////////
+
+#endif // SCINTRECTOOLS_WAVEFORMFITRESULT_H
diff --git a/Scintillator/ScintRecTools/src/WaveformReconstructionTool.cxx b/Scintillator/ScintRecTools/src/WaveformReconstructionTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..037cde51247e2c70b7f5db4728d336e28233d8ff
--- /dev/null
+++ b/Scintillator/ScintRecTools/src/WaveformReconstructionTool.cxx
@@ -0,0 +1,579 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/**
+ * @file WaveformReconstructionTool.cxx
+ * Implementation file for the WaveformReconstructionTool.cxx
+ * @ author E. Torrence, 2021
+ **/
+
+#include "WaveformReconstructionTool.h"
+
+#include "xAODFaserWaveform/WaveformHit.h"
+
+#include "TH1F.h"
+#include "TF1.h"
+#include "TFitResult.h"
+#include "TFitResultPtr.h"
+#include "TGraph.h"
+
+#include <vector>
+#include <tuple>
+
+// Constructor
+WaveformReconstructionTool::WaveformReconstructionTool(const std::string& type, const std::string& name, const IInterface* parent) :
+  base_class(type, name, parent)
+{
+}
+
+// Initialization
+StatusCode
+WaveformReconstructionTool::initialize() {
+  ATH_MSG_INFO( name() << "::initalize()" );
+
+  if (m_useSimpleBaseline.value()) {
+    ATH_MSG_INFO("Will use simple baseline estimation");
+  } else {
+    ATH_MSG_INFO("Will use fit to determine baseline");
+  }
+  return StatusCode::SUCCESS;
+}
+
+// Reconstruction step
+StatusCode
+WaveformReconstructionTool::reconstruct(const ScintWaveform& raw_wave,
+					const xAOD::WaveformClock* clock, 
+					xAOD::WaveformHitContainer* container) const {
+
+  ATH_MSG_DEBUG(" reconstruct called ");
+
+  // Check the container
+  if (!container) {
+    ATH_MSG_ERROR("WaveformHitCollection passed to reconstruct() is null!");
+    return StatusCode::FAILURE;
+  }
+
+  //
+  // We always want to create at least one hit, so create it here
+  xAOD::WaveformHit* hit = new xAOD::WaveformHit();
+  container->push_back(hit);
+  hit->set_channel(raw_wave.channel());
+
+  //
+  // Make some sanity checks on the waveform data
+  if (raw_wave.adc_counts().size() == 0) {
+    ATH_MSG_WARNING( "Found waveform for channel " << raw_wave.channel() 
+		     << " with size " << raw_wave.adc_counts().size() << "!");
+
+    hit->set_status_bit(xAOD::WaveformStatus::WAVEFORM_MISSING);
+    return StatusCode::SUCCESS;
+  }
+    
+  if (raw_wave.adc_counts().size() != raw_wave.n_samples()) {
+    ATH_MSG_WARNING( "Found waveform for channel " << raw_wave.channel() 
+		     << " with size " << raw_wave.adc_counts().size() 
+		     << " not equal to number of samples " << raw_wave.n_samples());
+
+    hit->set_status_bit(xAOD::WaveformStatus::WAVEFORM_INVALID);
+    return StatusCode::SUCCESS;
+  }
+
+  // Set channel
+  hit->set_channel(raw_wave.channel());
+
+  //
+  // Find baseline
+  WaveformBaselineData baseline;
+
+  if (m_useSimpleBaseline.value())
+    baseline = findSimpleBaseline(raw_wave);
+  else
+    baseline = findAdvancedBaseline(raw_wave);
+
+  if (!(baseline.valid)) {
+    ATH_MSG_WARNING("Failed to reconstruct baseline!");
+
+    hit->set_baseline_mean(0.);
+    hit->set_baseline_rms(-1.);
+    hit->set_status_bit(xAOD::WaveformStatus::BASELINE_FAILED);
+
+    return StatusCode::SUCCESS;
+  }
+
+  // Save baseline to hit collection object
+  hit->set_baseline_mean(baseline.mean);
+  hit->set_baseline_rms(baseline.rms);
+
+  //
+  // Create baseline-subtracted data array for both time and signal
+  // Time in ns from start of readout
+  unsigned int size = raw_wave.adc_counts().size();
+  std::vector<float> time(size);  
+  for (unsigned int i=0; i<size; i++)
+    time[i] = 2.*i;
+
+  // Baseline subtracted (and inverted) ADC waveform values
+  std::vector<float> wave(raw_wave.adc_counts().begin(), raw_wave.adc_counts().end());
+  for (auto& element : wave)
+    element = baseline.mean - element;
+
+  bool first = true;
+
+  // Now we iteratively find peaks and fit
+  while(true) {
+
+    //
+    // Find peak in array and return time and value arrays
+    // This range of data is also *removed* from original arrays
+    std::vector<float> wtime;
+    std::vector<float> wwave;
+
+    // All done if we don't have any peaks above threshold
+    // If we do find a significant peak, fill the window
+    if (! findPeak(baseline, time, wave, wtime, wwave) ) {
+      if (first) hit->set_status_bit(xAOD::WaveformStatus::THRESHOLD_FAILED);
+      break;
+    }
+
+    //
+    // Create new hit to fill
+    if (!first) {
+      hit = new xAOD::WaveformHit();
+      container->push_back(hit);
+      hit->set_status_bit(xAOD::WaveformStatus::SECONDARY);
+    }
+    first = false;
+
+    //
+    // Save windowed waveform to Hit object
+    hit->set_channel(raw_wave.channel());
+    hit->set_baseline_mean(baseline.mean);
+    hit->set_baseline_rms(baseline.rms);
+    hit->set_time_vector(wtime);
+    hit->set_wave_vector(wave);
+
+    //
+    // Find some raw values
+    WaveformFitResult raw = findRawHitValues(wtime, wwave);
+    hit->set_peak(raw.peak);
+    hit->set_mean(raw.mean);
+    hit->set_width(raw.sigma);
+    hit->set_integral(raw.integral);
+    hit->set_localtime(raw.mean);
+    hit->set_raw_peak(raw.peak);
+    hit->set_raw_integral(raw.integral);
+
+    //
+    // Perform Gaussian fit to waveform
+    WaveformFitResult gfit = fitGaussian(raw, wtime, wwave);
+    if (! gfit.valid) {
+      ATH_MSG_WARNING( " Gaussian waveform fit failed! ");
+      hit->set_status_bit(xAOD::WaveformStatus::GFIT_FAILED);
+    } 
+
+    // Fit results (or raw if it failed)
+    hit->set_peak(gfit.peak);
+    hit->set_mean(gfit.mean);
+    hit->set_width(gfit.sigma);
+    hit->set_integral(gfit.integral);
+    hit->set_localtime(gfit.time);
+
+    //
+    // Check for overflow
+    if (m_removeOverflow && findOverflow(baseline, wtime, wwave)) {
+      ATH_MSG_INFO("Found waveform overflow");
+      hit->set_status_bit(xAOD::WaveformStatus::WAVE_OVERFLOW);
+    }
+
+    //
+    // Perform CB fit
+    WaveformFitResult cbfit = fitCBall(gfit, wtime, wwave);
+    if (! cbfit.valid) {
+      ATH_MSG_WARNING("CrystalBall fit failed!");
+      // Still have gaussian parameters as an estimate
+      hit->set_status_bit(xAOD::WaveformStatus::CBFIT_FAILED);
+    } else {
+      hit->set_peak(cbfit.peak);
+      hit->set_mean(cbfit.mean);
+      hit->set_width(cbfit.sigma);
+      hit->set_integral(cbfit.integral);
+      hit->set_localtime(cbfit.time);
+
+      hit->set_alpha(cbfit.alpha);
+      hit->set_nval(cbfit.nval);
+    }
+
+    //
+    // Find time from clock
+    if (!clock) {
+      hit->set_status_bit(xAOD::WaveformStatus::CLOCK_INVALID);
+    } else {
+      hit->set_bcid_time(clock->time_from_clock(hit->localtime()));
+    }
+
+    if (! m_findMultipleHits) break;
+
+  } // End of loop over waveform data
+
+  ATH_MSG_DEBUG( "WaveformReconstructionTool finished for channel " 
+		 << raw_wave.channel() << " container size= " << container->size());
+
+  return StatusCode::SUCCESS;
+}
+
+bool
+WaveformReconstructionTool::findPeak(WaveformBaselineData& baseline, 
+   std::vector<float>& time, std::vector<float>& wave,
+   std::vector<float>& windowed_time, std::vector<float>& windowed_wave) const {
+
+  ATH_MSG_DEBUG("findPeak called");
+
+  // Find max value location in array
+  unsigned int imax = std::max_element(wave.begin(), wave.end()) - wave.begin();
+  float maxval = wave[imax];
+  ATH_MSG_DEBUG( "Found peak value " << maxval << " at position " << imax );
+
+  // Check if this is over threshold (in sigma)
+  if (maxval < m_peakThreshold*baseline.rms) {
+    ATH_MSG_DEBUG("Failed threshold");
+    return false;
+  }
+
+  // Make a window around this peak, values are in bins, so units of 2ns
+  // Ensure our window is within the vector range
+  int lo_edge = ((int(imax) + m_windowStart) >= 0 ? (imax + m_windowStart) : 0); 
+  int hi_edge = ((imax + m_windowStart + m_windowWidth) < wave.size() ? (imax + m_windowStart + m_windowWidth) : wave.size());
+  
+  ATH_MSG_DEBUG("Windowing waveform from " << lo_edge << " to " << hi_edge);
+  windowed_time = std::vector<float> (time.begin()+lo_edge, time.begin()+hi_edge);
+  windowed_wave = std::vector<float> (wave.begin()+lo_edge, wave.begin()+hi_edge);
+
+  // Remove these values from the original arrays so we can iterate
+  time.erase(time.begin()+lo_edge, time.begin()+hi_edge);
+  wave.erase(wave.begin()+lo_edge, wave.begin()+hi_edge);
+  
+  return true;
+}
+
+bool
+WaveformReconstructionTool::findOverflow(const WaveformBaselineData& base, 
+	      std::vector<float>& time, std::vector<float>& wave) const {
+
+  auto peakloc = std::max_element(wave.begin(), wave.end());
+
+  // If peak value is less than baseline, we have no overflow
+  if (*peakloc < int(base.mean)) return false;
+
+  ATH_MSG_DEBUG("Removing overflows from waveform with length " << wave.size());
+
+  // We have an overflow, remove all elements that are overflowing
+  unsigned int i = peakloc - wave.begin();
+  for (; i<wave.size(); i++) {
+    if (wave[i] < int(base.mean)) continue;
+
+    ATH_MSG_DEBUG("Removing position "<< i<< " with value " << wave[i] << " > " << int(base.mean));
+    // This is an overflow, remove elements
+    time.erase(time.begin() + i);
+    wave.erase(wave.begin() + i);
+    i--;  // Decrement so that loop itertaion points to next element
+  }
+
+  ATH_MSG_DEBUG("Removed overflows, length now " << wave.size());
+  return true;
+}
+
+WaveformBaselineData&
+WaveformReconstructionTool::findSimpleBaseline(const ScintWaveform& raw_wave) const {
+
+  ATH_MSG_DEBUG( "findSimpleBaseline called" );
+  //ATH_MSG_DEBUG( raw_wave );
+
+  // This must be static so we can return a reference
+  static WaveformBaselineData baseline;  
+  baseline.clear();
+
+  // Calling algorithm checks for proper data
+  // Here we just check algorithm-specific issues
+  if (int(raw_wave.n_samples()) < m_samplesForBaselineAverage.value()) {
+    ATH_MSG_WARNING( "Found waveform with " << raw_wave.n_samples() << " samples, not enough to find baseline!" );
+    return baseline;
+  }
+
+  const std::vector<unsigned int>& counts = raw_wave.adc_counts();
+
+  double sumx = 0.;
+  double sumx2 = 0.;
+
+  // Look at first n values in the waveform
+  unsigned int nvalues = m_samplesForBaselineAverage.value();
+
+  for (unsigned int i=0; i< nvalues; i++) {
+    sumx  += counts[i];
+    sumx2 += (counts[i] * counts[i]);
+  }
+  double mean = sumx / nvalues;
+  double mean2 = sumx2 / nvalues;
+
+  double rms = std::sqrt(mean2 - mean*mean);
+
+  baseline.valid = true;
+  baseline.channel = raw_wave.channel();
+  baseline.mean = mean;
+  baseline.rms = rms;
+  return baseline;
+}
+
+WaveformBaselineData&
+WaveformReconstructionTool::findAdvancedBaseline(const ScintWaveform& raw_wave) const {
+
+  ATH_MSG_DEBUG( "findAdvancedBaseline called" );
+
+  // This must be static so we can return a reference
+  static WaveformBaselineData baseline;  
+  baseline.clear();
+
+  // First histogram to find most likely value
+  // Turn these into configurables once this works
+  int nbins = m_baselineRangeBins;
+  double xlo = 0.;
+  double xhi = m_baselineRange;
+
+  TH1F h1("", "", nbins, xlo, xhi);
+
+  // Fill this histogram with the waveform
+  for (auto value : raw_wave.adc_counts()) {
+    //ATH_MSG_DEBUG( "Found value " << value );
+    h1.Fill(value);
+  }
+
+  // Find max bin
+  int maxbin = h1.GetMaximumBin();
+  double maxbinval = h1.GetXaxis()->GetBinCenter(maxbin);
+  ATH_MSG_DEBUG( "Found max bin at " << maxbinval << " counts");
+
+  // Fill second histogram with integer resolution around the peak
+  nbins = m_baselineFitRange;
+  xlo = int(maxbinval - nbins/2)-0.5;
+  xhi = xlo + nbins;
+  ATH_MSG_DEBUG( "Filling 2nd histogram from " << xlo << " to " << xhi);
+
+  TH1F h2("", "", nbins, xlo, xhi);
+  for (auto value : raw_wave.adc_counts()) {
+    h2.Fill(value);
+  }
+
+  // Start with full histogram range
+  double mean = h2.GetMean();
+  double rms = h2.GetRMS();
+  double peak = h2.GetBinContent(h2.GetMaximumBin());
+
+  ATH_MSG_DEBUG( "Initial Mean: " << mean << " RMS: " << rms << " Peak: " << peak );
+
+  // Restrict range to +/- 2 sigma of mean
+  double window = m_baselineFitWindow;  // Window range in sigma
+  h2.GetXaxis()->SetRangeUser(mean-window*rms, mean+window*rms);
+  mean = h2.GetMean();
+  rms = h2.GetRMS();
+  peak = h2.GetBinContent(h2.GetMaximumBin());
+
+  ATH_MSG_DEBUG( "2 Sigma Mean: " << mean << " RMS: " << rms << " Peak: " << peak);
+
+  // Finally, fit a Gaussian to the 2 sigma range
+  double fitlo = mean-window*rms;
+  double fithi = mean+window*rms;
+
+  // Define fit function and preset range
+  TF1 fitfunc("BaseFit", "gaus", fitlo, fithi);
+  fitfunc.SetParameters(peak, mean, rms);
+
+  // Fit Options:
+  // L - Likelihood fit
+  // Q - Quiet mode (V for verbose)
+  // N - Don't draw or store graphics
+  // S - Return fit results
+  // E - Better error estimation
+  // M - Use TMinuit IMPROVE to find a better minimum
+  // R - Use function range  
+  TFitResultPtr fit = h2.Fit(&fitfunc, "LQNSR", "");
+
+  int fitStatus = fit;
+  double chi2 = fit->Chi2();
+  unsigned int ndf = fit->Ndf();
+  if (ndf == 0) ndf = 1;
+
+  if (!fit->IsValid()) {
+    ATH_MSG_WARNING( " Baseline fit failed! ");
+  } else {
+    // Improve estimation with fit results
+    peak = fit->Parameter(0);
+    mean = fit->Parameter(1);
+    rms  = fit->Parameter(2);
+    ATH_MSG_DEBUG( "G Fit   Mean: " << mean << " RMS: " << rms << " Peak: " << peak << " Chi2/N: " << chi2/ndf);  
+  }
+
+  baseline.valid = true;
+  baseline.channel = raw_wave.channel();
+  baseline.mean = mean;
+  baseline.rms = rms;
+  baseline.fit_status = fitStatus; 
+  baseline.peak = peak;
+  baseline.chi2n = chi2/ndf;
+  return baseline;
+}
+
+WaveformFitResult&
+WaveformReconstructionTool::findRawHitValues(const std::vector<float> time, const std::vector<float> wave) const {
+
+  ATH_MSG_DEBUG("findRawHitValues called");
+
+  // This must be static so we can return a reference
+  static WaveformFitResult rfit;
+  rfit.clear();
+
+  // First, estimate starting values from input waveform
+  // Will use this to pre-set fit values, but also as a backup in case fit fails
+  double tot = 0.;
+  double sum = 0.;
+  double sum2 = 0.;
+  for (unsigned int i=0; i<time.size(); i++) {
+    tot += wave[i];
+    sum += time[i] * wave[i];
+    sum2 += time[i] * time[i] * wave[i];
+  }
+
+  // Pointer to peak
+  auto peakloc = std::max_element(wave.begin(), wave.end());
+
+  // Initial parameters from waveform
+  rfit.mean = sum/tot;
+  rfit.sigma = std::sqrt(sum2/tot - rfit.mean*rfit.mean);
+  rfit.peak = *peakloc;
+  rfit.integral = tot;
+  rfit.time = rfit.mean;
+
+  ATH_MSG_DEBUG( "Initial Mean: " << rfit.mean << " RMS: " << rfit.sigma << " Peak: " << rfit.peak);  
+
+  return rfit;
+}
+
+WaveformFitResult&
+WaveformReconstructionTool::fitGaussian(const WaveformFitResult& raw, const std::vector<float> time, const std::vector<float> wave) const {
+
+  ATH_MSG_DEBUG("fitGaussian called");
+
+  // This must be static so we can return a reference
+  static WaveformFitResult gfit;
+  gfit.clear();
+
+  // Start with raw values by default
+  gfit = raw;
+
+  // Start by creating a TGraph and fitting this with a Gaussian
+  // Must pass arrays to TGraph, use pointer to first vector element
+  TGraph tg(time.size(), &time[0], &wave[0]);
+
+  // Define fit function and preset range
+  TF1 gfunc("gfunc", "gaus");
+  gfunc.SetParameters(raw.peak, raw.mean, raw.sigma);
+  gfunc.SetParError(0, std::sqrt(raw.peak));
+  gfunc.SetParError(1, raw.sigma);
+  gfunc.SetParError(2, raw.sigma / 10.);
+  gfunc.SetParLimits(2, 0., 20.);  // Constrain width
+
+  TFitResultPtr gfitptr = tg.Fit(&gfunc, "QNS", "");
+
+  gfit.fit_status = gfitptr;
+  gfit.valid = (gfit.fit_status == 0);
+
+  if (!gfitptr->IsValid()) {
+    ATH_MSG_WARNING( " Gaussian waveform fit failed! ");
+  } else {
+    // Improve estimation with fit results
+    gfit.peak = gfitptr->Parameter(0);
+    gfit.mean = gfitptr->Parameter(1);
+    gfit.sigma = gfitptr->Parameter(2);
+    gfit.integral = gfunc.Integral(time.front(), time.back());
+
+    // Find time here
+    gfit.time = gfunc.GetX( gfit.peak * m_timingPeakFraction, time.front(), gfit.mean );
+
+    ATH_MSG_DEBUG("G Fit   Mean: " << gfit.mean << " RMS: " << gfit.sigma 
+		  << " Peak: " << gfit.peak << " Int: " << gfit.integral << " Time: " << gfit.time);
+  }
+
+  return gfit;
+}
+
+WaveformFitResult&
+WaveformReconstructionTool::fitCBall(const WaveformFitResult& gfit, 
+     const std::vector<float> time, const std::vector<float> wave) const {
+
+  ATH_MSG_DEBUG("fitCBall called");
+
+  // This must be static so we can return a reference
+  static WaveformFitResult cbfit;
+  cbfit.clear();
+
+  // Must pass arrays to TGraph, use pointer to first vector element
+  TGraph tg(time.size(), &time[0], &wave[0]);
+
+  // Values to preset CB fit
+  cbfit.peak = abs(gfit.peak);
+  cbfit.mean = gfit.mean;
+  cbfit.sigma = abs(gfit.sigma);
+  if (cbfit.sigma > 20.) cbfit.sigma = 5.;
+  cbfit.alpha = -0.25;  // Negative to get tail on high side
+  cbfit.nval = 3.;
+
+  // Define fit function and preset values
+  TF1 cbfunc("cbfunc", "crystalball");
+  cbfunc.SetParameter(0, cbfit.peak); // Peak height
+  cbfunc.SetParError(0, std::sqrt(cbfit.peak));
+  cbfunc.SetParameter(1, cbfit.mean); // Mean
+  cbfunc.SetParError(1, cbfit.sigma);
+  cbfunc.SetParameter(2, cbfit.sigma);  // Width
+  cbfunc.SetParError(2, cbfit.sigma/10.);
+  cbfunc.SetParLimits(2, 0., 20.);
+  cbfunc.SetParameter(3, cbfit.alpha); // Tail parameter (negative for high-side tail)
+  cbfunc.SetParError(3, -cbfit.alpha/10.);
+  cbfunc.SetParLimits(3, -10., 0.);
+  cbfunc.SetParameter(4, cbfit.nval);  // Tail power
+  cbfunc.SetParError(4, cbfit.nval/10.);
+  cbfunc.SetParLimits(4, 0., 1.E3);
+
+  TFitResultPtr cbfitptr = tg.Fit(&cbfunc, "QNS", "");
+
+  cbfit.fit_status = cbfitptr;
+  cbfit.valid = (cbfit.fit_status == 0);
+
+  if (!cbfitptr->IsValid()) {
+    ATH_MSG_WARNING( " Crystal Ball waveform fit failed! ");
+  } else {
+    // Improve estimation with fit results
+    cbfit.peak = cbfitptr->Parameter(0);
+    cbfit.mean = cbfitptr->Parameter(1);
+    cbfit.sigma = cbfitptr->Parameter(2);
+    cbfit.alpha = cbfitptr->Parameter(3);
+    cbfit.nval = cbfitptr->Parameter(4);
+
+    double chi2 = cbfitptr->Chi2();
+    unsigned int ndf = cbfitptr->Ndf();
+    if (ndf == 0) ndf = 1;
+    cbfit.chi2ndf = chi2/ndf;
+
+    cbfit.integral = cbfunc.Integral(time.front(), time.back());
+
+    // Find time here
+    cbfit.time = cbfunc.GetX( cbfit.peak * m_timingPeakFraction, time.front(), cbfit.mean );
+
+    ATH_MSG_DEBUG("CB Fit  Mean: " << cbfit.mean << " RMS: " << cbfit.sigma 
+		  << " Peak: " << cbfit.peak << " Int: " << cbfit.integral
+		  << " Time: " << cbfit.time 
+		  << " N: " << cbfit.nval << " Alpha: " << cbfit.alpha 
+		  << " Chi2/Ndf: " << cbfit.chi2ndf );
+
+  }
+
+  return cbfit;
+}
diff --git a/Scintillator/ScintRecTools/src/WaveformReconstructionTool.h b/Scintillator/ScintRecTools/src/WaveformReconstructionTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..45a1e9c79d1229e337e6223090959d6b955f7b03
--- /dev/null
+++ b/Scintillator/ScintRecTools/src/WaveformReconstructionTool.h
@@ -0,0 +1,117 @@
+/*
+   Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/** @file WaveformReconstructionTool.h
+ *  Header file for WaveformReconstructionTool.h
+ *
+ */
+#ifndef SCINTRECTOOLS_WAVEFORMRECONSTRUCTIONTOOL_H
+#define SCINTRECTOOLS_WAVEFORMRECONSTRUCTIONTOOL_H
+
+//Athena
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "ScintRecTools/IWaveformReconstructionTool.h"
+
+#include "ScintRawEvent/ScintWaveform.h"
+
+#include "WaveformBaselineData.h"
+#include "WaveformFitResult.h"
+
+//Gaudi
+#include "GaudiKernel/ToolHandle.h"
+
+//STL
+#include <string>
+#include <vector>
+
+class WaveformReconstructionTool: public extends<AthAlgTool, IWaveformReconstructionTool> {
+ public:
+
+  /// Normal constructor for an AlgTool; 'properties' are also declared here
+  WaveformReconstructionTool(const std::string& type, 
+			     const std::string& name, const IInterface* parent);
+
+  /// Retrieve the necessary services in initialize
+  StatusCode initialize();
+
+  /// Reconstruct hits from waveform
+  
+  virtual StatusCode reconstruct(const ScintWaveform& wave,
+				 const xAOD::WaveformClock* clock,
+				 xAOD::WaveformHitContainer* container) const;
+
+ private:
+
+  //
+  // Baseline Estimation Parameters
+  BooleanProperty m_useSimpleBaseline{this, "UseSimpleBaseline", false};
+  IntegerProperty m_samplesForBaselineAverage{this, "SamplesForBaselineAverage", 40};
+
+  //
+  // Parameters of initial histogram to find baseline max
+  // Range and bins to use, ratio should be an integer (bin width)
+  IntegerProperty m_baselineRange{this, "BaselineRange", 16000};
+  IntegerProperty m_baselineRangeBins{this, "BaselineRangeBins", 320};
+
+  // 
+  // Parameters for the Gaussian fit to the baseline peak (in counts)
+  // Range is total range to use to find truncated mean and width
+  IntegerProperty m_baselineFitRange{this, "BaselineFitRange", 200};
+  // Fit window is value (in sigma) of trucated width to use in final fit
+  FloatProperty m_baselineFitWindow{this, "BaselineFitWindow", 2.};
+
+  //
+  // Peak threshold (in sigma of baseline RMS) to find a hit
+  FloatProperty m_peakThreshold{this, "PeakThreshold", 10.};
+ 
+  //
+  // Window to define fitting range, in samples (2ns/sample)
+  IntegerProperty m_windowStart{this, "FitWindowStart", -10};
+  IntegerProperty m_windowWidth{this, "FitWindowWidth", 50};
+
+  //
+  // Remove overflow values from CB fit
+  BooleanProperty m_removeOverflow{this, "RemoveOverflow", true};
+
+  //
+  // Look for more than one hit in each channel
+  BooleanProperty m_findMultipleHits{this, "FindMultipleHits", false};
+
+  //
+  // Fraction of peak to set local hit time
+  FloatProperty m_timingPeakFraction{this, "TimingPeakFraction", 0.2};
+
+  // Baseline algorithms
+  WaveformBaselineData& findSimpleBaseline(const ScintWaveform& wave) const;
+  WaveformBaselineData& findAdvancedBaseline(const ScintWaveform& wave) const;
+
+  // Find peak in wave, return windowed region in windowed_time and windowed_wave
+  // Windowed region is removed from original vectors
+  // Returns true if peak found, false if not
+  bool findPeak(WaveformBaselineData& baseline,
+		std::vector<float>& time, std::vector<float>& wave,
+		std::vector<float>& windowed_time, std::vector<float>& windowed_wave) const;
+
+  // Get estimate from waveform data itself
+  WaveformFitResult& findRawHitValues(const std::vector<float> time, 
+				      const std::vector<float> wave) const;
+
+  // Fit windowed data to Gaussian (to get initial estimate of parameters
+  WaveformFitResult& fitGaussian(const WaveformFitResult& raw,
+				 const std::vector<float> time, 
+				 const std::vector<float> wave) const;
+
+  // Find overflows and remove points from arrays
+  bool findOverflow(const WaveformBaselineData& baseline, 
+		    std::vector<float>& time, std::vector<float>& wave) const;
+
+  // Fit windowed data to CrystalBall function
+  WaveformFitResult& fitCBall(const WaveformFitResult& gfit, 
+			      const std::vector<float> time, 
+			      const std::vector<float> wave) const;
+
+
+};
+
+#endif // SCINTRECTOOLS_WAVEFORMRECONSTRUCTIONTOOL_H
diff --git a/Scintillator/ScintRecTools/src/components/ScintRecTools_entries.cxx b/Scintillator/ScintRecTools/src/components/ScintRecTools_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a96a42c9b5e4a0007558650314ef6e1144281877
--- /dev/null
+++ b/Scintillator/ScintRecTools/src/components/ScintRecTools_entries.cxx
@@ -0,0 +1,5 @@
+#include "../ClockReconstructionTool.h"
+#include "../WaveformReconstructionTool.h"
+
+DECLARE_COMPONENT( ClockReconstructionTool )
+DECLARE_COMPONENT( WaveformReconstructionTool )
diff --git a/xAOD/xAODFaserWaveform/CMakeLists.txt b/xAOD/xAODFaserWaveform/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1185251139a20ea833a672371173ad00c2987ccd
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+
+# Declare the package name.
+atlas_subdir( xAODFaserWaveform )
+
+# External dependencies.
+find_package( xAODUtilities )
+
+# Component(s) in the package.
+atlas_add_library( xAODFaserWaveform
+   xAODFaserWaveform/*.h xAODFaserWaveform/versions/*.h Root/*.cxx
+   PUBLIC_HEADERS xAODFaserWaveform
+   LINK_LIBRARIES xAODCore )
+
+atlas_add_xaod_smart_pointer_dicts(
+   INPUT xAODFaserWaveform/selection.xml
+   OUTPUT _selectionFile
+   OBJECTS "xAOD::WaveformClock_v1"
+   CONTAINERS "xAOD::WaveformHitContainer_v1")
+
+atlas_add_dictionary( xAODFaserWaveformDict
+   xAODFaserWaveform/xAODFaserWaveformDict.h
+   ${_selectionFile}
+   LINK_LIBRARIES xAODCore xAODFaserWaveform
+   EXTRA_FILES Root/dict/*.cxx )
+
diff --git a/xAOD/xAODFaserWaveform/Root/WaveformClockAuxInfo_v1.cxx b/xAOD/xAODFaserWaveform/Root/WaveformClockAuxInfo_v1.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..61d5dc8e8d736c44ceba480964551bed85ccf981
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/Root/WaveformClockAuxInfo_v1.cxx
@@ -0,0 +1,18 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// Local include(s):
+#include "xAODFaserWaveform/versions/WaveformClockAuxInfo_v1.h"
+
+namespace xAOD {
+
+  WaveformClockAuxInfo_v1::WaveformClockAuxInfo_v1()
+    : AuxInfoBase(), 
+      frequency(0), phase(0), dc_offset(0), amplitude(0) {
+    AUX_VARIABLE( frequency );
+    AUX_VARIABLE( phase );
+    AUX_VARIABLE( dc_offset );
+    AUX_VARIABLE( amplitude );
+  }
+} // namespace xAOD
diff --git a/xAOD/xAODFaserWaveform/Root/WaveformClock_v1.cxx b/xAOD/xAODFaserWaveform/Root/WaveformClock_v1.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..04498ccc8ecfc6e0a2c62d1c404318988229b891
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/Root/WaveformClock_v1.cxx
@@ -0,0 +1,47 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// EDM include(s):
+#include "xAODCore/AuxStoreAccessorMacros.h"
+
+// Local include(s):
+#include "xAODFaserWaveform/versions/WaveformClock_v1.h"
+
+namespace xAOD {
+
+  WaveformClock_v1::WaveformClock_v1() : SG::AuxElement() {
+  }
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformClock_v1, double, frequency, set_frequency )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformClock_v1, double, phase, set_phase )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformClock_v1, double, dc_offset, set_dc_offset )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformClock_v1, double, amplitude, set_amplitude )
+
+  float WaveformClock_v1::time_from_clock(float time) const {
+    // Figure out which integer cycle we are on, must add 1/4 of a cycle
+    // because FFT finds phase of cosine, which would be middle of 
+    // clock HI region.  frequency is in MHz, so convert here to GHz
+    int ncycle = int(time*frequency() / 1.E3 + phase() / (2*M_PI) + 0.25);
+    float dt = time - (ncycle - phase() / (2*M_PI) - 0.25) * 1.E3 / frequency();
+    return dt;
+  }
+
+} // namespace xAOD
+
+namespace xAOD {
+
+  std::ostream& operator<<(std::ostream& s, const xAOD::WaveformClock_v1& clk) {
+    s << "xAODWaveformClock: frequency=" << clk.frequency()
+      << " phase=" << clk.phase()
+      << " amplitude=" << clk.amplitude()
+      << " offset=" << clk.dc_offset()
+      << std::endl;
+
+    return s;
+  }
+
+} // namespace xAOD
diff --git a/xAOD/xAODFaserWaveform/Root/WaveformHitAuxContainer_v1.cxx b/xAOD/xAODFaserWaveform/Root/WaveformHitAuxContainer_v1.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..76aed56247567f13e39fbcbf7471950dc93b3c23
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/Root/WaveformHitAuxContainer_v1.cxx
@@ -0,0 +1,35 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// Local include(s):
+#include "xAODFaserWaveform/versions/WaveformHitAuxContainer_v1.h"
+
+namespace xAOD {
+
+  WaveformHitAuxContainer_v1::WaveformHitAuxContainer_v1() 
+    : AuxContainerBase() {
+
+    AUX_VARIABLE(channel);
+    AUX_VARIABLE(localtime);
+    AUX_VARIABLE(peak);
+    AUX_VARIABLE(width);
+    AUX_VARIABLE(integral);
+    AUX_VARIABLE(bcid_time);
+    AUX_VARIABLE(raw_peak);
+    AUX_VARIABLE(raw_integral);
+
+    AUX_VARIABLE(baseline_mean);
+    AUX_VARIABLE(baseline_rms);
+
+    AUX_VARIABLE(status);
+    AUX_VARIABLE(mean);
+    AUX_VARIABLE(alpha);
+    AUX_VARIABLE(nval);
+    AUX_VARIABLE(time_vector);
+    AUX_VARIABLE(wave_vector);
+
+  }
+
+} // namespace xAOD
+
diff --git a/xAOD/xAODFaserWaveform/Root/WaveformHit_v1.cxx b/xAOD/xAODFaserWaveform/Root/WaveformHit_v1.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4d4ce278978e68024637c117bc1e9456c2e22730
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/Root/WaveformHit_v1.cxx
@@ -0,0 +1,61 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// EDM include(s):
+#include "xAODCore/AuxStoreAccessorMacros.h"
+
+// Local include(s):
+#include "xAODFaserWaveform/versions/WaveformHit_v1.h"
+
+namespace xAOD {
+
+  WaveformHit_v1::WaveformHit_v1() : SG::AuxElement() {
+  }
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, unsigned int, channel, set_channel )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, localtime, set_localtime )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, peak, set_peak )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, width, set_width )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, integral, set_integral )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, bcid_time, set_bcid_time )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, raw_peak, set_raw_peak )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, raw_integral, set_raw_integral )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, baseline_mean, set_baseline_mean )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, baseline_rms, set_baseline_rms )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, unsigned int, status, set_status )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, mean, set_mean )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, alpha, set_alpha )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, float, nval, set_nval )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, std::vector<float>, time_vector, set_time_vector )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( WaveformHit_v1, std::vector<float>, wave_vector, set_wave_vector )
+
+} // namespace xAOD
+
+namespace xAOD {
+
+  std::ostream& operator<<(std::ostream& s, const xAOD::WaveformHit_v1& hit) {
+    s << "xAODWaveformHit: channel=" << hit.channel()
+      << " local time=" << hit.localtime()
+      << " peak=" << hit.peak()
+      << std::endl;
+
+    return s;
+  }
+
+} // namespace xAOD
diff --git a/xAOD/xAODFaserWaveform/Root/dict/ContainerProxies.cxx b/xAOD/xAODFaserWaveform/Root/dict/ContainerProxies.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..5ef691ac9c9cd4e7091a9ed1d6640090d69bc016
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/Root/dict/ContainerProxies.cxx
@@ -0,0 +1,8 @@
+// EDM include(s):
+#include "xAODCore/AddDVProxy.h"
+
+// Local include(s):
+#include "xAODFaserWaveform/WaveformHitContainer.h"
+
+// Set up the collection proxies:
+ADD_NS_DV_PROXY( xAOD, WaveformHitContainer );
diff --git a/xAOD/xAODFaserWaveform/Root/xAODFaserWaveformCLIDs.cxx b/xAOD/xAODFaserWaveform/Root/xAODFaserWaveformCLIDs.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..acd57bce0d941b16c815e9805b7da8e76bf2ef27
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/Root/xAODFaserWaveformCLIDs.cxx
@@ -0,0 +1,10 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+//simple includes to force the CLASS_DEF etc to be encountered during compile
+
+#include "xAODFaserWaveform/WaveformHitContainer.h"
+#include "xAODFaserWaveform/WaveformHitAuxContainer.h"
+#include "xAODFaserWaveform/WaveformClock.h"
+#include "xAODFaserWaveform/WaveformClockAuxInfo.h"
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformClock.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformClock.h
new file mode 100644
index 0000000000000000000000000000000000000000..951d21a7a706e01c11b642490e4d71f7d019a8af
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformClock.h
@@ -0,0 +1,22 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_WAVEFORMCLOCK_H
+#define XAODFASERWAVEFORM_WAVEFORMCLOCK_H
+
+// Local include(s):
+#include "xAODFaserWaveform/versions/WaveformClock_v1.h"
+
+namespace xAOD {
+  /// Declare the latest version of the class
+  typedef WaveformClock_v1 WaveformClock;
+}
+
+// Set up a CLID for the container:
+#include "xAODCore/CLASS_DEF.h"
+CLASS_DEF( xAOD::WaveformClock, 58376762, 1 )
+
+#endif // XAODFASERWAVEFORM_WAVEFORMCLOCK_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformClockAuxInfo.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformClockAuxInfo.h
new file mode 100644
index 0000000000000000000000000000000000000000..5cdc5294f041b7370ca745e09dfe55223b47b3f9
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformClockAuxInfo.h
@@ -0,0 +1,22 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_WAVEFORMCLOCKAUXINFO_H
+#define XAODFASERWAVEFORM_WAVEFORMCLOCKAUXINFO_H
+
+// Local include(s):
+#include "xAODFaserWaveform/versions/WaveformClockAuxInfo_v1.h"
+
+namespace xAOD {
+  /// Declare the latest version of the class
+  typedef WaveformClockAuxInfo_v1 WaveformClockAuxInfo;
+}
+
+// Set up a CLID for the container:
+#include "xAODCore/CLASS_DEF.h"
+CLASS_DEF( xAOD::WaveformClockAuxInfo, 150155999, 1 )
+
+#endif // XAODFASERWAVEFORM_WAVEFORMCLOCKAUXINFO_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformHit.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformHit.h
new file mode 100644
index 0000000000000000000000000000000000000000..6f01f4fc47ca8e213dbd8634ea7444b5db0270b1
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformHit.h
@@ -0,0 +1,22 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_WAVEFORMHIT_H
+#define XAODFASERWAVEFORM_WAVEFORMHIT_H
+
+// Local include(s):
+#include "xAODFaserWaveform/versions/WaveformHit_v1.h"
+
+namespace xAOD {
+  /// Declare the latest version of the class
+  typedef WaveformHit_v1 WaveformHit;
+}
+
+// Set up a CLID for the container:
+#include "xAODCore/CLASS_DEF.h"
+CLASS_DEF( xAOD::WaveformHit, 131600577, 1 )
+
+#endif // XAODFASERWAVEFORM_WAVEFORMHIT_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformHitAuxContainer.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformHitAuxContainer.h
new file mode 100644
index 0000000000000000000000000000000000000000..014da4c02d3c80f2f2068f5994579deb33ecf056
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformHitAuxContainer.h
@@ -0,0 +1,22 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_WAVEFORMHITAUXCONTAINER_H
+#define XAODFASERWAVEFORM_WAVEFORMHITAUXCONTAINER_H
+
+// Local include(s):
+#include "xAODFaserWaveform/versions/WaveformHitAuxContainer_v1.h"
+
+namespace xAOD {
+  /// Declare the latest version of the class
+  typedef WaveformHitAuxContainer_v1 WaveformHitAuxContainer;
+}
+
+// Set up a CLID for the container:
+#include "xAODCore/CLASS_DEF.h"
+CLASS_DEF( xAOD::WaveformHitAuxContainer, 1262669778, 1 )
+
+#endif // XAODFASERWAVEFORM_WAVEFORMHITAUXCONTAINER_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformHitContainer.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformHitContainer.h
new file mode 100644
index 0000000000000000000000000000000000000000..41de79d21d8fa88389710a9dbef214653bc6edf1
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/WaveformHitContainer.h
@@ -0,0 +1,22 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_WAVEFORMHITCONTAINER_H
+#define XAODFASERWAVEFORM_WAVEFORMHITCONTAINER_H
+
+// Local include(s):
+#include "xAODFaserWaveform/versions/WaveformHitContainer_v1.h"
+
+namespace xAOD {
+  /// Declare the latest version of the class
+  typedef WaveformHitContainer_v1 WaveformHitContainer;
+}
+
+// Set up a CLID for the container:
+#include "xAODCore/CLASS_DEF.h"
+CLASS_DEF( xAOD::WaveformHitContainer, 1156844391, 1 )
+
+#endif // XAODFASERWAVEFORM_WAVEFORMHITCONTAINER_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/selection.xml b/xAOD/xAODFaserWaveform/xAODFaserWaveform/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..44146cd55fad3798ba00f365c671949e36e38423
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/selection.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2020 CERN for the benefit of the FASER collaboration -->
+<lcgdict>
+
+  <class name="xAOD::WaveformHit_v1" />
+  <typedef name="xAOD::WaveformHit" />
+
+  <class name="xAOD::WaveformHitContainer_v1" 
+	 id="fb49e082-7130-462f-9500-de9a2aef8a24" />
+  <typedef name="xAOD::WaveformHitContainer" />
+
+  <class name="xAOD::WaveformHitAuxContainer_v1" 
+	 id="091007bd-f4ab-4862-b905-1a0ed96c962f" />
+  <typedef name="xAOD::WaveformHitAuxContainer" />
+
+  <class name="xAOD::WaveformClock_v1" 
+	 id="461aa5ca-6c72-46ee-b5d5-5bd6df4e2848" />
+  <typedef name="xAOD::WaveformClock" />
+
+  <class name="xAOD::WaveformClockAuxInfo_v1" 
+	 id="fe0600cc-f2f3-4be5-9723-257646dbb8f7" />
+  <typedef name="xAOD::WaveformClockAuxInfo" />
+
+
+</lcgdict>
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformClockAuxInfo_v1.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformClockAuxInfo_v1.h
new file mode 100644
index 0000000000000000000000000000000000000000..24741e2476ab952f316a8ceeaf7611361bdd291d
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformClockAuxInfo_v1.h
@@ -0,0 +1,45 @@
+// Dear emacs, this is -*- c++ -*- 
+
+/* 
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_VERSIONS_WAVEFORMCLOCKAUXINFO_V1_H
+#define XAODFASERWAVEFORM_VERSIONS_WAVEFORMCLOCKAUXINFO_V1_H
+
+// System include(s):
+extern "C" {
+#   include "stdint.h"
+}
+
+// xAOD include(s):
+#include "xAODCore/AuxInfoBase.h"
+
+namespace xAOD {
+
+  /// Class holding the data handled by WaveformClock_v1
+
+  class WaveformClockAuxInfo_v1 : public AuxInfoBase {
+
+  public:
+    /// Default constructor
+    WaveformClockAuxInfo_v1();
+
+  private:
+    /// @name Basic variables
+    ///@ {
+    double frequency;
+    double phase;
+    double dc_offset;
+    double amplitude;
+    ///@}
+
+  }; // class WaveformClockAuxInfo_v1
+
+} // namespace xAOD
+
+// Set up a CLID and StoreGate inheritance for the class:
+#include "xAODCore/BaseInfo.h"
+SG_BASE( xAOD::WaveformClockAuxInfo_v1, xAOD::AuxInfoBase );
+
+#endif // XAODFASERWAVEFORM_VERSIONS_WAVEFORMCLOCKAUXINFO_V1_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformClock_v1.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformClock_v1.h
new file mode 100644
index 0000000000000000000000000000000000000000..437b3ecabf1f71c9be0c353d27bebec1d0916d11
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformClock_v1.h
@@ -0,0 +1,61 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_VERSIONS_WAVEFORMCLOCK_V1_H
+#define XAODFASERWAVEFORM_VERSIONS_WAVEFORMCLOCK_V1_H
+
+// System include(s):
+extern "C" {
+#   include "stdint.h"
+}
+
+// Core EDM include(s):
+#include "AthContainers/AuxElement.h"
+
+namespace xAOD {
+
+  // Cllss describing pulses in the waveform digitizer
+  class WaveformClock_v1 : public SG::AuxElement {
+
+  public:
+    /// Defaullt constructor
+    WaveformClock_v1();
+
+    /// @name Access WaveformClock elements
+    /// @{
+
+    /// Clock Frequency (in MHz)
+    double frequency() const;
+    void set_frequency(double value);
+
+    /// Clock Phase (in Radians)
+    double phase() const;
+    void set_phase(double value);
+
+    /// DC Clock offset
+    double dc_offset() const;
+    void set_dc_offset(double value);
+
+    /// Amplitude of primary freq. component
+    double amplitude() const;
+    void set_amplitude(double value);
+
+    /// Distance of time (in ns) from previous rising clock edge
+    float time_from_clock(float time) const;
+
+    /// @}
+
+  }; // class WaveformClock_v1
+
+  std::ostream& operator<<(std::ostream& s, const xAOD::WaveformClock_v1& clk);
+
+} // namespace xAOD
+
+// Declare the inheritance of the type:
+#include "xAODCore/BaseInfo.h"
+SG_BASE( xAOD::WaveformClock_v1, SG::AuxElement );
+
+#endif // XAODFASERWAVEFORM_VERSIONS_WAVEFORMCLOCK_V1_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformHitAuxContainer_v1.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformHitAuxContainer_v1.h
new file mode 100644
index 0000000000000000000000000000000000000000..0ea61b39a567a61882a390aa35277363771e970f
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformHitAuxContainer_v1.h
@@ -0,0 +1,63 @@
+// Dear emacs, this is -*- c++ -*-                                              
+
+/*                                                                              
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration            
+*/
+
+#ifndef XAODFASERWAVEFORM_VERSIONS_WAVEFORMHITAUXCONTAINER_V1_H
+#define XAODFASERWAVEFORM_VERSIONS_WAVEFORMHITAUXCONTAINER_V1_H
+
+// STL include(s):
+#include <vector>
+
+// EDM include(s):
+#include "xAODCore/AuxContainerBase.h"
+
+namespace xAOD {
+
+  /// Auxiliary container for WaveformHit containers
+
+  class WaveformHitAuxContainer_v1 : public AuxContainerBase {
+
+  public:
+    /// Default constructor
+    WaveformHitAuxContainer_v1();
+    /// Destructor
+    ~WaveformHitAuxContainer_v1() {}
+
+  private:
+    /// @name Basic variables
+    ///@ {
+    std::vector<unsigned int> channel;
+    std::vector<float> localtime;
+    std::vector<float> peak;
+    std::vector<float> width;
+    std::vector<float> integral;
+    std::vector<float> bcid_time;
+
+    std::vector<float> raw_peak;
+    std::vector<float> raw_integral;
+
+    std::vector<float> baseline_mean;
+    std::vector<float> baseline_rms;
+
+    std::vector<unsigned int> status;
+
+    std::vector<float> mean;
+    std::vector<float> alpha;
+    std::vector<float> nval;
+
+    std::vector<std::vector<float>> time_vector;
+    std::vector<std::vector<float>> wave_vector;
+
+    ///@}
+
+  }; // class WaveformHitAuxContainer_v1
+
+} // namespace xAOD
+
+// Set up a CLID and StoreGate inheritance for the class:
+#include "xAODCore/BaseInfo.h"
+SG_BASE( xAOD::WaveformHitAuxContainer_v1, xAOD::AuxContainerBase );
+
+#endif // XAODFASERWAVEFORM_VERSIONS_WAVEFORMHITAUXCONTAINER_V1_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformHitContainer_v1.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformHitContainer_v1.h
new file mode 100644
index 0000000000000000000000000000000000000000..180cf9719cacaded61292aa67d401d38472016cd
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformHitContainer_v1.h
@@ -0,0 +1,26 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_VERSIONS_WAVEFORMHITCONTAINER_V1_H
+#define XAODFASERWAVEFORM_VERSIONS_WAVEFORMHITCONTAINER_V1_H
+
+// System include(s):
+extern "C" {
+#   include "stdint.h"
+}
+
+// EDM include(s):
+#include "AthContainers/DataVector.h"
+
+// Local includes:
+#include "xAODFaserWaveform/versions/WaveformHit_v1.h"
+
+namespace xAOD {
+  // Define the container as a simple DataVector
+  typedef DataVector<WaveformHit_v1> WaveformHitContainer_v1;
+}
+
+#endif // XAODFASERWAVEFORM_VERSIONS_WAVEFORMHITCONTAINER_V1_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformHit_v1.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformHit_v1.h
new file mode 100644
index 0000000000000000000000000000000000000000..976c51512ae734902db9a5b3e30a309c5610723e
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/versions/WaveformHit_v1.h
@@ -0,0 +1,135 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_VERSIONS_WAVEFORMHIT_V1_H
+#define XAODFASERWAVEFORM_VERSIONS_WAVEFORMHIT_V1_H
+
+// System include(s):
+extern "C" {
+#   include "stdint.h"
+}
+
+#include <vector>
+
+// Core EDM include(s):
+#include "AthContainers/AuxElement.h"
+
+namespace xAOD {
+
+  // Meaning of status bits
+  enum WaveformStatus {
+    THRESHOLD_FAILED=0,  // No hit found over threshold
+    SECONDARY,           // Second or more hit found in raw waveform
+    WAVE_OVERFLOW,       // Overflows removed from fit
+    BASELINE_FAILED,     // Baseline determination failed
+    GFIT_FAILED,         // Gaussian fit failed
+    CBFIT_FAILED,        // CrystalBall fit failed
+    CLOCK_INVALID,
+    WAVEFORM_MISSING,    // Input waveform data missing
+    WAVEFORM_INVALID     // Input waveform data length mismatch
+  };
+
+  // Cllss describing pulses in the waveform digitizer
+  class WaveformHit_v1 : public SG::AuxElement {
+
+  public:
+    /// Defaullt constructor
+    WaveformHit_v1();
+
+    /// @name Access WaveformHit elements
+    /// @{
+
+    /// Waveform channel
+    unsigned int channel() const;
+    void set_channel(unsigned int value);
+
+    /// Best results
+    float localtime() const;
+    void set_localtime(float value);
+
+    float peak() const;
+    void set_peak(float value);
+
+    float width() const;
+    void set_width(float value);
+
+    float integral() const;
+    void set_integral(float value);
+
+    /// Time from previous clock edge
+    float bcid_time() const;
+    void set_bcid_time(float value);
+
+    /// Raw values from waveform
+    float raw_peak() const;
+    void set_raw_peak(float value);
+
+    float raw_integral() const;
+    void set_raw_integral(float value);
+
+    /// Bsaeline mean
+    float baseline_mean() const;
+    void set_baseline_mean(float value);
+
+    /// Baseline rms
+    float baseline_rms() const;
+    void set_baseline_rms(float value);
+
+    /// Status word
+    unsigned int status() const;
+    void set_status(unsigned int value);
+
+    /// Other fit results
+    float mean() const;
+    void set_mean(float value);
+
+    /// Crystal Ball fit parameters
+    float alpha() const;
+    void set_alpha(float value);
+
+    float nval() const;
+    void set_nval(float value);
+
+    /// Raw time and waveform data
+    std::vector<float> time_vector() const;
+    void set_time_vector(std::vector<float> value);
+
+    std::vector<float> wave_vector() const;
+    void set_wave_vector(std::vector<float> value);
+
+    /// Status bit access functions
+    void set_status_bit(WaveformStatus bit) {
+      this->set_status(this->status() | (1<<bit));
+    }
+    bool status_bit(WaveformStatus bit) const {
+      return (this->status() | (1<<bit));
+    }
+
+    bool threshold() const {
+      return !(this->status_bit(xAOD::WaveformStatus::THRESHOLD_FAILED));
+    }
+
+    bool overflow() const {
+      return (this->status_bit(xAOD::WaveformStatus::WAVE_OVERFLOW));
+    }
+
+    bool secondary() const {
+      return this->status_bit(xAOD::WaveformStatus::SECONDARY);
+    }
+
+    /// @}
+
+  }; // class WaveformHit_v1
+
+  std::ostream& operator<<(std::ostream& s, const xAOD::WaveformHit_v1& hit);
+}
+
+// Declare the inheritance of the type:
+#include "xAODCore/BaseInfo.h"
+SG_BASE( xAOD::WaveformHit_v1, SG::AuxElement );
+
+
+#endif // XAODFASERWAVEFORM_VERSIONS_WAVEFORMHIT_V1_H
diff --git a/xAOD/xAODFaserWaveform/xAODFaserWaveform/xAODFaserWaveformDict.h b/xAOD/xAODFaserWaveform/xAODFaserWaveform/xAODFaserWaveformDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..5bb2eceb5665288f05ed3481de0d201d825989ff
--- /dev/null
+++ b/xAOD/xAODFaserWaveform/xAODFaserWaveform/xAODFaserWaveformDict.h
@@ -0,0 +1,33 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORM_XAODFASERWAVEFORMDICT_H
+#define XAODFASERWAVEFORM_XAODFASERWAVEFORMDICT_H
+
+// Local includes
+#include "xAODFaserWaveform/WaveformHit.h"
+#include "xAODFaserWaveform/WaveformHitContainer.h"
+#include "xAODFaserWaveform/WaveformHitAuxContainer.h"
+
+#include "xAODFaserWaveform/WaveformClock.h"
+#include "xAODFaserWaveform/WaveformClockAuxInfo.h"
+
+#include "xAODFaserWaveform/versions/WaveformHit_v1.h"
+#include "xAODFaserWaveform/versions/WaveformHitContainer_v1.h"
+#include "xAODFaserWaveform/versions/WaveformHitAuxContainer_v1.h"
+
+#include "xAODFaserWaveform/versions/WaveformClock_v1.h"
+#include "xAODFaserWaveform/versions/WaveformClockAuxInfo_v1.h"
+
+// EDM include(s).
+#include "xAODCore/tools/DictHelpers.h"
+
+namespace {
+  struct GCCXML_DUMMY_INSTANTIATION_XAODFASERWAVEFORM {
+    XAOD_INSTANTIATE_NS_CONTAINER_TYPES( xAOD, WaveformHitContainer_v1 );
+    XAOD_INSTANTIATE_NS_OBJECT_TYPES( xAOD, WaveformClock_v1 );
+  };
+}
+
+#endif // XAODFASERWAVEFORM_XAODFASERWAVEFORMDICT_H
diff --git a/xAOD/xAODFaserWaveformAthenaPool/CMakeLists.txt b/xAOD/xAODFaserWaveformAthenaPool/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..99f9ee00fdbfd45e353401e1c6480e1ec0a0d370
--- /dev/null
+++ b/xAOD/xAODFaserWaveformAthenaPool/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+
+# Declare the package name.
+atlas_subdir( xAODFaserWaveformAthenaPool )
+
+# Component(s) in the package:
+atlas_add_poolcnv_library( xAODFaserWaveformAthenaPoolPoolCnv
+   src/*.h src/*.cxx
+   FILES xAODFaserWaveform/WaveformClock.h xAODFaserWaveform/WaveformClockAuxInfo.h xAODFaserWaveform/WaveformHitContainer.h xAODFaserWaveform/WaveformHitAuxContainer.h
+   TYPES_WITH_NAMESPACE xAOD::WaveformClock xAOD::WaveformClockAuxInfo xAOD::WaveformHitContainer xAOD::WaveformHitAuxContainer
+   CNV_PFX xAOD
+   LINK_LIBRARIES AthenaPoolCnvSvcLib AthenaPoolUtilities xAODFaserWaveform )
+
+
diff --git a/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockAuxInfoCnv.cxx b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockAuxInfoCnv.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b5024b1ae3058eccec0eaa0f4ab4e349cecf465f
--- /dev/null
+++ b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockAuxInfoCnv.cxx
@@ -0,0 +1,6 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// Dummy source file so that cmake will know this is a custom converter.
+// xAODWaveformClockAuxInfoCnv.cxx
diff --git a/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockAuxInfoCnv.h b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockAuxInfoCnv.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ac8b4c0db48ff15f3647f71f37f072682e55fa8
--- /dev/null
+++ b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockAuxInfoCnv.h
@@ -0,0 +1,15 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMCLOCKAUXINFOCNV_H
+#define XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMCLOCKAUXINFOCNV_H
+
+#include "xAODFaserWaveform/WaveformClockAuxInfo.h"
+#include "AthenaPoolCnvSvc/T_AthenaPoolAuxContainerCnv.h"
+
+typedef T_AthenaPoolAuxContainerCnv<xAOD::WaveformClockAuxInfo> xAODWaveformClockAuxInfoCnv;
+
+#endif // XAODFASERWAVEFORMATHENAPOOL_XAODFASERWAVEFORMCLOCKAUXINFOCNV_H
diff --git a/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockCnv.cxx b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockCnv.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a175dccc75b115fad4d53637e900d97d67552435
--- /dev/null
+++ b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockCnv.cxx
@@ -0,0 +1,6 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// Dummy source file so that cmake will know this is a custom converter.
+// xAODWaveformClockCnv.cxx
diff --git a/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockCnv.h b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockCnv.h
new file mode 100644
index 0000000000000000000000000000000000000000..2f591d164011f6ec3c4e83b5a89d55b0fc725c68
--- /dev/null
+++ b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformClockCnv.h
@@ -0,0 +1,15 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMCLOCKCNV_H
+#define XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMCLOCKCNV_H
+
+#include "xAODFaserWaveform/WaveformClock.h"
+#include "AthenaPoolCnvSvc/T_AthenaPoolxAODCnv.h"
+
+typedef T_AthenaPoolxAODCnv<xAOD::WaveformClock> xAODWaveformClockCnv;
+
+#endif // XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMCLOCKCNV_H
diff --git a/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitAuxContainerCnv.cxx b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitAuxContainerCnv.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..31d86ebb89db520de8029ae21581a7ffca95ddd0
--- /dev/null
+++ b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitAuxContainerCnv.cxx
@@ -0,0 +1,6 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// Dummy source file so that cmake will know this is a custom converter.
+// xAODWaveformHitAuxContainerCnv.cxx
diff --git a/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitAuxContainerCnv.h b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitAuxContainerCnv.h
new file mode 100644
index 0000000000000000000000000000000000000000..bffe321a489859d89c98a13d29e24ea4a5d7c338
--- /dev/null
+++ b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitAuxContainerCnv.h
@@ -0,0 +1,15 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMHITAUXCONTAINERCNV_H
+#define XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMHITAUXCONTAINERCNV_H
+
+#include "xAODFaserWaveform/WaveformHitAuxContainer.h"
+#include "AthenaPoolCnvSvc/T_AthenaPoolAuxContainerCnv.h"
+
+typedef T_AthenaPoolAuxContainerCnv<xAOD::WaveformHitAuxContainer> xAODWaveformHitAuxContainerCnv;
+
+#endif // XAODFASERWAVEFORMATHENAPOOL_XAODFASERWAVEFORMHITAUXCONTAINERCNV_H
diff --git a/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitContainerCnv.cxx b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitContainerCnv.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2cd6731a4992cdc4b8ec2be6fcc7eeded4690727
--- /dev/null
+++ b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitContainerCnv.cxx
@@ -0,0 +1,6 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// Dummy source file so that cmake will know this is a custom converter.
+// xAODWaveformHitContainerCnv.cxx
diff --git a/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitContainerCnv.h b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitContainerCnv.h
new file mode 100644
index 0000000000000000000000000000000000000000..770bb1625a210f23efb850bda8223a06e6496b5e
--- /dev/null
+++ b/xAOD/xAODFaserWaveformAthenaPool/src/xAODWaveformHitContainerCnv.h
@@ -0,0 +1,15 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMHITCONTAINERCNV_H
+#define XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMHITCONTAINERCNV_H
+
+#include "xAODFaserWaveform/WaveformHitContainer.h"
+#include "AthenaPoolCnvSvc/T_AthenaPoolxAODCnv.h"
+
+typedef T_AthenaPoolxAODCnv<xAOD::WaveformHitContainer> xAODWaveformHitContainerCnv;
+
+#endif // XAODFASERWAVEFORMATHENAPOOL_XAODWAVEFORMHITCONTAINERCNV_H