Print

Print


Author: [log in to unmask]
Date: Sat Mar  7 10:02:10 2015
New Revision: 2337

Log:
Updating all FADC Modes to more properly pedestal subtract clipped pulses, mainly just to aid trigger studies although it will never 100% match SSP which is always unclipped.  Updating Mode-1 to find multiple pulses in one channel.  For Mode-1, switching to current status of SSP firmware emulation for threshold crossing (>=) instead of FADC firmware (>) to aid trigger studies.  Apparently they are different, and firmware may be changed to make them equal.  Implemented conversion from Mode-1 to Mode-7.  Default behavior for Mode-3 and Mode-7 is unchanged.

Added:
    java/trunk/steering-files/src/main/resources/org/hps/steering/recon/EngineeringRun2015ECalRecon.lcsim
Modified:
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter.java
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverterDriver.java

Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter.java	(original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter.java	Sat Mar  7 10:02:10 2015
@@ -1,5 +1,7 @@
 package org.hps.recon.ecal;
 
+import java.awt.List;
+import java.util.ArrayList;
 import java.util.Map;
 
 import org.hps.conditions.database.DatabaseConditionsManager;
@@ -16,18 +18,33 @@
 
 /**
  * This class is used to convert {@link org.lcsim.event.RawCalorimeterHit}
- * objects to {@link org.lcsim.event.CalorimeterHit} objects with energy
- * information. It has methods to convert pedestal subtracted ADC counts to
- * energy.
- *
- * TODO: Switch all mode's HitDtoAs to use a clipped pedestal for clipped pulses.
- *       This requires another parameter, the window size.
+ * and {@link org.lcsim.event.RawTrackerHit} to {@link org.lcsim.event.CalorimeterHit}
+ * objects with energy information.
  *
  * @author Sho Uemura <[log in to unmask]>
  * @author Jeremy McCormick <[log in to unmask]>
  * @author Andrea Celentano <[log in to unmask]>
- * @author <[log in to unmask]>
+ * @author Nathan Baltzell <[log in to unmask]>
+ *
+ *
+ * baltzell:  New in 2015:  (default behavior is still unchanged)
+ *
+ * Implemented conversion of Mode-1 to Mode-3.
  * 
+ * Now using NSA/NSB for pedestal subtraction instead of integralWindow, to allow
+ * treating all FADC Modes uniformly.  (New) NSA+NSB == (Old) integralWindow*4(ns) 
+ * 
+ * Pedestal subtracting clipped pulses more correctly for all Modes.
+ * 
+ * Changed threshold cut for Mode-1 to >= instead of > to emulate SSP instead of
+ * FADC firmware for trigger diagnostics.
+ *
+ * Implemented finding multiple peaks for Mode-1.
+ * 
+ * Implemented conversion of Mode-1 to Mode-7 with high-resolution timing.
+ * Only some of the special cases in the firmware for when this algorithm fails due
+ * to bad pulses (e.g. clipping) are already implemented.  Not yet writing Mode-7's
+ * min/max to data stream. 
  */
 public class EcalRawConverter {
 
@@ -43,23 +60,46 @@
     private static final int nsPerSample = 4;
     
     /*
-     * The leading-edge threshold, relative to pedestal, for readout and pulse time
-     * determination.  Units = ADC.  This is used to convert mode-1 readout into
-     * mode-3/7 for clustering.
+     * The leading-edge threshold, relative to pedestal, for pulse-finding and
+     * time determination.  Units = ADC.  Used to convert mode-1 readout into
+     * mode-3/7 used by clustering.
      * 
      * The default value of 12 is what we used for most of the 2014 run.
      */
     private double leadingEdgeThreshold=12;
     
     /*
-     * Integration range after (NSA) and before (NSB) threshold crossing.  (units = ns)
-     * These must be multiples of 4 ns.
+     * Integration range after (NSA) and before (NSB) threshold crossing.  Units=ns,
+     * same as the DAQ configuration files.  These must be multiples of 4 ns.  Used
+     * for pulse integration in Mode-1, and pedestal subtraction in all modes.
      * 
      * The default values of 20/100 are what we had during the entire 2014 run.
      */
     private int NSB=20;
     private int NSA=100;
-    
+  
+    /*
+     * The number of samples in the FADC readout window.  Needed in order to
+     * properly pedestal-correct clipped pulses for Mode-3/7.  Ignored for
+     * mode-1 input, since it already knows its number of samples.
+     * 
+     * A non-positive number disables pulse-clipped pedestals and reverts to
+     * the old behavior which assumed integration range was constant.
+     * 
+     */
+    private int windowSamples=-1;
+    
+    /*
+     * The maximum number of peaks to be searched for.
+     */
+    private int nPeak=3;
+   
+    /*
+     * Convert Mode-1 into Mode-7, else Mode-3.
+     */
+    private boolean mode7=false;
+
+
     private EcalConditions ecalConditions = null;
 
     public EcalRawConverter() {
@@ -80,7 +120,20 @@
         }
         NSB=nsb;
     }
-    
+    public void setWindowSamples(int windowSamples) {
+        this.windowSamples=windowSamples;
+    }
+    public void setNPeak(int nPeak) {
+        if (nPeak<1 || nPeak>3) {
+            throw new RuntimeException("Npeak must be 1, 2, or 3.");
+        }
+        this.nPeak=nPeak;
+    }
+    public void setMode7(boolean mode7)
+    {
+        this.mode7=mode7;
+    }
+
     public void setGain(double gain) {
         constantGain = true;
         this.gain = gain;
@@ -93,25 +146,48 @@
     public void setUseRunningPedestal(boolean useRunningPedestal) {
         this.useRunningPedestal=useRunningPedestal;
     }
-    
+
     public void setUseTimeWalkCorrection(boolean useTimeWalkCorrection) {
         this.useTimeWalkCorrection=useTimeWalkCorrection;
     }
-  
-    /*
-     * NAB 2015/02/11 
-     * Choose whether to use static pedestal from database or running pedestal.
-     * This can only used for Mode-7 data.
-     */
-    public double getMode7Pedestal(EventHeader event,RawCalorimeterHit hit)
-    {
-        if (useRunningPedestal) {
+
+    /*
+     * This should probably be deprecated.  It just integrates the entire window.
+     */
+    public short sumADC(RawTrackerHit hit) {
+        EcalChannelConstants channelData = findChannel(hit.getCellID());
+        double pedestal = channelData.getCalibration().getPedestal();
+        short sum = 0;
+        short samples[] = hit.getADCValues();
+        for (int isample = 0; isample < samples.length; ++isample) {
+            sum += (samples[isample] - pedestal);
+        }
+        return sum;
+    }
+
+    /*
+     * This should probably be deprecated.  HitDtoA(EventHeader,RawTrackerHit)
+     * has the same functionality if NSA+NSB > windowSamples, with the exception
+     * that that one also finds pulse time instead of this one's always reporting zero.
+     */
+    public CalorimeterHit HitDtoA(RawTrackerHit hit) {
+        double time = hit.getTime();
+        long id = hit.getCellID();
+        double rawEnergy = adcToEnergy(sumADC(hit), id);
+        return CalorimeterHitUtilities.create(rawEnergy, time, id);
+    }
+
+    /*
+     * Get pedestal for a single ADC sample.
+     * Choose whether to use static pedestal from database or running pedestal from mode-7.
+     */
+    public double getSingleSamplePedestal(EventHeader event,long cellID) {
+        if (useRunningPedestal && event!=null) {
             if (event.hasItem("EcalRunningPedestals")) {
                 Map<EcalChannel, Double> runningPedMap=
                         (Map<EcalChannel, Double>)
                         event.get("EcalRunningPedestals");
-                EcalChannel chan = ecalConditions.getChannelCollection().
-                        findGeometric(hit.getCellID());
+                EcalChannel chan = ecalConditions.getChannelCollection().findGeometric(cellID);
                 if (!runningPedMap.containsKey(chan)){
                     System.err.println("************** Missing Pedestal");
                 } else {
@@ -125,65 +201,39 @@
                 useRunningPedestal = false;
             }
         }
-        return findChannel(hit.getCellID()).getCalibration().getPedestal();
-    }
-    
-    public short sumADC(RawTrackerHit hit) {
-        EcalChannelConstants channelData = findChannel(hit.getCellID());
-        double pedestal = channelData.getCalibration().getPedestal();
-        short sum = 0;
+        return findChannel(cellID).getCalibration().getPedestal();
+    }
+
+    /*
+     * Get pedestal for entire pulse integral.  Account for clipping if
+     * windowSamples is greater than zero.
+     */
+    public double getPulsePedestal(EventHeader event,long cellID,int windowSamples,int thresholdCrossing) {
+        int firstSample,lastSample;
+        if ( windowSamples>0 && (NSA+NSB)/nsPerSample >= windowSamples ) {
+            // special case where firmware always integrates entire window
+            firstSample = 0;
+            lastSample = windowSamples-1;
+        } else {
+            firstSample = thresholdCrossing - NSB/nsPerSample;
+            lastSample  = thresholdCrossing + NSA/nsPerSample-1;
+            if (windowSamples > 0) {
+                // properly pedestal subtract pulses clipped by edge(s) of readout window:
+                if (firstSample < 0) firstSample=0;
+                if (lastSample >= windowSamples) lastSample=windowSamples-1;
+            }
+        }
+        return (lastSample-firstSample+1)*getSingleSamplePedestal(event,cellID); 
+    }
+   
+    
+    /*
+     * Emulate the FADC250 firmware emulation Mode-1 waveform to a Mode-3/7 pulse,
+     * given a time for threshold crossing.
+     */
+    public double[] convertWaveformToPulse(RawTrackerHit hit,int thresholdCrossing,boolean mode7) {
         short samples[] = hit.getADCValues();
-        for (int isample = 0; isample < samples.length; ++isample) {
-            sum += (samples[isample] - pedestal);
-        }
-        return sum;
-    }
-
-    /*
-     * This should this be replaced by firmwareHitDtoA, as that has the
-     * same functionality if NSA+NSB > window size. Left for now.
-     */
-    public CalorimeterHit HitDtoA(RawTrackerHit hit) {
-        double time = hit.getTime();
-        long id = hit.getCellID();
-        double rawEnergy = adcToEnergy(sumADC(hit), id);
-        return CalorimeterHitUtilities.create(rawEnergy, time, id);
-    }
-
-    /*
-     * NAB 2015/02/26
-     * This HitDtoA is for emulating the conversion of Mode-1 readout (RawTrackerHit)
-     * into what EcalRawConverter would have created from a Mode-3 readout.
-     * 
-     */
-    public CalorimeterHit firmwareHitDtoA(RawTrackerHit hit) {
-     
-        long id = hit.getCellID();
-        short samples[] = hit.getADCValues();
-        if (samples.length==0) return null;
-        EcalChannelConstants channelData = findChannel(hit.getCellID());
-        double pedestal = channelData.getCalibration().getPedestal();
-        double absoluteThreshold = pedestal+leadingEdgeThreshold;
-        
-        // find threshold crossing:
-        int thresholdCrossing = -1;
-        if (samples[0] > absoluteThreshold) {
-            // special case, first sample above threshold:
-            thresholdCrossing=0;
-        } else {
-            for (int ii = 1; ii < samples.length; ++ii) {
-                if ( samples[ii]   >absoluteThreshold &&
-                     samples[ii-1]<=absoluteThreshold)
-                {
-                    // found threshold crossing:
-                    thresholdCrossing = ii;
-                    // one pulse only:
-                    break;
-                }
-            }
-        }
-        if (thresholdCrossing < 0) return null;
-
+        
         // choose integration range:
         int firstSample,lastSample;
         if ((NSA+NSB)/nsPerSample >= samples.length) {
@@ -194,43 +244,154 @@
             firstSample = thresholdCrossing - NSB/nsPerSample;
             lastSample  = thresholdCrossing + NSA/nsPerSample - 1;
         }
-         
+        
+        // mode-7 min and max:
+        double minADC=0;
+        double maxADC=0;
+        int sampleMaxADC=0;
+        
         // pulse integral:
-        short sum = 0;
+        short sumADC = 0;
+        
         for (int jj=firstSample; jj<=lastSample; jj++) {
+        
             if (jj<0) continue;
             if (jj>=samples.length) break;
-            sum += samples[jj];
-        }
-
-        // pedestal subtraction:
-        sum -= pedestal*(NSA+NSB)/nsPerSample;
+            
+            // integrate pulse:
+            sumADC += samples[jj];
+           
+            // compute "minimum" at beginning of window:
+            if (jj<4) minADC+=samples[jj];
+          
+            // find pulse maximum:
+            if (jj>firstSample && jj<samples.length-5) {
+                if (samples[jj+1]<samples[jj]) {
+                    sampleMaxADC=jj;
+                    maxADC=samples[jj];
+                }
+            }
+        }
+       
+        // minADC is the average of first four samples:
+        minADC/=4;
       
-        // conversion of ADC to energy:
-        double rawEnergy = adcToEnergy(sum, id);
-        
-        // pulse time:
-        double time = thresholdCrossing*nsPerSample;
-        if (useTimeWalkCorrection) {
-           time = EcalTimeWalk.correctTimeWalk(time,rawEnergy);
-        }
-
-        return CalorimeterHitUtilities.create(rawEnergy, time, id);
-    }
-
-    /*
-     * This HitDtoA is for Mode-3 data.
-     * A time-walk correction can be applied.
-     */
-    public CalorimeterHit HitDtoA(RawCalorimeterHit hit, double timeOffset) {
+        // pulse time with 4ns resolution:
+        double pulseTime=thresholdCrossing*nsPerSample;
+        
+        // calculate Mode-7 high-resolution time:
+        if (mode7) {
+            if (thresholdCrossing < 4) {
+                maxADC=0;
+            }
+            else if (maxADC>0) {
+                // linear interpolation between threshold crossing and
+                // pulse maximum to find time at pulse half-height:
+                double t0 = thresholdCrossing*nsPerSample;
+                double a0 = samples[thresholdCrossing];
+                double t1 = sampleMaxADC*nsPerSample;
+                double a1 = maxADC;
+                double slope = (a1-a0)/(t1-t0);
+                double halfMax = (maxADC+minADC)/2;
+                double tmpTime = t1 - (a1 - halfMax) / slope;
+                if (slope>0 && tmpTime>0) {
+                    pulseTime = tmpTime;
+                }
+            }
+        }
+        
+        return new double []{pulseTime,sumADC,minADC,maxADC};
+    }
+   
+    
+    /*
+     * This HitDtoA is for emulating the conversion of Mode-1 readout (RawTrackerHit)
+     * into what EcalRawConverter would have created from a Mode-3 or Mode-7 readout.
+     * Clustering classes will read the resulting CalorimeterHits same as if they were
+     * directly readout from the FADCs in Mode-3/7.
+     * 
+     * For Mode-3, hit time is just the time of threshold crossing, with an optional
+     * time-walk correction.  For Mode-7, it is a "high-resolution" one calculated
+     * by linear interpolation between threshold crossing and pulse maximum.
+     *
+     * TODO: Generate GenericObject (and corresponding LCRelation) to store min and max
+     * to fully emulate mode-7.  This is less important for now.
+     *
+     * NOTE: March 7, 2015. Threshold crossing requirement currently emulates the current
+     * SSP firmware (>=) instead of FADC firmware (>) to aid trigger studies.  Firmware will
+     * be changed to make them identical.  But for now this code prefers SSP over FADC.
+     */
+    public ArrayList <CalorimeterHit> HitDtoA(EventHeader event,RawTrackerHit hit) {
+     
+        final long cellID = hit.getCellID();
+        final short samples[] = hit.getADCValues();
+        if (samples.length==0) return null;
+        
+        // threshold is pedestal plus threshold configuration parameter:
+        final int absoluteThreshold = (int)(getSingleSamplePedestal(event,cellID)+leadingEdgeThreshold);
+       
+        ArrayList <Integer> thresholdCrossings = new ArrayList<Integer>();
+        
+        // special case, first sample is above threshold:
+        if (samples[0] >= absoluteThreshold) { // SSP/FADC firmware discrepancy.
+            thresholdCrossings.add(0);
+        } 
+
+        // search for threshold crossings:
+        for (int ii = 1; ii < samples.length; ++ii) {
+            if ( samples[ii]   >=  absoluteThreshold &&
+                 samples[ii-1] <   absoluteThreshold) // SSP/FADC firmware discrepancy.
+            {
+                // found one:
+                thresholdCrossings.add(ii);
+
+                // search for next threshold crossing begins at end of this pulse:
+                ii += NSA/nsPerSample-1; 
+
+                // firmware limit on # of peaks:
+                if (thresholdCrossings.size() >= nPeak) break;
+            }
+        }
+
+        // make hits
+        ArrayList <CalorimeterHit> newHits=new ArrayList<CalorimeterHit>();
+        for (int thresholdCrossing : thresholdCrossings) {
+           
+            // do pulse integral:
+            final double[] data = convertWaveformToPulse(hit,thresholdCrossing,mode7);
+            double time = data[0];
+            double sum = data[1];
+            final double min = data[2]; // TODO: stick min and max in a GenericObject with an 
+            final double max = data[3]; // LCRelation to finish mode-7 emulation
+            
+            // do pedestal subtraction:
+            sum -= getPulsePedestal(event,cellID,samples.length,thresholdCrossing);
+          
+            // do gain scaling:
+            double energy = adcToEnergy(sum, cellID);
+            
+            // do time-walk correction, mode-3 only:
+            if (!mode7 && useTimeWalkCorrection) {
+                time = EcalTimeWalk.correctTimeWalk(time,energy);
+            }
+            
+            newHits.add(CalorimeterHitUtilities.create(energy,time,cellID));
+        }
+        
+        return newHits;
+    }
+
+    /*
+     * This HitDtoA is for Mode-3 data.  A time-walk correction can be applied.
+     */
+    public CalorimeterHit HitDtoA(EventHeader event,RawCalorimeterHit hit, double timeOffset) {
         if (hit.getTimeStamp() % 64 != 0) {
             System.out.println("unexpected timestamp " + hit.getTimeStamp());
         }
         double time = hit.getTimeStamp() / 16.0;
         long id = hit.getCellID();
-        EcalChannelConstants channelData = findChannel(id);
-        int window = (NSA+NSB)/nsPerSample;
-        double adcSum = hit.getAmplitude() - window * channelData.getCalibration().getPedestal();
+        double pedestal = getPulsePedestal(event,id,windowSamples,(int)time/nsPerSample);
+        double adcSum = hit.getAmplitude() - pedestal;
         double rawEnergy = adcToEnergy(adcSum, id);
         if (useTimeWalkCorrection) {
            time = EcalTimeWalk.correctTimeWalk(time,rawEnergy);
@@ -240,15 +401,12 @@
 
     /*
      * This HitDtoA is exclusively for Mode-7 data, hence the GenericObject parameter.
-     * The decision to call this method is made in EcalRawConverterDriver based on the
-     * format of the input EVIO data.  EventHeader is also passed in order to allow access
-     * to running pedestals, which is only applicable to Mode-7 data.  (NAB, 2015/02/11)
      */
     public CalorimeterHit HitDtoA(EventHeader event,RawCalorimeterHit hit, GenericObject mode7Data, double timeOffset) {
         double time = hit.getTimeStamp() / 16.0; //timestamps use the full 62.5 ps resolution
         long id = hit.getCellID();
-        int window = (NSA+NSB)/nsPerSample;
-        double adcSum = hit.getAmplitude() - window * getMode7Pedestal(event,hit);
+        double pedestal = getPulsePedestal(event,id,windowSamples,(int)time/nsPerSample);
+        double adcSum = hit.getAmplitude() - pedestal;
         double rawEnergy = adcToEnergy(adcSum, id);        
         return CalorimeterHitUtilities.create(rawEnergy, time + timeOffset, id);
     }
@@ -263,11 +421,11 @@
         // Get the channel data.
         EcalChannelConstants channelData = findChannel(id);
         int amplitude;
-        int window = (NSA+NSB)/nsPerSample;
+        double pedestal = getPulsePedestal(null,id,windowSamples,(int)hit.getTime()/nsPerSample);
         if (constantGain) {
-            amplitude = (int) Math.round((hit.getRawEnergy() / ECalUtils.MeV) / gain + window * channelData.getCalibration().getPedestal());
+            amplitude = (int) Math.round((hit.getRawEnergy() / ECalUtils.MeV) / gain + pedestal);
         } else {
-            amplitude = (int) Math.round((hit.getRawEnergy() / ECalUtils.MeV) / channelData.getGain().getGain() + window * channelData.getCalibration().getPedestal());
+            amplitude = (int) Math.round((hit.getRawEnergy() / ECalUtils.MeV) / channelData.getGain().getGain() + pedestal);
         }
         RawCalorimeterHit h = new BaseRawCalorimeterHit(id, amplitude, time);
         return h;
@@ -314,5 +472,6 @@
      */
     public EcalChannelConstants findChannel(long cellID) {
         return ecalConditions.getChannelConstants(ecalConditions.getChannelCollection().findGeometric(cellID));
-    }    
+    }
+    
 }

Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverterDriver.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverterDriver.java	(original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverterDriver.java	Sat Mar  7 10:02:10 2015
@@ -20,15 +20,18 @@
  *
  * @version $Id: HPSEcalRawConverterDriver.java,v 1.2 2012/05/03 00:17:54
  * phansson Exp $
- *
- * baltzell: Feb 26, 2015:
- * added firmware emulation for converting from Mode-1 readout (RawTrackerHit)
+ * baltzell
+ * 
+ * 
+ * baltzell New in 2015:  (default behavior is unchanged)
+ * Added firmware emulation for converting from Mode-1 readout (RawTrackerHit)
  * to Mode-3 pulse (CalorimeterHit).  Turn it on with "emulateFirmware", else
- * defaults to previous behavior.  Removed integralWindow in favor of NSA and
- * NSB in EcalRawConverter, so that all conversions can use the same window.
- * March 3, 2015:  Removed integralWindow in favor of NSA/NSB in order to treat
- * all modes uniformly.
+ * defaults to previous behavior.  
+ *  
+ * Removed integralWindow in favor of NSA/NSB to allow treating all Modes uniformly.
+ * (New) NSA+NSB == (Old) integralWindow*4(ns) 
  * 
+ * Implemented finding multiple peaks for Mode-1.
  */
 public class EcalRawConverterDriver extends Driver {
 
@@ -41,7 +44,6 @@
     private String ecalCollectionName = "EcalCalHits";
 
     private static final String extraDataRelationsName = "EcalReadoutExtraDataRelations";
-//    private static final String extraDataCollectionName = "EcalReadoutExtraData";
 
     private boolean debug = false;
     private double threshold = Double.NEGATIVE_INFINITY;
@@ -80,17 +82,26 @@
         this.threshold = threshold;
     }
 
+    public void setEmulateMode7(boolean mode7) {
+        converter.setMode7(mode7);
+    }
     public void setEmulateFirmware(boolean emulateFirmware) {
         this.emulateFirmware = emulateFirmware;
     }
     public void setLeadingEdgeThreshold(double threshold) {
         converter.setLeadingEdgeThreshold(threshold);
     }
+    public void setWindowSamples(int windowSamples) {
+        converter.setWindowSamples(windowSamples);
+    }
     public void setNsa(int nsa) {
         converter.setNSA(nsa);
     }
     public void setNsb(int nsb) {
         converter.setNSB(nsb);
+    }
+    public void setNPeak(int nPeak) {
+        converter.setNPeak(nPeak);
     }
     
     public void setGain(double gain) {
@@ -196,37 +207,48 @@
         if (!runBackwards) {
             ArrayList<CalorimeterHit> newHits = new ArrayList<CalorimeterHit>();
 
-            // Get the list of ECal hits.
+            /*
+             * This is for FADC Mode-1 data:    
+             */
             if (event.hasCollection(RawTrackerHit.class, rawCollectionName)) {
                 List<RawTrackerHit> hits = event.get(RawTrackerHit.class, rawCollectionName);
 
                 for (RawTrackerHit hit : hits) {
-                    
-                    CalorimeterHit newHit = null;
+           
+                    ArrayList<CalorimeterHit> newHits2 = new ArrayList<CalorimeterHit>();
                     if (emulateFirmware) {
-                        newHit = converter.firmwareHitDtoA(hit);
-                        if (newHit==null) continue;
+                        newHits2.addAll(converter.HitDtoA(event,hit));
                     } else {
-                        newHit = converter.HitDtoA(hit);
-                    }
-                
-
-                    // Get the channel data.
-                    EcalChannelConstants channelData = findChannel(newHit.getCellID());
-
-                    if (applyBadCrystalMap && channelData.isBadChannel()) {
-                        continue;
-                    }
-                    if (dropBadFADC && isBadFADC(newHit)) {
-                        continue;
-                    }
-                    if (newHit.getRawEnergy() > threshold) {
-                        newHits.add(newHit);
+                        newHits2.add(converter.HitDtoA(hit));
+                    }
+               
+                    for (CalorimeterHit newHit : newHits2) {
+
+                        // Get the channel data.
+                        EcalChannelConstants channelData = findChannel(newHit.getCellID());
+
+                        if (applyBadCrystalMap && channelData.isBadChannel()) {
+                            continue;
+                        }
+                        if (dropBadFADC && isBadFADC(newHit)) {
+                            continue;
+                        }
+                        if (newHit.getRawEnergy() > threshold) {
+                            newHits.add(newHit);
+                        }
                     }
                 }
                 event.put(ecalCollectionName, newHits, CalorimeterHit.class, flags, ecalReadoutName);
             }
+           
+            /*
+             * This is for FADC pulse mode data (Mode-3 or Mode-7):
+             */
             if (event.hasCollection(RawCalorimeterHit.class, rawCollectionName)) { //A.C. this is the case of the RAW pulse hits
+
+                /*
+                 * This is for FADC Mode-7 data:
+                 */
                 if (event.hasCollection(LCRelation.class, extraDataRelationsName)) { // extra information available from mode 7 readout
                     List<LCRelation> extraDataRelations = event.get(LCRelation.class, extraDataRelationsName);
                     for (LCRelation rel : extraDataRelations) {
@@ -252,13 +274,16 @@
 
                     }
                 } else {
+                    /*
+                     * This is for FADC Mode-3 data:
+                     */
                     List<RawCalorimeterHit> hits = event.get(RawCalorimeterHit.class, rawCollectionName);
                     for (RawCalorimeterHit hit : hits) {
                         if (debug) {
                             System.out.format("old hit energy %d\n", hit.getAmplitude());
                         }
                         CalorimeterHit newHit;
-                        newHit = converter.HitDtoA(hit, timeOffset);
+                        newHit = converter.HitDtoA(event, hit, timeOffset);
                         if (newHit.getRawEnergy() > threshold) {
                             if (applyBadCrystalMap && isBadCrystal(newHit)) {
                                 continue;
@@ -295,6 +320,7 @@
                 event.put(rawCollectionName, newHits, RawCalorimeterHit.class, flags, ecalReadoutName);
             }
         }
+        
     }
 
     /**

Added: java/trunk/steering-files/src/main/resources/org/hps/steering/recon/EngineeringRun2015ECalRecon.lcsim
 =============================================================================
--- java/trunk/steering-files/src/main/resources/org/hps/steering/recon/EngineeringRun2015ECalRecon.lcsim	(added)
+++ java/trunk/steering-files/src/main/resources/org/hps/steering/recon/EngineeringRun2015ECalRecon.lcsim	Sat Mar  7 10:02:10 2015
@@ -0,0 +1,74 @@
+<!-- 
+    Offline reconstruction for 2014 engineering run (ECal only) data.
+
+    Changes made by JM:
+            
+    -Replaced clustering Drivers with new recon.ecal.cluster classes.    
+    -Commented out the legacy clusterer. 
+    -Configured ReconClusterDriver to not write the rejected hit collection.
+    -Changed output cluster collection names.
+     
+     NAB: (Feb 11, 2015) Added EcalRunningPedestalDriver
+     
+    @author Matt Graham <[log in to unmask]> 
+    @author Jeremy McCormick<[log in to unmask]>
+-->
+<lcsim xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
+    xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/lcsim/1.0/lcsim.xsd">
+    <execute>
+        <!--<driver name="EventMarkerDriver" />-->
+        <driver name="EcalRunningPedestal"/>
+        <driver name="EcalRawConverter" />
+        <driver name="ReconClusterer" />
+        <driver name="GTPOnlineClusterer" />
+        <driver name="LCIOWriter" />
+        <driver name="CleanupDriver" />
+    </execute>
+    <drivers>
+        <driver name="EventMarkerDriver" type="org.lcsim.job.EventMarkerDriver">
+            <eventInterval>1</eventInterval>
+        </driver>
+        <driver name="EcalRunningPedestal" type="org.hps.recon.ecal.ECalRunningPedestalDriver">
+            <logLevel>CONFIG</logLevel>
+            <minLookbackEvents>10</minLookbackEvents>
+            <maxLookbackEvents>50</maxLookbackEvents>
+        </driver>
+        <driver name="EcalRawConverter" type="org.hps.recon.ecal.EcalRawConverterDriver">
+            <ecalCollectionName>EcalCalHits</ecalCollectionName>
+            <use2014Gain>false</use2014Gain>
+            <useTimestamps>false</useTimestamps>
+            <useTruthTime>false</useTruthTime>
+            <useRunningPedestal>true</useRunningPedestal>
+            <useTimeWalkCorrection>true</useTimeWalkCorrection>
+            <emulateFirmware>true</emulateFirmware>
+            <emulateMode7>false</emulateMode7>
+            <leadingEdgeThreshold>12</leadingEdgeThreshold>
+            <nsa>100</nsa>
+            <nsb>20</nsb>
+            <windowSamples>50</windowSamples>
+            <nPeak>3</nPeak>
+        </driver>       
+        <driver name="ReconClusterer" type="org.hps.recon.ecal.cluster.ReconClusterDriver">
+            <logLevel>WARNING</logLevel>
+            <outputClusterCollectionName>EcalClusters</outputClusterCollectionName>
+            <hitEnergyThreshold>0.01</hitEnergyThreshold>
+            <seedEnergyThreshold>0.100</seedEnergyThreshold> 
+            <clusterEnergyThreshold>0.200</clusterEnergyThreshold>
+            <minTime>0.0</minTime>
+            <timeWindow>25.0</timeWindow>
+            <useTimeCut>true</useTimeCut>
+            <writeRejectedHitCollection>false</writeRejectedHitCollection>
+        </driver>
+        <driver name="GTPOnlineClusterer" type="org.hps.recon.ecal.cluster.ClusterDriver">
+            <logLevel>WARNING</logLevel>
+            <clustererName>GTPOnlineClusterer</clustererName>
+            <outputClusterCollectionName>EcalClustersGTP</outputClusterCollectionName>
+            <!-- seedMinEnergy -->
+            <cuts>0.100</cuts>
+        </driver>        
+        <driver name="LCIOWriter" type="org.lcsim.util.loop.LCIODriver">
+            <outputFilePath>${outputFile}.slcio</outputFilePath>
+        </driver>
+        <driver name="CleanupDriver" type="org.lcsim.recon.tracking.digitization.sisim.config.ReadoutCleanupDriver" />
+    </drivers>
+</lcsim>