Author: [log in to unmask] Date: Fri May 22 16:15:27 2015 New Revision: 3013 Log: first stab at fitting pulses Added: java/trunk/users/src/main/java/org/hps/users/baltzell/Ecal3PoleFunction.java java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverter.java Added: java/trunk/users/src/main/java/org/hps/users/baltzell/Ecal3PoleFunction.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/baltzell/Ecal3PoleFunction.java (added) +++ java/trunk/users/src/main/java/org/hps/users/baltzell/Ecal3PoleFunction.java Fri May 22 16:15:27 2015 @@ -0,0 +1,52 @@ +package org.hps.users.baltzell; + +import hep.aida.ref.function.AbstractIFunction; + + +public class Ecal3PoleFunction extends AbstractIFunction { + + protected double pedestal=0; + protected double time0=0; + protected double amplitude=0; + protected double shape=0; + + public Ecal3PoleFunction() { + this(""); + } + public Ecal3PoleFunction(String title) { + super(); + this.variableNames = new String[] { "time" }; + this.parameterNames = new String[] { "pedestal","time0","amplitude","shape" }; + init(title); + } + + @Override + public double value(double[] v) { + final double time = v[0]; + if (time <= time0) return pedestal; + return pedestal + + amplitude + * Math.pow(time-time0,2) + / (2*shape) + * Math.exp(-(time-time0)/shape); + } + + @Override + public void setParameters(double[] pars) throws IllegalArgumentException { + super.setParameters(pars); + pedestal=pars[0]; + time0=pars[1]; + amplitude=pars[2]; + shape=pars[3]; + System.err.println(String.format("%8.2f %8.2f %8.2f %8.2f",pars[0],pars[1],pars[2],pars[3])); + } + + @Override + public void setParameter(String key,double value) throws IllegalArgumentException{ + super.setParameter(key,value); + if (key.equals("pedestal")) pedestal=value; + else if (key.equals("time0")) time0=value; + else if (key.equals("amplitude")) amplitude=value; + else if (key.equals("shape")) shape=value; + } +} Added: java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverter.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverter.java (added) +++ java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverter.java Fri May 22 16:15:27 2015 @@ -0,0 +1,674 @@ +package org.hps.users.baltzell; + +import hep.aida.IAnalysisFactory; +import hep.aida.IDataPointSet; +import hep.aida.IFitFactory; +import hep.aida.IFitResult; +import hep.aida.IFitter; +import hep.aida.IFunction; +import hep.aida.IFunctionFactory; +import hep.aida.IHistogram1D; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +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.recon.ecal.CalorimeterHitUtilities; +import org.hps.recon.ecal.EcalTimeWalk; +import org.hps.recon.ecal.EcalUtils; +import org.hps.recon.ecal.daqconfig.ConfigurationManager; +import org.hps.recon.ecal.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; +import org.lcsim.util.aida.AIDA; + + +/** + * This class is used to convert {@link org.lcsim.event.RawCalorimeterHit} + * and {@link org.lcsim.event.RawTrackerHit} to {@link org.lcsim.event.CalorimeterHit} + * objects with energy information. + * + * @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]> + * + * 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. + * + * 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 { + + + // wtf. + AIDA aida = AIDA.defaultInstance(); + IAnalysisFactory analysisFactory = aida.analysisFactory(); + IFunctionFactory functionFactory = analysisFactory.createFunctionFactory(null); + IFitFactory fitFactory = analysisFactory.createFitFactory(); + IFitter fitter=fitFactory.createFitter(); + IFunction fitFunction=new Ecal3PoleFunction(); + IDataPointSet data=aida.analysisFactory().createDataPointSetFactory(null).create("ADC DataPointSet", 2); + + IHistogram1D hShape = aida.histogram1D("shape",500,0,5); + + private boolean useTimeWalkCorrection = false; + private boolean useRunningPedestal = false; + private boolean constantGain = false; + private double gain; + private boolean use2014Gain = true; + private boolean useDAQConfig = false; + private FADCConfig config = null; + + /* + * 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. + */ + private int nPeak = 3; + + /* + * Convert Mode-1 into Mode-7, else Mode-3. + */ + private boolean mode7 = false; + + + private EcalConditions ecalConditions = null; + + public EcalRawConverter() { + + Logger minuitLogger = Logger.getLogger("org.freehep.math.minuit"); + minuitLogger.setLevel(Level.OFF); + + // 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(); + } + } + }); + } + + public void setLeadingEdgeThreshold(double thresh) { + leadingEdgeThreshold=thresh; + } + + 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; + } + + 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; + } + + 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; + } + + public void setUse2014Gain(boolean use2014Gain) { + this.use2014Gain = use2014Gain; + } + + public void setUseRunningPedestal(boolean useRunningPedestal) { + this.useRunningPedestal=useRunningPedestal; + } + + public void setUseTimeWalkCorrection(boolean useTimeWalkCorrection) { + this.useTimeWalkCorrection=useTimeWalkCorrection; + } + + public void setUseDAQConfig(boolean state) { + useDAQConfig = state; + } + + /* + * This should probably be deprecated. It just integrates the entire window. + */ + 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); + } + + public IFitResult fitPulse(short samples[],int thresholdCrossing,double maxADC,double noise) { + + if (thresholdCrossing < 9 || thresholdCrossing > 15) return null; + + System.err.println("FITTING....................................................."); + + data.clear(); + int nped=0; + double ped=0; + for (int ii=thresholdCrossing-9; ii<thresholdCrossing-5; ii++) { + if (ii<0 || ii>=samples.length) break; + ped += samples[ii]; + nped++; + } + ped /= nped; + if (nped==0) return null; + int npts=0; + for (int ii=thresholdCrossing-5; ii<thresholdCrossing+20; ii++) { + if (ii<0 || ii>=samples.length) break; + System.err.print(ii+":"+samples[ii]+" "); + data.addPoint(); + data.point(npts).coordinate(0).setValue(ii); + data.point(npts).coordinate(1).setValue(samples[ii]); + data.point(npts).coordinate(1).setErrorMinus(noise); + data.point(npts).coordinate(1).setErrorPlus(noise); + npts++; + } + System.err.print("\n"); + if (npts>=10) { +// System.err.println("------- "+ped+" "+thresholdCrossing+" "+maxADC); + if (maxADC-ped < 0) return null; + fitFunction.setParameter("pedestal",ped); + fitFunction.setParameter("time0",(double)thresholdCrossing-2); + fitFunction.setParameter("amplitude",(maxADC-ped)*37.2/24); + fitFunction.setParameter("shape",2.5); + + System.err.println(String.format("A= %8.2f",(maxADC-ped)*37.2/24)); + System.err.println(String.format("T= %8.2f",4*((double)thresholdCrossing-2))); + System.err.println(String.format("P= %8.2f",ped)); + System.err.println(String.format("S= %8.2f",2.5)); + + fitter.fitParameterSettings("amplitude").setBounds(0,999999); + fitter.fitParameterSettings("time0").setBounds(5,40); + fitter.fitParameterSettings("shape").setBounds(0.1,5); + + Logger.getLogger("org.freehep.math.minuit").setLevel(Level.OFF); + return fitter.fit(data,fitFunction); + } + return null; + } + + + /* + * 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) { + + 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; + + } + + } + } + + double noise=findChannel(hit.getCellID()).getCalibration().getNoise(); + IFitResult fitResult=fitPulse(samples,thresholdCrossing,maxADC,noise); + if (fitResult != null) { + double A=fitResult.fittedParameter("amplitude"); + double T=fitResult.fittedParameter("time0"); + double P=fitResult.fittedParameter("pedestal"); + double S=fitResult.fittedParameter("shape"); + + // analytic integral from T to T+NSAMP + final int NSAMP=30; + double I = (A/2/S) * S*S*S* ( 2 - Math.exp(-NSAMP/S) * (Math.pow((S+NSAMP)/S,2) + 1)); + /* + System.err.println(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); + System.err.println(String.format("A= %8.2f %8.2f",I,sumADC-P*30)); + System.err.println(String.format("T= %8.2f %8.2f",T*4,pulseTime)); + System.err.println(String.format("P= %8.2f %8.2f",P,minADC)); + System.err.println(String.format("S= %8.2f",S)); + System.err.println(String.format("M= %8.2f %8.2f",A,(maxADC-P))); + */ + pulseTime = T*4; + sumADC = I; + maxADC = -1; + + hShape.fill(S); + } + 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. + * + */ + 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 + + if (max > 0) { + // 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(); + 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); + } + 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); + } + 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(); + } + + /** + * 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)); + } + +}