Print

Print


Author: [log in to unmask]
Date: Sat May 21 15:34:24 2016
New Revision: 4366

Log:
Same as EcalRawConverter, but no time corrections (shifts and offsets are excluded)

Modified:
    java/sandbox/EcalRawConverterNoTimeCorrection.java

Modified: java/sandbox/EcalRawConverterNoTimeCorrection.java
 =============================================================================
--- java/sandbox/EcalRawConverterNoTimeCorrection.java	(original)
+++ java/sandbox/EcalRawConverterNoTimeCorrection.java	Sat May 21 15:34:24 2016
@@ -0,0 +1,769 @@
+package org.hps.recon.ecal;
+
+import hep.aida.IFitResult;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Map;
+
+import org.hps.conditions.database.DatabaseConditionsManager;
+import org.hps.conditions.ecal.EcalChannel;
+import org.hps.conditions.ecal.EcalChannelConstants;
+import org.hps.conditions.ecal.EcalConditions;
+import org.hps.record.daqconfig.ConfigurationManager;
+import org.hps.record.daqconfig.FADCConfig;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.GenericObject;
+import org.lcsim.event.RawCalorimeterHit;
+import org.lcsim.event.RawTrackerHit;
+import org.lcsim.event.base.BaseRawCalorimeterHit;
+import org.lcsim.geometry.Detector;
+
+/**
+ * This class is used to convert between
+ * {@link org.lcsim.event.RawCalorimeterHit} or
+ * {@link org.lcsim.event.RawTrackerHit}, objects with ADC/sample information,
+ * and {@link org.lcsim.event.CalorimeterHit}, an object with energy+time
+ * information. At minimum this involves pedestal subtraction/addition and gain
+ * scaling. Knows how to deal with Mode-1/3/7 FADC readout formats. Can perform
+ * Mode-3/7 firmware algorithms on Mode-1 data. Can alternatively call
+ * pulse-fitting on Mode-1 data.
+ * 
+ * All time walk/time offset corrections are performed to this collection after gains
+ * in EcalTimeCorrectionDriver
+ *
+ * @author Sho Uemura <[log in to unmask]>
+ * @author Andrea Celentano <[log in to unmask]>
+ * @author Nathan Baltzell <[log in to unmask]>
+ * @author Holly Szumila <[log in to unmask]>
+ */
+public class EcalRawConverterNoTimeCorrection {
+
+    /**
+     * If true, time walk correction is performed.
+     */
+    private boolean useTimeWalkCorrection = true;
+
+    /**
+     * If true, running pedestal is used.
+     */
+    private boolean useRunningPedestal = true;
+
+    /**
+     * If true, use a single gain factor for all channels. Else, use 442 gains
+     * from the conditions system.
+     */
+    private boolean constantGain = false;
+
+    /**
+     * A single gain factor for all channels (only used if constantGain=true)
+     */
+    private double gain;
+
+    /**
+     * If true, the relationship between ADC and GeV is a convention that
+     * includes readoutPeriod and a global scaling factor. If false, it is the
+     * currently used convention: E(GeV) = GAIN * ADC
+     */
+    private boolean use2014Gain = false;
+
+    /**
+     * If true, use the DAQ configuration from EVIO to set EcalRawConverter
+     * parameters. This should be removed to a standalone EcalRawConverter
+     * solely for trigger emulation.
+     */
+    private boolean useDAQConfig = false;
+
+    /**
+     * The DAQ configuration from EVIO used to set EcalRawConverter parameters
+     * if useDAQConfig=true. This should be removed to a standalone
+     * EcalRawConverter solely for trigger emulation.
+     */
+    private FADCConfig config = null;
+
+    /**
+     * Whether to use pulse fitting (EcalPulseFitter) to extract pulse energy
+     * time. Only applicable to Mode-1 data.
+     */
+    private boolean useFit = true;
+
+    /**
+     * The pulse fitter class.
+     */
+    private EcalPulseFitter pulseFitter = new EcalPulseFitter();
+
+    /**
+     * The time for one FADC sample (units = ns).
+     */
+    private static final int nsPerSample = 4;
+
+    /**
+     * 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, 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. This is applicable only
+     * to Mode-1 data.
+     */
+    private int nPeak = 3;
+
+    /**
+     * Perform Mode-7 algorithm, else Mode-3. Only applicable to Mode-1 data.
+     */
+    private boolean mode7 = true;
+
+    private EcalConditions ecalConditions = null;
+
+    /**
+     * Currently sets up a listener for DAQ configuration from EVIO. This should
+     * be removed to a standalone ECalRawConverter solely for trigger emulation.
+     */
+    public EcalRawConverter() {
+        // Track changes in the DAQ configuration.
+        ConfigurationManager.addActionListener(new ActionListener() {
+
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                // If the DAQ configuration should be used, load the
+                // relevant settings into the driver.
+                if (useDAQConfig) {
+                    // Get the FADC configuration.
+                    config = ConfigurationManager.getInstance().getFADCConfig();
+
+                    // Load the settings.
+                    NSB = config.getNSB();
+                    NSA = config.getNSA();
+                    windowSamples = config.getWindowWidth() / 4;
+
+                    // Get the number of peaks.
+                    if (config.getMode() == 1) {
+                        nPeak = Integer.MAX_VALUE;
+                    } else {
+                        nPeak = config.getMaxPulses();
+                    }
+
+                    // Print the FADC configuration.
+                    System.out.println();
+                    System.out.println();
+                    System.out.printf("NSA            :: %d ns%n", NSA);
+                    System.out.printf("NSB            :: %d ns%n", NSB);
+                    System.out.printf("Window Samples :: %d clock-cycles%n",
+                            windowSamples);
+                    System.out.printf("Max Peaks      :: %d peaks%n", nPeak);
+                    System.out
+                            .println("======================================================================");
+                    System.out
+                            .println("=== FADC Pulse-Processing Settings ===================================");
+                    System.out
+                            .println("======================================================================");
+                    config.printConfig(System.out);
+                }
+            }
+        });
+    }
+
+    public void setUseFit(boolean useFit) {
+        this.useFit = useFit;
+    }
+
+    public void setFixShapeParameter(boolean fix) {
+        pulseFitter.fixShapeParameter = fix;
+    }
+
+    public void setGlobalFixedPulseWidth(double width) {
+        pulseFitter.globalThreePoleWidth = width;
+        pulseFitter.fixShapeParameter = true;
+    }
+
+    /**
+     * Pulses with threshold crossing earlier than this will not be fit.
+     */
+    public void setFitThresholdTimeLo(int sample) {
+        pulseFitter.threshRange[0] = sample;
+    }
+
+    /**
+     * Pulses with threshold crossing time greater than this will not be fit.
+     */
+    public void setFitThresholdTimeHi(int sample) {
+        pulseFitter.threshRange[1] = sample;
+    }
+
+    /**
+     * Tell Minuit to limit pulse time parameter in fit to be greater than this.
+     */
+    public void setFitLimitTimeLo(int sample) {
+        pulseFitter.t0limits[0] = sample;
+    }
+
+    /**
+     * Tell Minuit to limit pulse time parameter in fit to be less than this.
+     */
+    public void setFitLimitTimeHi(int sample) {
+        pulseFitter.t0limits[1] = sample;
+    }
+
+    /**
+     * Set threshold for pulse finding. Units = ADC
+     */
+    public void setLeadingEdgeThreshold(double thresh) {
+        leadingEdgeThreshold = thresh;
+    }
+
+    /**
+     * Set number of samples after threshold crossing for pulse integration
+     * range.
+     */
+    public void setNSA(int nsa) {
+        if (NSA % nsPerSample != 0 || NSA < 0) {
+            throw new RuntimeException(
+                    "NSA must be multiples of 4ns and non-negative.");
+        }
+        NSA = nsa;
+    }
+
+    /**
+     * Set number of samples before threshold crossing for pulse integration
+     * range.
+     */
+    public void setNSB(int nsb) {
+        if (NSB % nsPerSample != 0 || NSB < 0) {
+            throw new RuntimeException(
+                    "NSB must be multiples of 4ns and non-negative.");
+        }
+        NSB = nsb;
+    }
+
+    /**
+     * Set number of samples in readout window. Used for pedestal subtraction
+     * for clipped pulses. This is ignored for Mode-1 raw data, since Mode-1
+     * knows its number of samples.
+     */
+    public void setWindowSamples(int windowSamples) {
+        this.windowSamples = windowSamples;
+    }
+
+    /**
+     * Set maximum number of pulses to search for in Mode-1 data.
+     */
+    public void setNPeak(int nPeak) {
+        if (nPeak < 1 || nPeak > 3) {
+            throw new RuntimeException("Npeak must be 1, 2, or 3.");
+        }
+        this.nPeak = nPeak;
+    }
+
+    /**
+     * Set Mode-7 emulation on/off. If off, falls back to Mode-3.
+     */
+    public void setMode7(boolean mode7) {
+        this.mode7 = mode7;
+    }
+
+    /**
+     * Set global gain value and turn on constant gain. The 442 gains from the
+     * conditions system will be ignored.
+     */
+    public void setGain(double gain) {
+        constantGain = true;
+        this.gain = gain;
+    }
+
+    /**
+     * Chooses which ADC --> Energy convention is used. If true, the
+     * relationship between ADC and GeV is a convention that includes
+     * readoutPeriod and a global scaling factor. If false, it is the currently
+     * used convention: E(GeV) = GAIN * ADC
+     */
+    public void setUse2014Gain(boolean use2014Gain) {
+        this.use2014Gain = use2014Gain;
+    }
+
+    /**
+     * Enables using running pedestals calculated on the fly from previous
+     * events. If false, uses 442 fixed pedestals from the conditions system.
+     * Only applies to FADC Mode-1/7 input data formats.
+     */
+    public void setUseRunningPedestal(boolean useRunningPedestal) {
+        this.useRunningPedestal = useRunningPedestal;
+    }
+
+    /**
+     * Set whether to use timewalk corrections.
+     */
+    public void setUseTimeWalkCorrection(boolean useTimeWalkCorrection) {
+        this.useTimeWalkCorrection = useTimeWalkCorrection;
+    }
+
+    /**
+     * Set whether to use DAQ configuration read from EVIO to set
+     * EcalRawConverter parameters. This should be removed to a standalone
+     * EcalRawCongverterDriver solely for trigger emulation.
+     */
+    public void setUseDAQConfig(boolean state) {
+        useDAQConfig = state;
+    }
+
+    /**
+     * Integrate the entire window. Return pedestal-subtracted integral.
+     */
+    public int sumADC(RawTrackerHit hit) {
+        EcalChannelConstants channelData = findChannel(hit.getCellID());
+        double pedestal;
+        if (useDAQConfig) {
+            // EcalChannel channel =
+            // ecalConditions.getChannelCollection().findGeometric(hit.getCellID());
+            pedestal = config.getPedestal(hit.getCellID());
+        } else {
+            pedestal = channelData.getCalibration().getPedestal();
+        }
+
+        int 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 (useDAQConfig) {
+            // EcalChannel channel =
+            // ecalConditions.getChannelCollection().findGeometric(cellID);
+            return config.getPedestal(cellID);
+        }
+        if (useRunningPedestal && event != null) {
+            if (event.hasItem("EcalRunningPedestals")) {
+                Map<EcalChannel, Double> runningPedMap = (Map<EcalChannel, Double>) event
+                        .get("EcalRunningPedestals");
+                EcalChannel chan = ecalConditions.getChannelCollection()
+                        .findGeometric(cellID);
+                if (!runningPedMap.containsKey(chan)) {
+                    System.err.println("************** Missing Pedestal");
+                } else {
+                    return runningPedMap.get(chan);
+                }
+            } else {
+                System.err
+                        .println("*****************************************************************");
+                System.err
+                        .println("**  You Requested a Running Pedestal, but it is NOT available. **");
+                System.err
+                        .println("**     Reverting to the database. Only printing this ONCE.     **");
+                System.err
+                        .println("*****************************************************************");
+                useRunningPedestal = false;
+            }
+        }
+        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 in conversion of Mode-1 waveform to a
+     * Mode-3/7 pulse, given a time for threshold crossing.
+     */
+    public double[] convertWaveformToPulse(RawTrackerHit hit,
+            int thresholdCrossing, boolean mode7) {
+
+        double fitQuality = -1;
+
+        short samples[] = hit.getADCValues();
+        // System.out.println("NewEvent");
+        // choose integration range:
+        int firstSample, lastSample;
+        if ((NSA + NSB) / nsPerSample >= samples.length) {
+            // firmware treats this case specially:
+            firstSample = 0;
+            lastSample = samples.length - 1;
+        } else {
+            firstSample = thresholdCrossing - NSB / nsPerSample;
+            lastSample = thresholdCrossing + NSA / nsPerSample - 1;
+        }
+
+        // mode-7's minimum/pedestal (average of first 4 samples):
+        double minADC = 0;
+        for (int jj = 0; jj < 4; jj++)
+            minADC += samples[jj];
+        // does the firmware's conversion of min to int occur before or after
+        // time calculation? undocumented.
+        // minADC=(int)(minADC/4);
+        minADC = (minADC / 4);
+
+        // System.out.println("Avg pedestal:\t"+minADC);
+
+        // mode-7's max pulse height:
+        double maxADC = 0;
+        // int sampleMaxADC=0;
+
+        // mode-3/7's pulse integral:
+        double sumADC = 0;
+
+        for (int jj = firstSample; jj <= lastSample; jj++) {
+
+            if (jj < 0)
+                continue;
+            if (jj >= samples.length)
+                break;
+
+            // integrate pulse:
+            sumADC += samples[jj];
+        }
+
+        // find pulse maximum:
+        // if (jj>firstSample && jj<samples.length-5) { // The "5" here is a
+        // firmware constant.
+        for (int jj = thresholdCrossing; jj < samples.length - 5; jj++) { // The
+                                                                          // "5"
+                                                                          // here
+                                                                          // is
+                                                                          // a
+                                                                          // firmware
+                                                                          // constant.
+            if (samples[jj + 1] < samples[jj]) {
+                // sampleMaxADC=jj;
+                maxADC = samples[jj];
+                break;
+            }
+        }
+
+        // pulse time with 4ns resolution:
+        double pulseTime = thresholdCrossing * nsPerSample;
+
+        // calculate Mode-7 high-resolution time:
+        if (mode7) {
+            if (thresholdCrossing < 4) {
+                // special case where firmware sets max to zero and time to 4ns
+                // time.
+                maxADC = 0;
+            } else if (maxADC > 0) {
+                // linear interpolation between threshold crossing and
+                // pulse maximum to find time at pulse half-height:
+
+                final double halfMax = (maxADC + minADC) / 2;
+                int t0 = -1;
+                for (int ii = thresholdCrossing - 1; ii < lastSample; ii++) {
+                    if (ii >= samples.length - 1)
+                        break;
+                    if (samples[ii] <= halfMax && samples[ii + 1] > halfMax) {
+                        t0 = ii;
+                        break;
+                    }
+                }
+                if (t0 > 0) {
+                    final int t1 = t0 + 1;
+                    final int a0 = samples[t0];
+                    final int a1 = samples[t1];
+                    // final double slope = (a1 - a0); // units = ADC/sample
+                    // final double yint = a1 - slope * t1; // units = ADC
+                    pulseTime = ((halfMax - a0) / (a1 - a0) + t0) * nsPerSample;
+                }
+            }
+        }
+
+        if (useFit) {
+            IFitResult fitResult = pulseFitter.fitPulse(hit, thresholdCrossing,
+                    maxADC);
+            if (fitResult != null) {
+                fitQuality = fitResult.quality();
+                if (fitQuality > 0) {
+                    pulseTime = fitResult.fittedParameter("time0")
+                            * nsPerSample;
+                    sumADC = fitResult.fittedParameter("integral");
+                    minADC = fitResult.fittedParameter("pedestal");
+                    maxADC = ((Ecal3PoleFunction) fitResult.fittedFunction())
+                            .maximum();
+                }
+            }
+        }
+
+        return new double[] { pulseTime, sumADC, minADC, maxADC, fitQuality };
+    }
+
+    /**
+     * 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.
+     */
+    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;
+        if (useDAQConfig) {
+            // EcalChannel channel =
+            // ecalConditions.getChannelCollection().findGeometric(hit.getCellID());
+            // int leadingEdgeThreshold =
+            // ConfigurationManager.getInstance().getFADCConfig().getThreshold(channel.getChannelId());
+            int leadingEdgeThreshold = config.getThreshold(cellID);
+            absoluteThreshold = (int) (getSingleSamplePedestal(event, cellID) + leadingEdgeThreshold);
+        } else {
+            absoluteThreshold = (int) (getSingleSamplePedestal(event, cellID) + leadingEdgeThreshold);
+        }
+
+        ArrayList<Integer> thresholdCrossings = new ArrayList<Integer>();
+
+        // special case, first sample is above threshold:
+        if (samples[0] > absoluteThreshold) {
+            thresholdCrossings.add(0);
+        }
+
+        // search for threshold crossings:
+        for (int ii = 1; ii < samples.length; ++ii) {
+            if (samples[ii] > absoluteThreshold
+                    && samples[ii - 1] <= absoluteThreshold) {
+
+                // found one:
+                thresholdCrossings.add(ii);
+
+                // search for next threshold crossing begins at end of this
+                // pulse:
+                if (useDAQConfig
+                        && ConfigurationManager.getInstance().getFADCConfig()
+                                .getMode() == 1) {
+                    // special case, emulating SSP:
+                    ii += 8;
+                } else {
+                    // "normal" case, emulating FADC250:
+                    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
+            final double fitQuality = data[4];
+
+            if (!useFit || fitQuality <= 0) {
+                // do pedestal subtraction:
+                sum -= getPulsePedestal(event, cellID, samples.length,
+                        thresholdCrossing);
+            }
+
+            // do gain scaling:
+            double energy = adcToEnergy(sum, cellID);
+
+            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();
+        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);
+    }
+
+    /**
+     * This HitDtoA is exclusively for Mode-7 data, hence the GenericObject
+     * parameter.
+     */
+    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();
+        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);
+    }
+
+    /**
+     * This converts a corrected pulse integral (pedestal-subtracted and
+     * gain-scaled) back into raw pulse integral with units ADC.
+     */
+    public RawCalorimeterHit HitAtoD(CalorimeterHit hit) {
+        int time = (int) (Math.round(hit.getTime() / 4.0) * 64.0);
+        long id = hit.getCellID();
+        // Get the channel data.
+        EcalChannelConstants channelData = findChannel(id);
+        int amplitude;
+        double pedestal = getPulsePedestal(null, id, windowSamples,
+                (int) hit.getTime() / nsPerSample);
+        if (constantGain) {
+            amplitude = (int) Math.round((hit.getRawEnergy() / EcalUtils.MeV)
+                    / gain + pedestal);
+        } else {
+            amplitude = (int) Math.round((hit.getRawEnergy() / EcalUtils.MeV)
+                    / channelData.getGain().getGain() + pedestal);
+        }
+        time += findChannel(id).getTimeShift().getTimeShift();
+        RawCalorimeterHit h = new BaseRawCalorimeterHit(id, amplitude, time);
+        return h;
+    }
+
+    /**
+     * return energy (units of GeV) corresponding to the ADC sum and crystal ID
+     */
+    private double adcToEnergy(double adcSum, long cellID) {
+
+        // Get the channel data.
+        EcalChannelConstants channelData = findChannel(cellID);
+
+        if (useDAQConfig) {
+            // float gain =
+            // ConfigurationManager.getInstance().getFADCConfig().getGain(ecalConditions.getChannelCollection().findGeometric(cellID));
+            return config.getGain(cellID) * adcSum * EcalUtils.MeV;
+        } else if (use2014Gain) {
+            if (constantGain) {
+                return adcSum * EcalUtils.gainFactor
+                        * EcalUtils.ecalReadoutPeriod;
+            } else {
+                return channelData.getGain().getGain() * adcSum
+                        * EcalUtils.gainFactor * EcalUtils.ecalReadoutPeriod; // should
+                                                                              // not
+                                                                              // be
+                                                                              // used
+                                                                              // for
+                                                                              // the
+                                                                              // moment
+                                                                              // (2014/02)
+            }
+        } else {
+            if (constantGain) {
+                return gain * adcSum * EcalUtils.MeV;
+            } else {
+                return channelData.getGain().getGain() * adcSum * EcalUtils.MeV; // gain
+                                                                                 // is
+                                                                                 // defined
+                                                                                 // as
+                                                                                 // MeV/integrated
+                                                                                 // ADC
+            }
+        }
+    }
+
+    /**
+     * Must be set when an object EcalRawConverter is created.
+     *
+     * @param detector
+     *            (long)
+     */
+    public void setDetector(Detector detector) {
+        // ECAL combined conditions object.
+        ecalConditions = DatabaseConditionsManager.getInstance()
+                .getEcalConditions();
+        pulseFitter.setDetector(detector);
+    }
+
+    /**
+     * Convert physical ID to gain value.
+     *
+     * @param cellID
+     *            (long)
+     * @return channel constants (EcalChannelConstants)
+     */
+    public EcalChannelConstants findChannel(long cellID) {
+        return ecalConditions.getChannelConstants(ecalConditions
+                .getChannelCollection().findGeometric(cellID));
+    }
+
+}