Author: [log in to unmask]
Date: Mon Oct 10 06:41:35 2016
New Revision: 4508
Log:
separated ecal gains calculation from the raw converter driver (new version called EcalRawConverterDriver3), and created new driver EcalGain for the gain corrections.
Added:
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalGain.java (with props)
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalGainDriver.java (with props)
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter3.java (with props)
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter3Driver.java (with props)
Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalGain.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalGain.java (added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalGain.java Mon Oct 10 06:41:35 2016
@@ -0,0 +1,158 @@
+package org.hps.recon.ecal;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import org.hps.conditions.database.DatabaseConditionsManager;
+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.geometry.Detector;
+
+public class EcalGain {
+ /**
+ * 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;
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+ }
+
+
+ private EcalConditions ecalConditions = null;
+
+
+ /**
+ * return energy (units of GeV) corresponding to the ADC sum and crystal ID
+ */
+ public 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));
+ }
+
+
+ /**
+ * 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;
+
+ /**
+ * Currently sets up a listener for DAQ configuration from EVIO. This should be removed to a standalone
+ * ECalRawConverter solely for trigger emulation.
+ */
+ public EcalGain() {
+ // 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();
+
+
+ }
+ }
+ });
+ }
+
+}
Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalGainDriver.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalGainDriver.java (added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalGainDriver.java Mon Oct 10 06:41:35 2016
@@ -0,0 +1,119 @@
+package org.hps.recon.ecal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.EventHeader;
+import org.lcsim.geometry.Detector;
+import org.lcsim.util.Driver;
+
+public class EcalGainDriver extends Driver{
+ /**
+ * ecalCollectionName "type" (must match detector-data)
+ */
+ private final String ecalReadoutName = "EcalHits";
+
+ private String inputHitsCollectionName = "EcalUncalHits";
+
+ private String outputHitsCollectionName = "EcalCalHits";
+
+ public void process(EventHeader event) {
+
+ List<CalorimeterHit> hits = event.get(CalorimeterHit.class, inputHitsCollectionName);
+
+ List<CalorimeterHit> newHits = new ArrayList<CalorimeterHit>();
+
+ for (CalorimeterHit hit : hits) {
+ double time = hit.getTime();
+ double adcSum = hit.getRawEnergy(); //the "raw energy" is actually the adc sum.
+ long cellID = hit.getCellID();
+
+ double energy =converter.adcToEnergy(adcSum, cellID);
+
+ newHits.add(CalorimeterHitUtilities.create(energy, time, hit.getCellID()));
+ }
+
+ event.put(this.outputHitsCollectionName, newHits, CalorimeterHit.class, event.getMetaData(hits).getFlags(), ecalReadoutName);
+
+ }
+
+
+ private EcalGain converter = new EcalGain();
+
+
+ /**
+ * Sets whether the driver should use the DAQ configuration from EvIO file for its parameters. If activated, the
+ * converter will obtain gains, thresholds, pedestals, the window size, and the pulse integration window from the
+ * EvIO file. This will replace and overwrite any manually defined settings.<br/>
+ * <br/>
+ * Note that if this setting is active, the driver will not output any data until a DAQ configuration has been read
+ * from the data stream.
+ *
+ * @param state - <code>true</code> indicates that the configuration should be read from the DAQ data in an EvIO
+ * file. Setting this to <code>false</code> will cause the driver to use its regular manually-defined
+ * settings and pull gains and pedestals from the conditions database.
+ */
+ public void setUseDAQConfig(boolean state) {
+ // useDAQConfig = state;
+ converter.setUseDAQConfig(state);
+ }
+
+ /**
+ * Set to <code>true</code> to use the "2014" gain formula:<br/>
+ *
+ * <pre>
+ * channelGain * adcSum * gainFactor * readoutPeriod
+ * </pre>
+ * <p>
+ * Set to <code>false</code> to use the gain formula for the Test Run:
+ *
+ * <pre>
+ * gain * adcSum * ECalUtils.MeV
+ * </pre>
+ *
+ * @param use2014Gain True to use 2014 gain formulation.
+ */
+ public void setUse2014Gain(boolean use2014Gain) {
+ converter.setUse2014Gain(use2014Gain);
+ }
+
+ /**
+ * Set a constant gain factor in the converter for all channels.
+ *
+ * @param gain The constant gain value.
+ */
+ public void setGain(double gain) {
+ converter.setGain(gain);
+ }
+
+ @Override
+ public void detectorChanged(Detector detector) {
+
+ // set the detector for the converter
+ // FIXME: This method doesn't even need the detector object and does not use it.
+ converter.setDetector(detector);
+
+ // ECAL combined conditions object.
+ // ecalConditions = DatabaseConditionsManager.getInstance().getEcalConditions();
+ }
+
+
+ /**
+ * Set the input {@link org.lcsim.event.CalorimeterHit} collection name,
+ *
+ * @param ecalCollectionName The <code>CalorimeterHit</code> collection name.
+ */
+ public void setInputHitsCollectionName(String inputHitsCollectionName) {
+ this.inputHitsCollectionName = inputHitsCollectionName;
+ }
+ /**
+ * Set the output {@link org.lcsim.event.CalorimeterHit} collection name,
+ *
+ * @param ecalCollectionName The <code>CalorimeterHit</code> collection name.
+ */
+ public void setOutputHitsCollectionName(String name){
+ this.outputHitsCollectionName = name;
+ }
+
+}
Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter3.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter3.java (added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter3.java Mon Oct 10 06:41:35 2016
@@ -0,0 +1,629 @@
+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 EcalRawConverter3 {
+
+
+ /**
+ * If true, running pedestal is used.
+ */
+ private boolean useRunningPedestal = true;
+
+
+
+ /**
+ * 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 EcalRawConverter3() {
+ // 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;
+ }
+
+
+
+ /**
+ * 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 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;
+ }
+
+
+
+
+
+ /**
+ * 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")) {
+ @SuppressWarnings("unchecked")
+ 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 using a dummy gain.
+
+
+ newHits.add(CalorimeterHitUtilities.create(sum, 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);
+
+ return CalorimeterHitUtilities.create(adcSum, 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);
+ return CalorimeterHitUtilities.create(adcSum, 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);
+ amplitude = (int) Math.round((hit.getRawEnergy() / EcalUtils.MeV) / (EcalUtils.gainFactor * EcalUtils.ecalReadoutPeriod) + pedestal);
+
+ // time += findChannel(id).getTimeShift().getTimeShift();
+ RawCalorimeterHit h = new BaseRawCalorimeterHit(id, amplitude, time);
+ return h;
+ }
+
+ /**
+ * 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 adcSum = sumADC(hit);
+ return CalorimeterHitUtilities.create(adcSum, time, id);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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));
+ }
+
+}
Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter3Driver.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter3Driver.java (added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter3Driver.java Mon Oct 10 06:41:35 2016
@@ -0,0 +1,586 @@
+package org.hps.recon.ecal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hps.conditions.database.DatabaseConditionsManager;
+import org.hps.conditions.ecal.EcalChannelConstants;
+import org.hps.conditions.ecal.EcalConditions;
+import org.hps.record.daqconfig.ConfigurationManager;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.GenericObject;
+import org.lcsim.event.LCRelation;
+import org.lcsim.event.RawCalorimeterHit;
+import org.lcsim.event.RawTrackerHit;
+import org.lcsim.geometry.Detector;
+import org.lcsim.lcio.LCIOConstants;
+import org.lcsim.util.Driver;
+
+/**
+ * This <code>Driver</code> converts raw ECal data collections to {@link org.lcsim.event.CalorimeterHit} collections
+ * with energy and time information. The {@link EcalRawConverter} does most of the low-level work.
+ * <p>
+ * The following input collections are used:
+ * <ul>
+ * <li>EcalReadoutHits
+ * <li>
+ * <li>EcalReadoutExtraDataRelations</li>
+ * <li>EcalRunningPedestals</li>
+ * </ul>
+ * <p>
+ * The results are by default written to the <b>EcalCalHits</b> output collection.
+ */
+public class EcalRawConverter3Driver extends Driver {
+
+ // To import database conditions
+ private EcalConditions ecalConditions = null;
+
+ private EcalRawConverter3 converter = null;
+ /**
+ * Input collection name (unless runBackwards=true, then it's output). Can be a
+ * {@link org.lcsim.event.RawTrackerHit} or {@link org.lcsim.event.RawCalorimeterHit} These have ADC and sample time
+ * information.
+ */
+ private String rawCollectionName = "EcalReadoutHits";
+
+ /**
+ * Output collection name (unless runBackwards=true, then it's input). Always a
+ * {@link org.lcsim.event.CalorimeterHit} This has energy (GeV) and ns time information.
+ */
+ private String ecalCollectionName = "EcalUncalHits";
+
+ /**
+ * ecalCollectionName "type" (must match detector-data)
+ */
+ private final String ecalReadoutName = "EcalHits";
+
+ /*
+ * Output relation between ecalCollectionName and Mode-7 pedestals
+ */
+ private static final String extraDataRelationsName = "EcalReadoutExtraDataRelations";
+
+ private boolean debug = false;
+
+ /**
+ * Hit threshold in GeV. Anything less will not be put into LCIO.
+ */
+ private double threshold = Double.NEGATIVE_INFINITY;
+
+ /**
+ * Whether to reject bad crystals.
+ */
+ private boolean applyBadCrystalMap = true;
+
+ /**
+ * Whether to reject bad FADC channels.
+ */
+ private boolean dropBadFADC = false;
+
+ /**
+ * If true, convert ecalCollectionName to rawCollectionName (GeV to ADC). Else, convert rawCollectionName to
+ * ecalCollectionName (ADC to GeV).
+ */
+ private boolean runBackwards = false;
+
+ /**
+ *
+ */
+ private boolean useTimestamps = false;
+
+ /**
+ *
+ */
+ private boolean useTruthTime = false;
+
+ /**
+ * Whether to use DAQ config read from EVIO for EcalRawConverter parameters. Should be completely removed to a
+ * standalone class soilely for trigger emulation.
+ */
+ private boolean useDAQConfig = false;
+
+ /**
+ * Whether to perform "firmware algorithm" on Mode-1 data. If so, this includes finding a threshold crossing,
+ * extracting a pulse time, and integrating over some configurable sample range inside the window to extract pulse
+ * integral. If not, it simply integrates the entire window and makes no attempt at extracting pulse time. This is
+ * poorly named.
+ */
+ private boolean emulateFirmware = true;
+
+ public EcalRawConverter3Driver() {
+ converter = new EcalRawConverter3();
+ }
+
+ /**
+ * Set to <code>true</code> to use pulse fitting instead of arithmetic integration:<br/>
+ */
+ public void setUseFit(boolean useFit) {
+ converter.setUseFit(useFit);
+ }
+
+ /**
+ * Fix 3-pole function width to be the same for all 442 ECal channels. Units=samples.
+ */
+ public void setGlobalFixedPulseWidth(double width) {
+ converter.setGlobalFixedPulseWidth(width);
+ }
+
+ /**
+ * Set to <code>true</code> to fix fitted pulse widths to their channel's mean value:<br/>
+ */
+ public void setFixShapeParameter(boolean fix) {
+ converter.setFixShapeParameter(fix);
+ }
+
+ /**
+ * Limit threshold crossing range that is candidate for pulse-fitting. Units=samples.
+ */
+ public void setFitThresholdTimeLo(int sample) {
+ converter.setFitThresholdTimeLo(sample);
+ }
+
+ public void setFitThresholdTimeHi(int sample) {
+ converter.setFitThresholdTimeHi(sample);
+ }
+
+ /**
+ * Constrain pulse fit time0 parameter. Units=samples.
+ */
+ public void setFitLimitTimeLo(int sample) {
+ converter.setFitLimitTimeLo(sample);
+ }
+
+ public void setFitLimitTimeHi(int sample) {
+ converter.setFitLimitTimeHi(sample);
+ }
+
+ /**
+ * Set to <code>true</code> to use a running pedestal calibration from mode 7 data.
+ * <p>
+ * The running pedestal values are retrieved from the event collection "EcalRunningPedestals" which is a
+ * <code>Map</code> between {@link org.hps.conditions.ecal.EcalChannel} objects are their average pedestal.
+ *
+ * @param useRunningPedestal True to use a running pedestal value.
+ */
+ public void setUseRunningPedestal(boolean useRunningPedestal) {
+ converter.setUseRunningPedestal(useRunningPedestal);
+ }
+
+ /**
+ * Set to <code>true</code> to generate a {@link org.lcsim.event.CalorimeterHit} collection which is a conversion
+ * from energy to raw signals.
+ *
+ * @param runBackwards True to run the procedure backwards.
+ */
+ public void setRunBackwards(boolean runBackwards) {
+ this.runBackwards = runBackwards;
+ }
+
+ /**
+ * Set to <code>true</code> to drop hits that are mapped to a hard-coded bad FADC configuration from the Test Run.
+ *
+ * @param dropBadFADC True to drop hits mapped to a bad FADC.
+ */
+ public void setDropBadFADC(boolean dropBadFADC) {
+ this.dropBadFADC = dropBadFADC;
+ }
+
+ /**
+ * Set a minimum energy threshold in GeV for created {@link org.lcsim.event.CalorimeterHit} objects to be written
+ * into the output collection.
+ *
+ * @param threshold The minimum energy threshold in GeV.
+ */
+ public void setThreshold(double threshold) {
+ this.threshold = threshold;
+ }
+
+ /**
+ * Set to <code>true</code> to use Mode-7 emulation in calculations. False is Mode-3.
+ *
+ * @param mode7 True to use Mode-7 emulation in calculations.
+ */
+ public void setEmulateMode7(boolean mode7) {
+ converter.setMode7(mode7);
+ }
+
+ /**
+ * Set to <code>true</code> to emulate firmware conversion of Mode-1 to Mode-3/7 data.
+ *
+ * @param emulateFirmware True to use firmware emulation.
+ */
+ public void setEmulateFirmware(boolean emulateFirmware) {
+ this.emulateFirmware = emulateFirmware;
+ }
+
+ /**
+ * Set the leading-edge threshold in ADC counts, relative to pedestal, for pulse-finding and time determination.
+ * <p>
+ * Used to convert Mode-1 readout into Mode-3 or Mode-7 data that is usable by clustering.
+ *
+ * @param threshold The leading edge threshold in ADC counts.
+ */
+ public void setLeadingEdgeThreshold(double threshold) {
+ converter.setLeadingEdgeThreshold(threshold);
+ }
+
+ /**
+ * Set the number of samples in the FADC readout window.
+ * <p>
+ * This is needed in order to properly pedestal-correct clipped pulses for mode-3 and mode-7. It is ignored for
+ * mode-1 input, since this data already includes the number of samples.
+ * <p>
+ * A non-positive number disables pulse-clipped pedestals and reverts to the old behavior which assumed that the
+ * integration range was constant.
+ *
+ * @param windowSamples The number of samples in the FADC readout window.
+ */
+ public void setWindowSamples(int windowSamples) {
+ converter.setWindowSamples(windowSamples);
+ }
+
+ /**
+ * Set the integration range in nanoseconds after the threshold crossing.
+ * <p>
+ * These numbers must be multiples of 4 nanoseconds.
+ * <p>
+ * This value is used for pulse integration in Mode-1, and pedestal subtraction in all modes.
+ *
+ * @param nsa The number of nanoseconds after the threshold crossing.
+ * @see #setNsb(int)
+ */
+ public void setNsa(int nsa) {
+ converter.setNSA(nsa);
+ }
+
+ /**
+ * Set the integration range in nanoseconds before the threshold crossing.
+ * <p>
+ * These numbers must be multiples of 4 nanoseconds.
+ * <p>
+ * This value is used for pulse integration in Mode-1, and pedestal subtraction in all modes.
+ *
+ * @param nsb The number of nanoseconds after the threshold crossing.
+ * @see #setNsa(int)
+ */
+ public void setNsb(int nsb) {
+ converter.setNSB(nsb);
+ }
+
+ /**
+ * Set the maximum number of peaks to search for in the signal, which must be between 1 and 3, inclusive.
+ *
+ * @param nPeak The maximum number of peaks to search for in the signal.
+ */
+ public void setNPeak(int nPeak) {
+ converter.setNPeak(nPeak);
+ }
+
+ /**
+ * Set the {@link org.lcsim.event.CalorimeterHit} collection name, which is used as input in "normal" mode and
+ * output when running "backwards".
+ *
+ * @param ecalCollectionName The <code>CalorimeterHit</code> collection name.
+ * @see #runBackwards
+ */
+ public void setEcalCollectionName(String ecalCollectionName) {
+ this.ecalCollectionName = ecalCollectionName;
+ }
+
+ /**
+ * Set the raw collection name which is used as output in "normal" mode and input when running "backwards".
+ * <p>
+ * Depending on the Driver configuration, this could be a collection of {@link org.lcsim.event.RawTrackerHit}
+ * objects for Mode-1 or {@link org.lcsim.event.RawCalorimeterHit} objects for Mode-3 or Mode-7.
+ *
+ * @param rawCollectionName The raw collection name.
+ */
+ public void setRawCollectionName(String rawCollectionName) {
+ this.rawCollectionName = rawCollectionName;
+ }
+
+ /**
+ * Set to <code>true</code> to ignore data from channels that are flagged as "bad" in the conditions system.
+ *
+ * @param apply True to ignore bad channels.
+ */
+ public void setApplyBadCrystalMap(boolean apply) {
+ this.applyBadCrystalMap = apply;
+ }
+
+ /**
+ * Set to <code>true</code> to turn on debug output.
+ *
+ * @param debug True to turn on debug output.
+ */
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * Set to <code>true</code> to use timestamp information from the ECal or trigger.
+ *
+ * @param useTimestamps True to use timestamp information.
+ */
+ // FIXME: What does this actually do? What calculations does it affect?
+ public void setUseTimestamps(boolean useTimestamps) {
+ this.useTimestamps = useTimestamps;
+ }
+
+ /**
+ * Set to <code>true</code> to use MC truth information.
+ *
+ * @param useTruthTime True to use MC truth information.
+ */
+ // FIXME: What does this actually do? What calculations does it affect?
+ public void setUseTruthTime(boolean useTruthTime) {
+ this.useTruthTime = useTruthTime;
+ }
+
+ /**
+ * Sets whether the driver should use the DAQ configuration from EvIO file for its parameters. If activated, the
+ * converter will obtain gains, thresholds, pedestals, the window size, and the pulse integration window from the
+ * EvIO file. This will replace and overwrite any manually defined settings.<br/>
+ * <br/>
+ * Note that if this setting is active, the driver will not output any data until a DAQ configuration has been read
+ * from the data stream.
+ *
+ * @param state - <code>true</code> indicates that the configuration should be read from the DAQ data in an EvIO
+ * file. Setting this to <code>false</code> will cause the driver to use its regular manually-defined
+ * settings and pull gains and pedestals from the conditions database.
+ */
+ public void setUseDAQConfig(boolean state) {
+ useDAQConfig = state;
+ converter.setUseDAQConfig(state);
+ }
+
+ @Override
+ public void startOfData() {
+ if (ecalCollectionName == null) {
+ throw new RuntimeException("The parameter ecalCollectionName was not set!");
+ }
+ }
+
+ @Override
+ public void detectorChanged(Detector detector) {
+
+ // set the detector for the converter
+ // FIXME: This method doesn't even need the detector object and does not use it.
+ converter.setDetector(detector);
+
+ // ECAL combined conditions object.
+ ecalConditions = DatabaseConditionsManager.getInstance().getEcalConditions();
+ }
+
+ /**
+ * @return false if the channel is a good one, true if it is a bad one
+ * @param hit the <code>CalorimeterHit</code> pointing to the channel
+ */
+ public boolean isBadCrystal(CalorimeterHit hit) {
+ // Get the channel data.
+ EcalChannelConstants channelData = findChannel(hit.getCellID());
+ return channelData.isBadChannel();
+ }
+
+ /**
+ * @return false if the ADC is a good one, true if it is a bad one
+ * @param hit the <code>CalorimeterHit</code> pointing to the FADC
+ */
+ public boolean isBadFADC(CalorimeterHit hit) {
+ return (getCrate(hit.getCellID()) == 1 && getSlot(hit.getCellID()) == 3);
+ }
+
+ private static double getTimestamp(int system, EventHeader event) { // FIXME: copied from
+ // org.hps.readout.ecal.ReadoutTimestamp
+ if (event.hasCollection(GenericObject.class, "ReadoutTimestamps")) {
+ List<GenericObject> timestamps = event.get(GenericObject.class, "ReadoutTimestamps");
+ for (GenericObject timestamp : timestamps) {
+ if (timestamp.getIntVal(0) == system) {
+ return timestamp.getDoubleVal(0);
+ }
+ }
+ return 0;
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public void process(EventHeader event) {
+ // Do not process the event if the DAQ configuration should be
+ // used for value, but is not initialized.
+ if (useDAQConfig && !ConfigurationManager.isInitialized()) {
+ return;
+ }
+
+ final int SYSTEM_TRIGGER = 0;
+ // final int SYSTEM_TRACKER = 1;
+ final int SYSTEM_ECAL = 2;
+
+ double timeOffset = 0.0;
+ if (useTimestamps) {
+ double t0ECal = getTimestamp(SYSTEM_ECAL, event);
+ double t0Trig = getTimestamp(SYSTEM_TRIGGER, event);
+ timeOffset += (t0ECal - t0Trig) + 200.0;
+ }
+ if (useTruthTime) {
+ double t0ECal = getTimestamp(SYSTEM_ECAL, event);
+ timeOffset += ((t0ECal + 250.0) % 500.0) - 250.0;
+ }
+
+ int flags = 0;
+ flags += 1 << LCIOConstants.RCHBIT_TIME; // store hit time
+ flags += 1 << LCIOConstants.RCHBIT_LONG; // store hit position; this flag has no effect for RawCalorimeterHits
+
+ if (!runBackwards) {
+ ArrayList<CalorimeterHit> newHits = new ArrayList<CalorimeterHit>();
+
+ /*
+ * 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) {
+
+ ArrayList<CalorimeterHit> newHits2 = new ArrayList<CalorimeterHit>();
+ if (emulateFirmware) {
+ newHits2.addAll(converter.HitDtoA(event, hit));
+ } else {
+ 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)) {
+
+ /*
+ * 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) {
+ RawCalorimeterHit hit = (RawCalorimeterHit) rel.getFrom();
+ if (debug) {
+ System.out.format("old hit energy %d\n", hit.getAmplitude());
+ }
+ GenericObject extraData = (GenericObject) rel.getTo();
+ CalorimeterHit newHit;
+ newHit = converter.HitDtoA(event, hit, extraData, timeOffset);
+ if (newHit.getRawEnergy() > threshold) {
+ if (applyBadCrystalMap && isBadCrystal(newHit)) {
+ continue;
+ }
+ if (dropBadFADC && isBadFADC(newHit)) {
+ continue;
+ }
+ if (debug) {
+ System.out.format("new hit energy %f\n", newHit.getRawEnergy());
+ }
+ newHits.add(newHit);
+ }
+
+ }
+ } 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(event, hit, timeOffset);
+ if (newHit.getRawEnergy() > threshold) {
+ if (applyBadCrystalMap && isBadCrystal(newHit)) {
+ continue;
+ }
+ if (dropBadFADC && isBadFADC(newHit)) {
+ continue;
+ }
+ if (debug) {
+ System.out.format("new hit energy %f\n", newHit.getRawEnergy());
+ }
+ newHits.add(newHit);
+ }
+ }
+ }
+ event.put(ecalCollectionName, newHits, CalorimeterHit.class, flags, ecalReadoutName);
+ }
+ } else {
+ ArrayList<RawCalorimeterHit> newHits = new ArrayList<RawCalorimeterHit>();
+ if (event.hasCollection(CalorimeterHit.class, ecalCollectionName)) {
+ List<CalorimeterHit> hits = event.get(CalorimeterHit.class, ecalCollectionName);
+
+ for (CalorimeterHit hit : hits) {
+ if (debug) {
+ System.out.format("old hit energy %f\n", hit.getRawEnergy());
+ }
+ RawCalorimeterHit newHit = converter.HitAtoD(hit);
+ if (newHit.getAmplitude() > 0) {
+ if (debug) {
+ System.out.format("new hit energy %d\n", newHit.getAmplitude());
+ }
+ newHits.add(newHit);
+ }
+ }
+ event.put(rawCollectionName, newHits, RawCalorimeterHit.class, flags, ecalReadoutName);
+ }
+ }
+
+ }
+
+ /**
+ * Convert physical ID to gain value.
+ *
+ * @param cellID (long)
+ * @return channel constants (EcalChannelConstants)
+ */
+ private EcalChannelConstants findChannel(long cellID) {
+ return ecalConditions.getChannelConstants(ecalConditions.getChannelCollection().findGeometric(cellID));
+ }
+
+ /**
+ * Return crate number from cellID
+ *
+ * @param cellID (long)
+ * @return Crate number (int)
+ */
+ private int getCrate(long cellID) {
+ // Find the ECAL channel and return the crate number.
+ return ecalConditions.getChannelCollection().findGeometric(cellID).getCrate();
+ }
+
+ /**
+ * Return slot number from cellID
+ *
+ * @param cellID (long)
+ * @return Slot number (int)
+ */
+ private int getSlot(long cellID) {
+ // Find the ECAL channel and return the slot number.
+ return ecalConditions.getChannelCollection().findGeometric(cellID).getSlot();
+ }
+}
|