diff --git a/Calorimeter/CaloRecTools/src/CaloRecTool.h b/Calorimeter/CaloRecTools/src/CaloRecTool.h
index de736af74393bf424b2fd471795aea7bc2808f52..82ba2b60740dade0e64e91273e089d2ced98780a 100644
--- a/Calorimeter/CaloRecTools/src/CaloRecTool.h
+++ b/Calorimeter/CaloRecTools/src/CaloRecTool.h
@@ -91,7 +91,7 @@ class CaloRecTool: public extends<AthAlgTool, ICaloRecTool> {
 
   // Could also put these in DB, but just hardcode them for now
   FloatProperty m_MIP_sim_Edep_calo {this, "MIP_sim_Edep_calo", 58.5}; // MIP deposits 5.85 MeV of energy in calo
-  FloatProperty m_MIP_sim_Edep_calo2 {this, "MIP_sim_Edep_calo", 58.5}; // MIP deposits 5.85 MeV of energy in calo
+  FloatProperty m_MIP_sim_Edep_calo2 {this, "MIP_sim_Edep_calo2", 58.5}; // MIP deposits 5.85 MeV of energy in calo
   FloatProperty m_MIP_sim_Edep_preshower {this, "MIP_sim_Edep_preshower", 4.894}; // MIP deposits 4.894 MeV of energy in a preshower layer
 
   FloatProperty m_calo_EM_mu {this, "m_calo_EM_mu", 330.0}; // factor used to do rough calibration of calo to EM energy: 0.33 GeV or 330 MeV
diff --git a/PhysicsAnalysis/NtupleDumper/CMakeLists.txt b/PhysicsAnalysis/NtupleDumper/CMakeLists.txt
index 24b788d23b88e95ed37a20e86a109f198e836851..cb082e543f613fa613ef23105d5b5deebf97f481 100644
--- a/PhysicsAnalysis/NtupleDumper/CMakeLists.txt
+++ b/PhysicsAnalysis/NtupleDumper/CMakeLists.txt
@@ -7,7 +7,7 @@ atlas_add_component(
         src/NtupleDumperAlg.h
         src/NtupleDumperAlg.cxx
         src/component/NtupleDumper_entries.cxx
-        LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserWaveform xAODFaserCalorimeter xAODFaserTrigger xAODFaserLHC ScintIdentifier FaserCaloIdentifier GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerSimData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack TrackerSpacePoint FaserActsKalmanFilterLib FaserActsmanVertexingLib AtlasHepMCLib
+        LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserWaveform xAODFaserCalorimeter xAODFaserTrigger xAODFaserLHC ScintIdentifier FaserCaloIdentifier GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerSimData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack TrackerSpacePoint FaserActsKalmanFilterLib FaserActsmanVertexingLib AtlasHepMCLib WaveformConditionsToolsLib
 	PRIVATE_LINK_LIBRARIES nlohmann_json::nlohmann_json 
 )
 
diff --git a/PhysicsAnalysis/NtupleDumper/python/NtupleDumperConfig.py b/PhysicsAnalysis/NtupleDumper/python/NtupleDumperConfig.py
index da44697fc23f3318f599d4451f03fcc196a3f463..cc68970f9447e8b93433d93153b349247bcdad8a 100644
--- a/PhysicsAnalysis/NtupleDumper/python/NtupleDumperConfig.py
+++ b/PhysicsAnalysis/NtupleDumper/python/NtupleDumperConfig.py
@@ -5,6 +5,7 @@
 from AthenaConfiguration.ComponentFactory import CompFactory
 from MagFieldServices.MagFieldServicesConfig import MagneticFieldSvcCfg
 from FaserActsGeometry.ActsGeometryConfig import ActsTrackingGeometryToolCfg
+from WaveformConditionsTools.WaveformCableMappingConfig import WaveformCableMappingCfg
 
 def NtupleDumperAlgCfg(flags, OutName, **kwargs):
     # Initialize GeoModel
@@ -14,6 +15,7 @@ def NtupleDumperAlgCfg(flags, OutName, **kwargs):
     acc.merge(MagneticFieldSvcCfg(flags))
     #acc.merge(ActsTrackingGeometrySvcCfg(flags))
     #acc.merge(FaserActsAlignmentCondAlgCfg(flags))
+    acc.merge(WaveformCableMappingCfg(flags, **kwargs))
 
     result, actsTrackingGeometryTool = ActsTrackingGeometryToolCfg(flags)
     acc.merge(result)
diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx
index 468a6739bdd9606cc8f34c4ff0996cbda393273d..6550279abc6bee821ada0fe81fa629bcab7fb58b 100644
--- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx
+++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx
@@ -31,17 +31,76 @@ NtupleDumperAlg::NtupleDumperAlg(const std::string &name,
 
 
 void NtupleDumperAlg::addBranch(const std::string &name,
-				float* var) {
+				float* var) const {
   m_tree->Branch(name.c_str(),var,(name+"/F").c_str());
 }
 void NtupleDumperAlg::addBranch(const std::string &name,
-				unsigned int* var) {
+				unsigned int* var) const {
   m_tree->Branch(name.c_str(),var,(name+"/I").c_str());
 }
 
+// Have to declare this cost to call during execute
+void NtupleDumperAlg::defineWaveBranches() const {
+
+  ATH_MSG_DEBUG("defineWaveBranches called");
+  
+  // Use waveform map to find all defined waveform channels
+  auto mapping = m_mappingTool->getCableMapping();
+  ATH_MSG_DEBUG("Cable mapping contains " << mapping.size() << " entries");
+
+  // mapping is std::map<int, std::pair<std::string, Identifier> >
+  // use this to fill my own map of channel lists keyed by type
+
+  std::map<std::string, std::list<int>> wave_map;
+
+  for (const auto& [key, value] : mapping) {
+    wave_map[value.first].push_back(key);
+    ATH_MSG_DEBUG("Found mapping " << value.first << " chan " << key);
+  }
+
+  // Now go through found types and define ntuple entries
+  // Keys are defined in cable map and used by RawWaveformDecoder
+  for (const auto& [key, value] : wave_map) {
+
+    if (key == std::string("calo")) {
+      addWaveBranches("CaloLo", value);
+    }
+    else if (key == std::string("calo2")) {
+      addWaveBranches("CaloHi", value);
+    }
+    else if (key == std::string("calonu")) {
+      addWaveBranches("CaloNu", value);
+    }
+    else if (key == std::string("veto")) {
+      addWaveBranches("Veto", value);
+    }
+    else if (key == std::string("vetonu")) {
+      addWaveBranches("VetoNu", value);
+    }
+    else if (key == std::string("trigger")) {
+      addWaveBranches("Timing", value);
+    }
+    else if (key == std::string("preshower")) {
+      addWaveBranches("Preshower", value);
+    }
+    else if (key == std::string("clock")) {
+      // ignore this
+    }
+    else if (key == std::string("none")) {
+      // ignore this
+    }
+    else {
+      ATH_MSG_WARNING("Found unknown mapping type " << key);
+    }
+  }
+
+  ATH_MSG_DEBUG("defineWaveBranches done");
+
+}
+
 void NtupleDumperAlg::addWaveBranches(const std::string &name,
 				      int nchannels,
-				      int first) {
+				      int first) const {
   for(int ch=0;ch<nchannels;ch++) {
     std::string base=name+std::to_string(ch)+"_";
     addBranch(base+"time",&m_wave_localtime[first]);
@@ -57,22 +116,64 @@ void NtupleDumperAlg::addWaveBranches(const std::string &name,
   }
 }
 
-void NtupleDumperAlg::FillWaveBranches(const xAOD::WaveformHitContainer &wave) const {
+// Use channel list to add branches
+void NtupleDumperAlg::addWaveBranches(const std::string &name,
+				      std::list<int> channel_list) const {
+
+  ATH_MSG_DEBUG("Adding " << name << " with channels " << channel_list);
+
+  int nchannels = channel_list.size();
+  for(int i=0;i<nchannels;i++) {
+    int ch = channel_list.front();
+    channel_list.pop_front();
+    
+    std::string base=name+std::to_string(i);
+    addBranch(base+"_triggertime",&m_wave_triggertime[ch]);
+    addBranch(base+"_localtime",&m_wave_localtime[ch]);
+    addBranch(base+"_bcidtime",&m_wave_bcidtime[ch]);
+    addBranch(base+"_peak",&m_wave_peak[ch]);
+    addBranch(base+"_width",&m_wave_width[ch]);
+    addBranch(base+"_charge",&m_wave_charge[ch]);
+    addBranch(base+"_raw_peak",&m_wave_raw_peak[ch]);
+    addBranch(base+"_raw_charge",&m_wave_raw_charge[ch]);
+    addBranch(base+"_baseline",&m_wave_baseline_mean[ch]);
+    addBranch(base+"_baseline_rms",&m_wave_baseline_rms[ch]);
+    addBranch(base+"_status",&m_wave_status[ch]);
+    ATH_MSG_DEBUG("Added "  << base << " ch " << ch);
+    
+    // Also add the random histogram
+    std::string hname = "hRandomCharge"+std::to_string(ch);
+    base = name + std::to_string(i) + " Ch" + std::to_string(ch);
+    std::string title = base+" Charge from RandomEvents;charge (pC);Events/bin";
+    m_HistRandomCharge[ch] = new TH1F(hname.c_str(), title.c_str(), 100, -1.0, 1.0);
+    StatusCode sc = histSvc()->regHist("/HIST2/RandomCharge"+std::to_string(ch), m_HistRandomCharge[ch]);
+  }
+}
+
+void NtupleDumperAlg::FillWaveBranches(const xAOD::WaveformHitContainer &wave, bool isMC) const {
+  ATH_MSG_DEBUG("FillWaveBranches called");
   for (auto hit : wave) {
     if ((hit->hit_status()&2)==0) { // dont store secoondary hits as they can overwrite the primary hit
       int ch=hit->channel();
-      m_wave_localtime[ch]=hit->localtime()+m_clock_phase;
+      ATH_MSG_DEBUG("FillWaveBranches filling channel " << ch);
+
+      m_wave_localtime[ch]=hit->localtime();
+      m_wave_bcidtime[ch] = hit->bcid_time(); // Now corrected for offsets
+      // Not sure why this doesn't exist in MC...
+      if (!isMC)
+	m_wave_triggertime[ch] = hit->trigger_time(); // + m_clock_phase;
       m_wave_peak[ch]=hit->peak();
       m_wave_width[ch]=hit->width();
-      m_wave_charge[ch]=hit->integral()/50;
+      m_wave_charge[ch]=hit->integral()/50;  // In pC
 
       m_wave_raw_peak[ch]=hit->raw_peak();
-      m_wave_raw_charge[ch]=hit->raw_integral()/50;
+      m_wave_raw_charge[ch]=hit->raw_integral()/50;  // In pC
       m_wave_baseline_mean[ch]=hit->baseline_mean();
       m_wave_baseline_rms[ch]=hit->baseline_rms();
       m_wave_status[ch]=hit->hit_status();  
     }
   }
+  ATH_MSG_DEBUG("FillWaveBranches done");
 }
 
 void NtupleDumperAlg::addCalibratedBranches(const std::string &name,
@@ -89,6 +190,9 @@ void NtupleDumperAlg::addCalibratedBranches(const std::string &name,
 
 StatusCode NtupleDumperAlg::initialize() 
 {
+
+  m_first = true;
+  
   ATH_CHECK(m_truthEventContainer.initialize());
   ATH_CHECK(m_mcEventContainer.initialize());
   ATH_CHECK(m_truthParticleContainer.initialize());
@@ -102,6 +206,7 @@ StatusCode NtupleDumperAlg::initialize()
   ATH_CHECK(m_preshowerContainer.initialize());
   ATH_CHECK(m_ecalContainer.initialize());
   ATH_CHECK(m_ecal2Container.initialize());
+  ATH_CHECK(m_caloNuContainer.initialize());
   ATH_CHECK(m_clusterContainer.initialize());
   ATH_CHECK(m_simDataCollection.initialize());
   ATH_CHECK(m_FaserTriggerData.initialize());
@@ -111,14 +216,10 @@ StatusCode NtupleDumperAlg::initialize()
   ATH_CHECK(m_preshowerCalibratedContainer.initialize());
   ATH_CHECK(m_ecalCalibratedContainer.initialize());
   ATH_CHECK(m_ecal2CalibratedContainer.initialize());
+  ATH_CHECK(m_calonuCalibratedContainer.initialize());
   ATH_CHECK(m_eventInfoKey.initialize());
 
   ATH_CHECK(detStore()->retrieve(m_sctHelper,       "FaserSCT_ID"));
-  ATH_CHECK(detStore()->retrieve(m_vetoNuHelper,    "VetoNuID"));
-  ATH_CHECK(detStore()->retrieve(m_vetoHelper,      "VetoID"));
-  ATH_CHECK(detStore()->retrieve(m_triggerHelper,   "TriggerID"));
-  ATH_CHECK(detStore()->retrieve(m_preshowerHelper, "PreshowerID"));
-  ATH_CHECK(detStore()->retrieve(m_ecalHelper,      "EcalID"));
 
   ATH_CHECK(detStore()->retrieve(m_detMgr, "SCT"));
   ATH_CHECK(m_extrapolationTool.retrieve());
@@ -126,7 +227,8 @@ StatusCode NtupleDumperAlg::initialize()
   ATH_CHECK(m_trackTruthMatchingTool.retrieve());
   ATH_CHECK(m_fiducialParticleTool.retrieve());
   ATH_CHECK(m_vertexingTool.retrieve());
-
+  ATH_CHECK(m_mappingTool.retrieve());
+  
   ATH_CHECK(m_spacePointContainerKey.initialize());
 
   // Read GRL if requested
@@ -164,7 +266,8 @@ StatusCode NtupleDumperAlg::initialize()
   m_tree->Branch("eventTimeNSOffset", &m_event_timeNSOffset, "eventTimeNSOffset/I");
   m_tree->Branch("BCID", &m_bcid, "BCID/I");
   m_tree->Branch("inGRL", &m_in_grl, "inGRL/I");
-
+  m_tree->Branch("clockPhase", &m_clock_phase, "clockPhase");
+  
   m_tree->Branch("fillNumber", &m_fillNumber, "fillNumber/I");
   m_tree->Branch("betaStar", &m_betaStar, "betaStar/F");
   m_tree->Branch("crossingAngle", &m_crossingAngle, "crossingAngle/F");
@@ -180,15 +283,8 @@ StatusCode NtupleDumperAlg::initialize()
   m_tree->Branch("inputBits", &m_inputBits, "inputBits/I");
   m_tree->Branch("inputBitsNext", &m_inputBitsNext, "inputBitsNext/I");
 
-  //WAVEFORMS
-  // Need to put an option in here for pre/post 2024 data
-  addWaveBranches("VetoNu",2,4);
-  addWaveBranches("VetoSt1",2,14);
-  addWaveBranches("VetoSt2",2,6);
-  addWaveBranches("Timing",4,8);
-  addWaveBranches("Preshower",2,12);
-  addWaveBranches("Calo",4,0);
-  addWaveBranches("CaloHi", 4,16);
+  // WAVEFORMS
+  // Now defined on first event so we can use cable mapping tool
   
   m_tree->Branch("ScintHit", &m_scintHit);
 
@@ -418,48 +514,7 @@ StatusCode NtupleDumperAlg::initialize()
   ATH_CHECK(histSvc()->regTree("/HIST2/tree", m_tree));
 
   // Register histograms
-  m_HistRandomCharge[0] = new TH1F("hRandomCharge0", "Calo ch0 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[1] = new TH1F("hRandomCharge1", "Calo ch1 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[2] = new TH1F("hRandomCharge2", "Calo ch2 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[3] = new TH1F("hRandomCharge3", "Calo ch3 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[4] = new TH1F("hRandomCharge4", "VetoNu ch4 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[5] = new TH1F("hRandomCharge5", "VetoNu ch5 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[6] = new TH1F("hRandomCharge6", "Veto ch6 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[7] = new TH1F("hRandomCharge7", "Veto ch7 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[8] = new TH1F("hRandomCharge8", "Trig ch8 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[9] = new TH1F("hRandomCharge9", "Trig ch9 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[10] = new TH1F("hRandomCharge10", "Trig ch10 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[11] = new TH1F("hRandomCharge11", "Trig ch11 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[12] = new TH1F("hRandomCharge12", "Preshower ch12 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[13] = new TH1F("hRandomCharge13", "Preshower ch13 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[14] = new TH1F("hRandomCharge14", "Veto ch14 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[15] = new TH1F("hRandomCharge15", "Veto ch15 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-
-  m_HistRandomCharge[16] = new TH1F("hRandomCharge16", "CaloHi ch0 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[17] = new TH1F("hRandomCharge17", "CaloHi ch1 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[18] = new TH1F("hRandomCharge18", "CaloHi ch2 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-  m_HistRandomCharge[19] = new TH1F("hRandomCharge19", "CaloHi ch3 Charge from Random Events;charge (pC);Events/bin", 100, -1.0, 1.0);
-
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge0", m_HistRandomCharge[0]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge1", m_HistRandomCharge[1]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge2", m_HistRandomCharge[2]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge3", m_HistRandomCharge[3]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge4", m_HistRandomCharge[4]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge5", m_HistRandomCharge[5]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge6", m_HistRandomCharge[6]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge7", m_HistRandomCharge[7]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge8", m_HistRandomCharge[8]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge9", m_HistRandomCharge[9]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge10", m_HistRandomCharge[10]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge11", m_HistRandomCharge[11]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge12", m_HistRandomCharge[12]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge13", m_HistRandomCharge[13]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge14", m_HistRandomCharge[14]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge15", m_HistRandomCharge[15]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge16", m_HistRandomCharge[16]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge17", m_HistRandomCharge[17]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge18", m_HistRandomCharge[18]));
-  ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge19", m_HistRandomCharge[19]));
+  // Also done on first event
 
   if (m_onlyBlinded){
     ATH_MSG_INFO("Only events that would be blinded are saved in ntuple");
@@ -478,6 +533,12 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
 {
   clearTree();
 
+  // Define waveform channels on first event
+  if (m_first) {
+    defineWaveBranches();
+    m_first = false;
+  }
+  
   // check if real data or simulation data
   bool isMC = false;
   SG::ReadHandle<xAOD::TruthEventContainer> truthEventContainer { m_truthEventContainer, ctx };
@@ -575,44 +636,60 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
   }
 
   // process all waveform data for all scintillator and calorimeter channels
+  // Not all are guaranteed to exist, so fill the ones that are valid
   SG::ReadHandle<xAOD::WaveformHitContainer> vetoNuContainer { m_vetoNuContainer, ctx };
-  ATH_CHECK(vetoNuContainer.isValid());
+  if (vetoNuContainer.isValid()) {
+    FillWaveBranches(*vetoNuContainer, isMC);
+  }
 
   SG::ReadHandle<xAOD::WaveformHitContainer> vetoContainer { m_vetoContainer, ctx };
-  ATH_CHECK(vetoContainer.isValid());
+  if (vetoContainer.isValid()) {
+    FillWaveBranches(*vetoContainer, isMC);
+  }
 
   SG::ReadHandle<xAOD::WaveformHitContainer> triggerContainer { m_triggerContainer, ctx };
-  ATH_CHECK(triggerContainer.isValid());
+  if (triggerContainer.isValid()) {
+    FillWaveBranches(*triggerContainer, isMC);
+  }
 
   SG::ReadHandle<xAOD::WaveformHitContainer> preshowerContainer { m_preshowerContainer, ctx };
-  ATH_CHECK(preshowerContainer.isValid());
+  if (preshowerContainer.isValid()) {
+    FillWaveBranches(*preshowerContainer, isMC);
+  }
 
   SG::ReadHandle<xAOD::WaveformHitContainer> ecalContainer { m_ecalContainer, ctx };
-  ATH_CHECK(ecalContainer.isValid());
+  if (ecalContainer.isValid()) {
+    FillWaveBranches(*ecalContainer, isMC);
+  }
 
   SG::ReadHandle<xAOD::WaveformHitContainer> ecal2Container { m_ecal2Container, ctx };
-  ATH_CHECK(ecal2Container.isValid());
+  if (ecal2Container.isValid()) {
+    FillWaveBranches(*ecal2Container, isMC);
+  }
 
-  FillWaveBranches(*vetoNuContainer);
-  FillWaveBranches(*vetoContainer);
-  FillWaveBranches(*triggerContainer);
-  FillWaveBranches(*preshowerContainer);
-  FillWaveBranches(*ecalContainer);
-  FillWaveBranches(*ecal2Container);
+  SG::ReadHandle<xAOD::WaveformHitContainer> caloNuContainer { m_caloNuContainer, ctx };
+  if (caloNuContainer.isValid()) {
+    FillWaveBranches(*caloNuContainer, isMC);
+  }
 
-  // if real data, store charge in histograms from random events
+  // Some things for real data
   if (!isMC) {
+    // if real data, store charge in histograms from random events
     SG::ReadHandle<xAOD::FaserTriggerData> triggerData(m_FaserTriggerData, ctx);
     m_tap=triggerData->tap();
 
     // for random (only) triggers, store charge of scintillators in histograms
-    if (m_tap == 16) { 
+    if (m_tap == 16) {
+      ATH_MSG_DEBUG("Filling random charge histogram");
       // Fill histograms
-      for (unsigned int chan = 0; chan<nchan; chan++) {
-        m_HistRandomCharge[chan]->Fill(m_wave_raw_charge[chan]);
+      for (unsigned int chan = 0; chan<max_chan; chan++) {
+	// Only fill histograms that have been defined
+	if (m_HistRandomCharge[chan])
+	  m_HistRandomCharge[chan]->Fill(m_wave_raw_charge[chan]);
       }
     }
 
+    // Apply ntuple selection for real data
     if (m_doTrigFilter) {
 
       bool trig_coincidence_preshower_and_vetoes = ( (m_tap&8) != 0 );
@@ -922,7 +999,7 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     return StatusCode::SUCCESS;
   }
 
-  SG::ReadDecorHandle<xAOD::EventInfo,uint32_t> eventInfo (m_eventInfoKey, ctx);
+  SG::ReadHandle<xAOD::EventInfo> eventInfo (m_eventInfoKey, ctx);  
   if (eventInfo->errorState(xAOD::EventInfo_v1::SCT) == xAOD::EventInfo::Error) {
     m_propagationError = 1;
     ATH_MSG_DEBUG("NtupleDumper: xAOD::EventInfo::SCT::Error");
@@ -1404,7 +1481,8 @@ NtupleDumperAlg::clearTree() const
   m_event_timeNSOffset = 0;
   m_bcid = 0;
   m_in_grl = 0;
-
+  m_clock_phase = 0;
+  
   m_fillNumber = 0;
   m_betaStar = 0;
   m_crossingAngle = 0;
@@ -1420,8 +1498,10 @@ NtupleDumperAlg::clearTree() const
   m_inputBits=0;
   m_inputBitsNext=0;
 
-  for(unsigned int ii=0;ii<nchan;ii++) {
+  for(unsigned int ii=0;ii<max_chan;ii++) {
       m_wave_localtime[ii]=0;
+      m_wave_triggertime[ii]=0;
+      m_wave_bcidtime[ii]=0;
       m_wave_peak[ii]=0;
       m_wave_width[ii]=0;
       m_wave_charge[ii]=0;
diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h
index d4fc22b9754617ea8c68aba3c365ef334abd0d5d..456f0f82280bd5efa8ffd9226902d3a4eb062248 100644
--- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h
+++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h
@@ -22,11 +22,12 @@
 #include "FaserActsKalmanFilter/ITrackTruthMatchingTool.h"
 #include "TrackerSimEvent/FaserSiHitCollection.h"
 #include "xAODEventInfo/EventInfo.h"
-#include "StoreGate/ReadDecorHandle.h"
 #include "FaserActsVertexing/IVertexingTool.h"
 #include "GeneratorObjects/McEventCollection.h"
 #include <boost/dynamic_bitset.hpp>
 
+#include "WaveformConditionsTools/IWaveformCableMappingTool.h"
+
 #include <vector>
 #include <nlohmann/json.hpp>
 
@@ -56,13 +57,17 @@ public:
 
 private:
 
+  mutable bool m_first;
+  
   bool waveformHitOK(const xAOD::WaveformHit* hit) const;
   void clearTree() const;
   void clearTrackTruth() const;
-  void addBranch(const std::string &name,float* var);
-  void addBranch(const std::string &name,unsigned int* var);
-  void addWaveBranches(const std::string &name, int nchannels, int first);
-  void FillWaveBranches(const xAOD::WaveformHitContainer &wave) const;
+  void addBranch(const std::string &name,float* var) const;
+  void addBranch(const std::string &name,unsigned int* var) const;
+  void defineWaveBranches() const;
+  void addWaveBranches(const std::string &name, int nchannels, int first) const;
+  void addWaveBranches(const std::string &name, std::list<int> channel_list) const;
+  void FillWaveBranches(const xAOD::WaveformHitContainer &wave, bool isMC) const;
   void addCalibratedBranches(const std::string &name, int nchannels, int first);
   double radius(const Acts::Vector3 &position) const;
 
@@ -85,9 +90,11 @@ private:
   SG::ReadHandleKey<xAOD::WaveformHitContainer> m_preshowerContainer { this, "PreshowerContainer", "PreshowerWaveformHits", "Preshower hit container name" };
   SG::ReadHandleKey<xAOD::WaveformHitContainer> m_ecalContainer { this, "EcalContainer", "CaloWaveformHits", "Ecal hit container name" };
   SG::ReadHandleKey<xAOD::WaveformHitContainer> m_ecal2Container { this, "Ecal2Container", "Calo2WaveformHits", "Ecal hit container name" };
+  SG::ReadHandleKey<xAOD::WaveformHitContainer> m_caloNuContainer { this, "CaloNuContainer", "CaloNuWaveformHits", "CaloNu hit container name" };
 
   SG::ReadHandleKey<xAOD::CalorimeterHitContainer> m_ecalCalibratedContainer { this, "EcalCalibratedContainer", "CaloHits", "Ecal Calibrated hit container name" };
   SG::ReadHandleKey<xAOD::CalorimeterHitContainer> m_ecal2CalibratedContainer { this, "Ecal2CalibratedContainer", "Calo2Hits", "Ecal Calibrated hit container name" };
+  SG::ReadHandleKey<xAOD::CalorimeterHitContainer> m_calonuCalibratedContainer { this, "CaloNuCalibratedContainer", "CaloNuHits", "CaloNu Calibrated hit container name" };
   SG::ReadHandleKey<xAOD::CalorimeterHitContainer> m_preshowerCalibratedContainer { this, "preshowerCalibratedContainer", "PreshowerHits", "Preshower Calibrated hit container name" };
 
   SG::ReadHandleKey<Tracker::FaserSCT_ClusterContainer> m_clusterContainer { this, "ClusterContainer", "SCT_ClusterContainer", "Tracker cluster container name" };
@@ -96,21 +103,17 @@ private:
 
   SG::ReadHandleKey<xAOD::FaserTriggerData> m_FaserTriggerData     { this, "FaserTriggerDataKey", "FaserTriggerData", "ReadHandleKey for xAOD::FaserTriggerData"};
   SG::ReadHandleKey<xAOD::WaveformClock> m_ClockWaveformContainer     { this, "WaveformClockKey", "WaveformClock", "ReadHandleKey for ClockWaveforms Container"};
-  SG::ReadDecorHandleKey<xAOD::EventInfo> m_eventInfoKey{this, "EventInfoKey", "EventInfo"};
+  SG::ReadHandleKey<xAOD::EventInfo> m_eventInfoKey{this, "EventInfoKey", "EventInfo", "ReadHandleKey for EventInfo"};
   ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool { this, "ExtrapolationTool", "FaserActsExtrapolationTool" };
   ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
   ToolHandle<ITrackTruthMatchingTool> m_trackTruthMatchingTool {this, "TrackTruthMatchingTool", "TrackTruthMatchingTool"};
   ToolHandle<IFiducialParticleTool> m_fiducialParticleTool {this, "FiducialParticleTool", "FiducialParticleTool"};
   ToolHandle<FaserTracking::IVertexingTool> m_vertexingTool { this, "VertexingTool", "FaserTracking::PointOfClosestApproachSearchTool"};
+  ToolHandle<IWaveformCableMappingTool> m_mappingTool { this, "WaveformCableMappingTool", "WaveformCableMappingTool" };
 
   const TrackerDD::SCT_DetectorManager* m_detMgr {nullptr};
 
   const FaserSCT_ID* m_sctHelper;
-  const VetoNuID*    m_vetoNuHelper;
-  const VetoID*      m_vetoHelper;
-  const TriggerID*   m_triggerHelper;
-  const PreshowerID* m_preshowerHelper;
-  const EcalID*      m_ecalHelper;
 
   BooleanProperty m_useIFT               { this, "UseIFT", false, "Use IFT tracks" };
   BooleanProperty m_doCalib              { this, "DoCalib", true, "Fill calibrated calorimeter quantities" };
@@ -148,9 +151,9 @@ private:
 
   mutable TTree* m_tree;
 
-  const static unsigned int nchan=20;
-
-  mutable TH1* m_HistRandomCharge[nchan];
+  //mutable unsigned int n_wave_chan;  // Actual number of waveform channels
+  const static unsigned int max_chan=32;
+  mutable TH1* m_HistRandomCharge[max_chan];
 
   mutable unsigned int m_run_number;
   mutable unsigned int m_event_number;
@@ -174,22 +177,24 @@ private:
   mutable unsigned int m_inputBits;
   mutable unsigned int m_inputBitsNext;
 
-  mutable float m_wave_localtime[nchan];
-  mutable float m_wave_peak[nchan];
-  mutable float m_wave_width[nchan];
-  mutable float m_wave_charge[nchan];
-
-  mutable float m_wave_raw_peak[nchan];
-  mutable float m_wave_raw_charge[nchan];
-  mutable float m_wave_baseline_mean[nchan];
-  mutable float m_wave_baseline_rms[nchan];
-  mutable unsigned int m_wave_status[nchan];
+  mutable float m_wave_localtime[max_chan];
+  mutable float m_wave_triggertime[max_chan];
+  mutable float m_wave_bcidtime[max_chan];
+  mutable float m_wave_peak[max_chan];
+  mutable float m_wave_width[max_chan];
+  mutable float m_wave_charge[max_chan];
+
+  mutable float m_wave_raw_peak[max_chan];
+  mutable float m_wave_raw_charge[max_chan];
+  mutable float m_wave_baseline_mean[max_chan];
+  mutable float m_wave_baseline_rms[max_chan];
+  mutable unsigned int m_wave_status[max_chan];
  
   mutable unsigned int m_scintHit;
  
-  mutable float m_calibrated_nMIP[nchan];
-  mutable float m_calibrated_E_dep[nchan];
-  mutable float m_calibrated_E_EM[nchan];
+  mutable float m_calibrated_nMIP[max_chan];
+  mutable float m_calibrated_E_dep[max_chan];
+  mutable float m_calibrated_E_EM[max_chan];
 
   mutable float m_calo_total_nMIP;
   mutable float m_calo_total_E_dep;