Author: [log in to unmask]
Date: Tue Dec 6 19:31:53 2016
New Revision: 4602
Log:
Updated EcalOnlineRawConverter and associated driver to better emulate hardware calculations.
Modified:
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalOnlineRawConverter.java
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalOnlineRawConverterDriver.java
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterer.java
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalOnlineRawConverter.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalOnlineRawConverter.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalOnlineRawConverter.java Tue Dec 6 19:31:53 2016
@@ -1,328 +1,351 @@
-package org.hps.recon.ecal;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.hps.record.daqconfig.ConfigurationManager;
-import org.hps.record.daqconfig.FADCConfig;
-import org.lcsim.event.CalorimeterHit;
-import org.lcsim.event.GenericObject;
-import org.lcsim.event.RawCalorimeterHit;
-import org.lcsim.event.RawTrackerHit;
-
-/**
- * <code>EcalOnlineRawConverter</code> handles the conversion of raw
- * hits of all modes to energy hit <code>CalorimeterHit</code> objects.
- * This converter will employ the runtime values for all parameters and
- * is intended to emulate the firmware specifically.<br/>
- * <br/>
- * The converter requires the presence of the DAQ configuration manager,
- * which is activated by either <code>DatabaseDAQConfigDriver</code>
- * or <code>DAQConfigDriver</code> depending on from where it is to
- * obtain the configuration.<br/>
- * <br/>
- * This converter is primarily employed in the trigger and hardware
- * diagnostic processes as well as the readout simulation in Monte
- * Carlo.
- *
- * @author Nathan Baltzell <[log in to unmask]>
- * @author Kyle McCarty <[log in to unmask]>
- */
-public class EcalOnlineRawConverter {
- // Defines the maximum number of peaks that may be extracted from
- // a single waveform.
- private int nPeak = 3;
- // The DAQ configuration manager for FADC parameters.
- private FADCConfig config = null;
- // Whether or not a constant integration window should be assumed
- // for the purpose of pedestal calculations/
- private boolean constantWindow = false;
- // The number of nanoseconds in a clock-cycle (sample).
- private static final int nsPerSample = 4;
-
- /**
- * Instantiates the <code>EcalOnlineRawConverter</code> and connects
- * it to the <code>ConfigurationManager</code> to receive settings
- * from the DAQ configuration.
- */
- public EcalOnlineRawConverter() {
- // Track changes in the DAQ configuration.
- ConfigurationManager.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- // Get the FADC configuration.
- config = ConfigurationManager.getInstance().getFADCConfig();
-
- // Get the number of peaks.
- if(config.getMode() == 1) { nPeak = Integer.MAX_VALUE; }
- else { nPeak = config.getMaxPulses(); }
- }
- });
- }
-
- /**
- * Gets the pedestal for a given crystal and threshold crossing.
- * @param cellID - The cell ID of the crystal.
- * @param windowSamples - The size of the readout window. A value
- * of <code>-1</code> indicates an infinite readout window.
- * @param thresholdCrossing - The time of the threshold crossing in
- * 4-nanosecond clock-cycles (samples).
- * @return Returns the pedestal for the crystal and threshold
- * crossing.
- */
- public double getPulsePedestal(long cellID, int windowSamples, int thresholdCrossing) {
- // Track the starting and ending samples over which integration
- // will occur. Only the intermediary samples need be considered
- // for pedestal calculation.
- int firstSample, lastSample;
-
- // For finite readout windows, calculate the pedestal based on
- // the size of the full readout window in the event that the
- // integration window is larger than the readout window.
- if(windowSamples > 0 && (config.getNSA() + config.getNSB()) / nsPerSample >= windowSamples) {
- firstSample = 0;
- lastSample = windowSamples - 1;
- }
-
- // Otherwise, the pedestal should be calculated based on the
- // integration window size.
- else {
- // Define the sample width as equivalent to the integration
- // window size.
- firstSample = thresholdCrossing - config.getNSB() / nsPerSample;
- lastSample = thresholdCrossing + config.getNSA() / nsPerSample - 1;
-
- // In the event of a finite readout window, ignore any
- // samples that fall outside the readout window. Since these
- // are clipped and will not be integrated, these pedestals
- // do not contribute.
- if(windowSamples > 0) {
- if(firstSample < 0) { firstSample = 0; }
- if(lastSample >= windowSamples) { lastSample = windowSamples - 1; }
- }
- }
-
- // Calculate and return the pedestal.
- return(lastSample - firstSample + 1) * config.getPedestal(cellID);
- }
-
- /**
- * Converts a mode-1 digitized waveform into standard energy hit.
- * @param hit - The "hit" object representing the digitized waveform
- * for a given crystal.
- * @return Returns a list of <code>CalorimeterHit</code> objects
- * parsed from the waveform.
- */
- public List<CalorimeterHit> HitDtoA(RawTrackerHit hit) {
- // Get the cell ID for the crystal as well as the digitized
- // waveform samples.
- final long cellID = hit.getCellID();
- final short[] waveform = hit.getADCValues();
-
- // If there are no samples, then there is nothing to integrate.
- if(waveform.length == 0) { return null; }
-
- // The pulse integration threshold is defined as the combination
- // of the pedestal and the threshold configuration parameter.
- final int absoluteThreshold = (int) (config.getPedestal(cellID) + config.getThreshold(cellID));
-
- // Store each instance of a threshold crossing in that can be
- // found within the digitized waveform.
- List<Integer> thresholdCrossings = new ArrayList<Integer>();
-
- // Check for the special case of the first sample exceeding
- // the integration threshold.
- if(waveform[0] > absoluteThreshold) {
- thresholdCrossings.add(0);
- }
-
- // Search the remaining samples for threshold crossings.
- thresholdLoop:
- for(int sample = 1; sample < waveform.length; ++sample) {
- if(waveform[sample] > absoluteThreshold && waveform[sample - 1] <= absoluteThreshold) {
- // Add the sample index to the list of threshold crossing.
- thresholdCrossings.add(sample);
-
- // No new threshold crossings can be registered within
- // this pulse. In the case of mode-1 data, the end of
- // the pulse is considered to be 8 samples past the
- // crossing, as the per the SSP. Otherwise, it is defined
- // by the integration window.
- if(config.getMode() == 1) { sample += 8; }
- else { sample += config.getNSA() / nsPerSample - 1; }
-
- // If there is a limit defined on the maximum number
- // of peaks that may be processed, terminate the search
- // after this number of peaks have been found.
- if(thresholdCrossings.size() >= nPeak) { break thresholdLoop; }
- }
- }
-
- // Use the previously located threshold crossing to generate
- // calorimeter hits.
- List<CalorimeterHit> newHits = new ArrayList<CalorimeterHit>();
- for(int thresholdCrossing : thresholdCrossings) {
- // Perform the pulse integral.
- final double[] data = convertWaveformToPulse(waveform, thresholdCrossing);
- double time = data[0];
- double sum = data[1];
-
- // Perform pedestal subtraction.
- sum -= getPulsePedestal(cellID, waveform.length, thresholdCrossing);
-
- // Perform gain scaling.
- double energy = adcToEnergy(sum, cellID);
-
- // Create a new hit and add it to the list.
- newHits.add(CalorimeterHitUtilities.create(energy, time, cellID));
- }
-
- // Return the list of hits.
- return newHits;
- }
-
- /**
- * Converts a raw mode-3 hit to a proper calorimeter hit in units
- * of energy.
- * @param hit - The raw hit that is to be converted.
- * @param timeOffset - The time offset for the hit.
- * @return Returns a <code>CalorimeterHit</code> hit object that
- * represents the raw mode-3 hit with units of energy and a correct
- * time-stamp.
- */
- public CalorimeterHit HitDtoA(RawCalorimeterHit hit, double timeOffset) {
- // Verify the validity of the time-stamp.
- if(hit.getTimeStamp() % 64 != 0) {
- System.out.println("Unexpected time-stamp: " + hit.getTimeStamp());
- }
-
- // Get the pedestal. In the case of a constant integration window
- // (i.e. infinite readout window size, so no risk of clipping),
- // the window width should be given as -1. Otherwise, the real
- // readout window size should be used.
- long id = hit.getCellID();
- double time = hit.getTimeStamp() / 16.0;
- double pedestal = getPulsePedestal(id, constantWindow ? -1 : config.getWindowWidth(), (int) time / nsPerSample);
-
- // Calculate the total ADC value for the pulse and convert it
- // to energy.
- double adcSum = hit.getAmplitude() - pedestal;
- double rawEnergy = adcToEnergy(adcSum, id);
-
- // Create a calorimeter hit from the result and return it.
- return CalorimeterHitUtilities.create(rawEnergy, time + timeOffset, id);
- }
-
- /**
- * Converts a raw mode-7 hit to a proper calorimeter hit in units
- * of energy.
- * @param hit - The raw hit that is to be converted.
- * @param mode7Data - Additional mode-7 data object.
- * @param timeOffset - The time offset for the hit.
- * @return Returns a <code>CalorimeterHit</code> hit object that
- * represents the raw mode-7 hit with units of energy and a correct
- * time-stamp.
- */
- public CalorimeterHit HitDtoA(RawCalorimeterHit hit, GenericObject mode7Data, double timeOffset) {
- // Get the time and crystal cell ID for the hit. Note that the
- // time-stamps use the full 62.5 ps resolution.
- double time = hit.getTimeStamp() / 16.0;
- long id = hit.getCellID();
-
- // Get the pedestal. In the case of a constant integration window
- // (i.e. infinite readout window size, so no risk of clipping),
- // the window width should be given as -1. Otherwise, the real
- // readout window size should be used.
- double pedestal = getPulsePedestal(id, constantWindow ? -1 : config.getWindowWidth(), (int) time / nsPerSample);
-
- // Calculate the total ADC value for the pulse and convert it
- // to energy.
- double adcSum = hit.getAmplitude() - pedestal;
- double rawEnergy = adcToEnergy(adcSum, id);
-
- // Create a calorimeter hit from the result and return it.
- return CalorimeterHitUtilities.create(rawEnergy, time + timeOffset, id);
- }
-
- /**
- * Converts a value in ADC in a crystal to energy in units of GeV.
- * @param adcSum - The ADC value to convert.
- * @param cellID - The cell ID of the crystal containing the value.
- * @return Returns the ADC value as an energy in units of GeV.
- */
- private double adcToEnergy(double adcSum, long cellID) {
- return config.getGain(cellID) * adcSum * EcalUtils.MeV;
- }
-
- /**
- * Emulate the FADC250 firmware in conversion of Mode-1 waveform to a Mode-3/7 pulse,
- * given a time for threshold crossing.
- */
-
- /**
- * Converts a mode-1 digitized waveform to a mode-3/7 pulse for a
- * a given threshold crossing.
- * @param waveform - The digitized waveform. Each array value should
- * correspond to a sample of the waveform in units of ADC.
- * @param thresholdCrossing - The time of the threshold crossing
- * in samples.
- * @return Returns a <code>double</code> primitive of size 2. The
- * first value represents the time in nanoseconds of the pulser and
- * the second value the total integrated value of the pulse in ADC.
- */
- private double[] convertWaveformToPulse(short[] waveform, int thresholdCrossing) {
- // Store the integration range.
- int firstSample, lastSample;
-
- // If the integration range is larger than the number of samples,
- // then all the samples are used for pulse integration.
- if((config.getNSA() + config.getNSB()) / nsPerSample >= waveform.length) {
- firstSample = 0;
- lastSample = waveform.length - 1;
- }
-
- // Otherwise, the integration range covers a number of samples
- // before and after the threshold crossing as defined by the
- // run parameters.
- else {
- firstSample = thresholdCrossing - config.getNSB() / nsPerSample;
- lastSample = thresholdCrossing + config.getNSA() / nsPerSample - 1;
- }
-
- // Perform the pulse integral.
- double sumADC = 0;
- integrationLoop:
- for (int sample = firstSample; sample <= lastSample; sample++) {
- // If the current sample occurs before the readout window,
- // then it does not exist and can not be integrated. Skip it.
- if(sample < 0) { continue integrationLoop; }
-
- // Likewise, samples that occur after the readout window are
- // not retained and must be skipped.
- if(sample >= waveform.length) { break integrationLoop; }
-
- // Otherwise, add the sample to the pulse total.
- sumADC += waveform[sample];
- }
-
- // Calculate the pulse time with a 4 nanosecond resolution.
- double pulseTime = thresholdCrossing * nsPerSample;
-
- // Return both the pulse time and the total integrated pulse ADC.
- return new double [] { pulseTime, sumADC };
- }
-
- /**
- * Sets whether to use a constant integration window for the the
- * purpose of determining the correct pedestal. This should be used
- * in conjunction with Monte Carlo data during the readout cycle.
- * @param state - <code>true</code> ignores the size of the readout
- * window when calculating pedestals, and <code>false</code> accounts
- * for it in the case of pulse-clipping.
- */
- void setUseConstantWindow(boolean state) {
- constantWindow = state;
- }
+package org.hps.recon.ecal;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.hps.record.daqconfig.ConfigurationManager;
+import org.hps.record.daqconfig.FADCConfig;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.GenericObject;
+import org.lcsim.event.RawCalorimeterHit;
+import org.lcsim.event.RawTrackerHit;
+
+/**
+ * <code>EcalOnlineRawConverter</code> handles the conversion of raw
+ * hits of all modes to energy hit <code>CalorimeterHit</code> objects.
+ * This converter will employ the runtime values for all parameters and
+ * is intended to emulate the firmware specifically.<br/>
+ * <br/>
+ * The converter requires the presence of the DAQ configuration manager,
+ * which is activated by either <code>DatabaseDAQConfigDriver</code>
+ * or <code>DAQConfigDriver</code> depending on from where it is to
+ * obtain the configuration.<br/>
+ * <br/>
+ * This converter is primarily employed in the trigger and hardware
+ * diagnostic processes as well as the readout simulation in Monte
+ * Carlo.
+ *
+ * @author Nathan Baltzell <[log in to unmask]>
+ * @author Kyle McCarty <[log in to unmask]>
+ */
+public class EcalOnlineRawConverter2 {
+ // Defines the maximum number of peaks that may be extracted from
+ // a single waveform.
+ private int nPeak = 3;
+ // The DAQ configuration manager for FADC parameters.
+ private FADCConfig config = null;
+ // Whether or not a constant integration window should be assumed
+ // for the purpose of pedestal calculations/
+ private boolean constantWindow = false;
+ // The number of nanoseconds in a clock-cycle (sample).
+ private static final int nsPerSample = 4;
+
+ /**
+ * Instantiates the <code>EcalOnlineRawConverter</code> and connects
+ * it to the <code>ConfigurationManager</code> to receive settings
+ * from the DAQ configuration.
+ */
+ public EcalOnlineRawConverter2() {
+ // Track changes in the DAQ configuration.
+ ConfigurationManager.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // Get the FADC configuration.
+ config = ConfigurationManager.getInstance().getFADCConfig();
+
+ // Get the number of peaks.
+ if(config.getMode() == 1) { nPeak = Integer.MAX_VALUE; }
+ else { nPeak = config.getMaxPulses(); }
+ }
+ });
+ }
+
+ /**
+ * Gets the pedestal for a given crystal and threshold crossing.
+ * @param cellID - The cell ID of the crystal.
+ * @param windowSamples - The size of the readout window. A value
+ * of <code>-1</code> indicates an infinite readout window.
+ * @param thresholdCrossing - The time of the threshold crossing in
+ * 4-nanosecond clock-cycles (samples).
+ * @return Returns the pedestal for the crystal and threshold
+ * crossing.
+ */
+ public int getPulsePedestal(long cellID, int windowSamples, int thresholdCrossing) {
+ // Track the starting and ending samples over which integration
+ // will occur. Only the intermediary samples need be considered
+ // for pedestal calculation.
+ int firstSample, lastSample;
+
+ // For finite readout windows, calculate the pedestal based on
+ // the size of the full readout window in the event that the
+ // integration window is larger than the readout window.
+ if(windowSamples > 0 && (config.getNSA() + config.getNSB()) / nsPerSample >= windowSamples) {
+ firstSample = 0;
+ lastSample = windowSamples - 1;
+ }
+
+ // Otherwise, the pedestal should be calculated based on the
+ // integration window size.
+ else {
+ // Define the sample width as equivalent to the integration
+ // window size.
+ firstSample = thresholdCrossing - config.getNSB() / nsPerSample;
+ lastSample = thresholdCrossing + config.getNSA() / nsPerSample - 1;
+
+ // In the event of a finite readout window, ignore any
+ // samples that fall outside the readout window. Since these
+ // are clipped and will not be integrated, these pedestals
+ // do not contribute.
+ if(windowSamples > 0) {
+ if(firstSample < 0) { firstSample = 0; }
+ if(lastSample >= windowSamples) { lastSample = windowSamples - 1; }
+ }
+ }
+
+ // Calculate and return the pedestal. The extra 1 MeV added to
+ // the pedestal offsets the rounding error that is incurred when
+ // converting it to an integer.
+ return (int) ((lastSample - firstSample + 1) * (config.getPedestal(cellID) + 0.001));
+ }
+
+ /**
+ * Converts a mode-1 digitized waveform into standard energy hit.
+ * @param hit - The "hit" object representing the digitized waveform
+ * for a given crystal.
+ * @return Returns a list of <code>CalorimeterHit</code> objects
+ * parsed from the waveform.
+ */
+ public List<CalorimeterHit> HitDtoA(RawTrackerHit hit) {
+ // Get the cell ID for the crystal as well as the digitized
+ // waveform samples.
+ final long cellID = hit.getCellID();
+ final short[] waveform = hit.getADCValues();
+
+ // If there are no samples, then there is nothing to integrate.
+ if(waveform.length == 0) { return null; }
+
+ // The pulse integration threshold is defined as the combination
+ // of the pedestal and the threshold configuration parameter.
+ final int absoluteThreshold = (int) (config.getPedestal(cellID) + config.getThreshold(cellID));
+
+ // Store each instance of a threshold crossing in that can be
+ // found within the digitized waveform.
+ List<Integer> thresholdCrossings = new ArrayList<Integer>();
+
+ // Check for the special case of the first sample exceeding
+ // the integration threshold.
+ if(waveform[0] > absoluteThreshold) {
+ thresholdCrossings.add(0);
+ }
+
+ // Search the remaining samples for threshold crossings.
+ thresholdLoop:
+ for(int sample = 1; sample < waveform.length; sample++) {
+ if(waveform[sample] > absoluteThreshold && waveform[sample - 1] <= absoluteThreshold) {
+ // Add the sample index to the list of threshold crossing.
+ thresholdCrossings.add(sample);
+
+ // No new threshold crossings can be registered within
+ // this pulse. In the case of mode-1 data, the end of
+ // the pulse is considered to be 8 samples past the
+ // crossing, as the per the SSP. Otherwise, it is defined
+ // by the integration window.
+ if(config.getMode() == 1) { sample += 8; }
+ else { sample += config.getNSA() / nsPerSample - 1; }
+
+ // If there is a limit defined on the maximum number
+ // of peaks that may be processed, terminate the search
+ // after this number of peaks have been found.
+ if(thresholdCrossings.size() >= nPeak) { break thresholdLoop; }
+ }
+ }
+
+ // Use the previously located threshold crossing to generate
+ // calorimeter hits.
+ List<CalorimeterHit> newHits = new ArrayList<CalorimeterHit>();
+ for(int thresholdCrossing : thresholdCrossings) {
+ // Obtain the pedestal for the pulse.
+ int pedestal = getPulsePedestal(cellID, waveform.length, thresholdCrossing);
+
+ // Perform the pulse integral.
+ final int[] data = convertWaveformToPulse(waveform, thresholdCrossing);
+ int time = data[0];
+ int sum = data[1];
+
+ // Perform pedestal subtraction.
+ sum -= pedestal;
+
+ // Perform gain scaling.
+ double energy = adcToEnergy(sum, cellID);
+
+ // Hits should not have less than zero energy.
+ if(energy < 0) {
+ Logger.getGlobal().log(Level.WARNING, "A hit was produced with " + energy + " GeV energy!");
+ }
+
+ // Create a new hit and add it to the list.
+ newHits.add(CalorimeterHitUtilities.create(energy, time, cellID));
+ }
+
+ // Return the list of hits.
+ return newHits;
+ }
+
+ /**
+ * Converts a raw mode-3 hit to a proper calorimeter hit in units
+ * of energy.
+ * @param hit - The raw hit that is to be converted.
+ * @param timeOffset - The time offset for the hit.
+ * @return Returns a <code>CalorimeterHit</code> hit object that
+ * represents the raw mode-3 hit with units of energy and a correct
+ * time-stamp.
+ */
+ public CalorimeterHit HitDtoA(RawCalorimeterHit hit, double timeOffset) {
+ // Verify the validity of the time-stamp.
+ if(hit.getTimeStamp() % 64 != 0) {
+ System.out.println("Unexpected time-stamp: " + hit.getTimeStamp());
+ }
+
+ // Get the pedestal. In the case of a constant integration window
+ // (i.e. infinite readout window size, so no risk of clipping),
+ // the window width should be given as -1. Otherwise, the real
+ // readout window size should be used.
+ long id = hit.getCellID();
+ double time = hit.getTimeStamp() / 16.0;
+ int pedestal = getPulsePedestal(id, constantWindow ? -1 : config.getWindowWidth(), (int) time / nsPerSample);
+
+ // Calculate the total ADC value for the pulse and convert it
+ // to energy.
+ int adcSum = hit.getAmplitude() - pedestal;
+ double rawEnergy = adcToEnergy(adcSum, id);
+
+ // Create a calorimeter hit from the result and return it.
+ return CalorimeterHitUtilities.create(rawEnergy, time + timeOffset, id);
+ }
+
+ /**
+ * Converts a raw mode-7 hit to a proper calorimeter hit in units
+ * of energy.
+ * @param hit - The raw hit that is to be converted.
+ * @param mode7Data - Additional mode-7 data object.
+ * @param timeOffset - The time offset for the hit.
+ * @return Returns a <code>CalorimeterHit</code> hit object that
+ * represents the raw mode-7 hit with units of energy and a correct
+ * time-stamp.
+ */
+ public CalorimeterHit HitDtoA(RawCalorimeterHit hit, GenericObject mode7Data, double timeOffset) {
+ // Get the time and crystal cell ID for the hit. Note that the
+ // time-stamps use the full 62.5 ps resolution.
+ double time = hit.getTimeStamp() / 16.0;
+ long id = hit.getCellID();
+
+ // Get the pedestal. In the case of a constant integration window
+ // (i.e. infinite readout window size, so no risk of clipping),
+ // the window width should be given as -1. Otherwise, the real
+ // readout window size should be used.
+ int pedestal = getPulsePedestal(id, constantWindow ? -1 : config.getWindowWidth(), (int) time / nsPerSample);
+
+ // Calculate the total ADC value for the pulse and convert it
+ // to energy.
+ int adcSum = hit.getAmplitude() - pedestal;
+ double rawEnergy = adcToEnergy(adcSum, id);
+
+ // Create a calorimeter hit from the result and return it.
+ return CalorimeterHitUtilities.create(rawEnergy, time + timeOffset, id);
+ }
+
+ /**
+ * Converts a value in ADC in a crystal to energy in units of GeV.
+ * @param adcSum - The ADC value to convert.
+ * @param cellID - The cell ID of the crystal containing the value.
+ * @return Returns the ADC value as an energy in units of GeV.
+ */
+ private double adcToEnergy(int adcSum, long cellID) {
+ // Define the gain. To mimic the hardware, this is done through
+ // manipulation of integer values only. We multiply by 256 to
+ // preserve extra digits of accuracy.
+ int gain = (int) (256.0 * (config.getGain(cellID) + 0.001));
+
+ // Multiply the gain by the pulse-subtracted energy sum. Also
+ // remove the extra factor of 256. This gives the energy in units
+ // of MeV.
+ int energy = (int) ((adcSum * gain) / 256.0);
+
+ // Return the final energy as a double in units of GeV.
+ return energy * EcalUtils.MeV;
+ }
+
+ /**
+ * Emulate the FADC250 firmware in conversion of Mode-1 waveform to a Mode-3/7 pulse,
+ * given a time for threshold crossing.
+ */
+
+ /**
+ * Converts a mode-1 digitized waveform to a mode-3/7 pulse for a
+ * a given threshold crossing.
+ * @param waveform - The digitized waveform. Each array value should
+ * correspond to a sample of the waveform in units of ADC.
+ * @param thresholdCrossing - The time of the threshold crossing
+ * in samples.
+ * @return Returns a <code>double</code> primitive of size 2. The
+ * first value represents the time in nanoseconds of the pulser and
+ * the second value the total integrated value of the pulse in ADC.
+ */
+ private int[] convertWaveformToPulse(short[] waveform, int thresholdCrossing) {
+ // Store the integration range.
+ int firstSample, lastSample;
+
+ // If the integration range is larger than the number of samples,
+ // then all the samples are used for pulse integration.
+ if((config.getNSA() + config.getNSB()) / nsPerSample >= waveform.length) {
+ firstSample = 0;
+ lastSample = waveform.length - 1;
+ }
+
+ // Otherwise, the integration range covers a number of samples
+ // before and after the threshold crossing as defined by the
+ // run parameters.
+ else {
+ firstSample = thresholdCrossing - config.getNSB() / nsPerSample;
+ lastSample = thresholdCrossing + config.getNSA() / nsPerSample - 1;
+ }
+
+ // Perform the pulse integral.
+ int sumADC = 0;
+ integrationLoop:
+ for (int sample = firstSample; sample <= lastSample; sample++) {
+ // If the current sample occurs before the readout window,
+ // then it does not exist and can not be integrated. Skip it.
+ if(sample < 0) { continue integrationLoop; }
+
+ // Likewise, samples that occur after the readout window are
+ // not retained and must be skipped.
+ if(sample >= waveform.length) { break integrationLoop; }
+
+ // Otherwise, add the sample to the pulse total.
+ sumADC += waveform[sample];
+ }
+
+ // Calculate the pulse time with a 4 nanosecond resolution.
+ int pulseTime = thresholdCrossing * nsPerSample;
+
+ // Return both the pulse time and the total integrated pulse ADC.
+ return new int[] { pulseTime, sumADC };
+ }
+
+ /**
+ * Sets whether to use a constant integration window for the the
+ * purpose of determining the correct pedestal. This should be used
+ * in conjunction with Monte Carlo data during the readout cycle.
+ * @param state - <code>true</code> ignores the size of the readout
+ * window when calculating pedestals, and <code>false</code> accounts
+ * for it in the case of pulse-clipping.
+ */
+ void setUseConstantWindow(boolean state) {
+ constantWindow = state;
+ }
}
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalOnlineRawConverterDriver.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalOnlineRawConverterDriver.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalOnlineRawConverterDriver.java Tue Dec 6 19:31:53 2016
@@ -1,160 +1,160 @@
-package org.hps.recon.ecal;
-
-import java.util.ArrayList;
-import java.util.List;
-
-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.lcio.LCIOConstants;
-import org.lcsim.util.Driver;
-
-/**
- * This class is used to convert between collections of {@link org.lcsim.event.RawCalorimeterHit}
- * and {@link org.lcsim.event.RawTrackerHit}, objects with ADC/sample information, and
- * collections of {@link org.lcsim.event.CalorimeterHit}, objects with energy/time information.
- *
- * org.hps.recon.ecal.EcalRawConverter is called to do most of the lower level work.
- *
- *
-*/
-public class EcalOnlineRawConverterDriver extends Driver {
- private EcalOnlineRawConverter converter = null;
- /**
- * The input LCIO collection name. This can be either a
- * {@link org.lcsim.event.RawTrackerHit} or
- * {@link org.lcsim.event.RawCalorimeterHit}. These have ADC and
- * sample time information.
- */
- private String rawCollectionName = "EcalReadoutHits";
-
- /**
- * The output LCIO collection name. This will always a
- * {@link org.lcsim.event.CalorimeterHit} with energy (GeV) and
- * ns time information.
- */
- private String ecalCollectionName = "EcalCalHits";
-
- /**
- * 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";
-
- /**
- * Instantiates the <code>EcalOnlineRawConverter</code> for this
- * driver.
- */
- public EcalOnlineRawConverterDriver() { converter = new EcalOnlineRawConverter(); }
-
- /**
- * Checks that the required LCIO collection names are defined.
- */
- @Override
- public void startOfData() {
- if(ecalCollectionName == null) {
- throw new RuntimeException("The parameter ecalCollectionName was not set!");
- }
- }
-
- @Override
- public void process(EventHeader event) {
- // Do not process the event if the DAQ configuration is not
- // initialized. All online raw converter parameters are obtained
- // from this class, and this nothing can be done before they
- // are available.
- if(!ConfigurationManager.isInitialized()) {
- return;
- }
-
- double timeOffset = 0.0;
-
- // Define the LCIO data flags.
- 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.
-
- // Create a list to store hits.
- ArrayList<CalorimeterHit> newHits = new ArrayList<CalorimeterHit>();
-
- // Events that contain RawTrackerHit objects use mode-1 data.
- if(event.hasCollection(RawTrackerHit.class, rawCollectionName)) {
- // Get the list of mode-1 waveforms.
- List<RawTrackerHit> hits = event.get(RawTrackerHit.class, rawCollectionName);
-
- // Extract hits from each waveform and store them.
- for(RawTrackerHit hit : hits) {
- newHits.addAll(converter.HitDtoA(hit));
- }
-
- // Add the hits to the data stream.
- event.put(ecalCollectionName, newHits, CalorimeterHit.class, flags, ecalReadoutName);
- }
-
- // Events that contain RawCalorimeterHit objects are either
- // mode-3 or mode-7.
- if(event.hasCollection(RawCalorimeterHit.class, rawCollectionName)) {
- // Check for extra relations data. This indicates that the
- // hits should be interpreted as mode-7.
- 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();
- GenericObject extraData = (GenericObject) rel.getTo();
- newHits.add(converter.HitDtoA(hit, extraData, timeOffset));
- }
- }
-
- // Otherwise, the hits should be treated as mode-3.
- else {
- List<RawCalorimeterHit> hits = event.get(RawCalorimeterHit.class, rawCollectionName);
- for(RawCalorimeterHit hit : hits) {
- newHits.add(converter.HitDtoA(hit, timeOffset));
- }
- }
- event.put(ecalCollectionName, newHits, CalorimeterHit.class, flags, ecalReadoutName);
- }
- }
-
- /**
- * Sets the output {@link org.lcsim.event.CalorimeterHit} LCIO
- * collection name.
- * @param ecalCollectionName - The LCIO collection name for output
- * data.
- */
- public void setEcalCollectionName(String ecalCollectionName) {
- this.ecalCollectionName = ecalCollectionName;
- }
-
- /**
- * Sets whether to use a constant integration window for the the
- * purpose of determining the correct pedestal. This should be used
- * in conjunction with Monte Carlo data during the readout cycle.
- * @param state - <code>true</code> ignores the size of the readout
- * window when calculating pedestals, and <code>false</code> accounts
- * for it in the case of pulse-clipping.
- */
- public void setIsReadoutSimulation(boolean state) {
- converter.setUseConstantWindow(state);
- }
-
- /**
- * Sets the input raw hit data LCIO collection name. Depending on
- * the driver configuration, this could be either a collection of
- * {@link org.lcsim.event.RawTrackerHit} objects for mode-1 data or
- * {@link org.lcsim.event.RawCalorimeterHit} objects for mode-3
- * and mode-7 data.
- * @param rawCollectionName - The LCIO collection name for raw data.
- */
- public void setRawCollectionName(String rawCollectionName) {
- this.rawCollectionName = rawCollectionName;
- }
+package org.hps.recon.ecal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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.lcio.LCIOConstants;
+import org.lcsim.util.Driver;
+
+/**
+ * This class is used to convert between collections of {@link org.lcsim.event.RawCalorimeterHit}
+ * and {@link org.lcsim.event.RawTrackerHit}, objects with ADC/sample information, and
+ * collections of {@link org.lcsim.event.CalorimeterHit}, objects with energy/time information.
+ *
+ * org.hps.recon.ecal.EcalRawConverter is called to do most of the lower level work.
+ *
+ *
+*/
+public class EcalOnlineRawConverterDriver2 extends Driver {
+ private EcalOnlineRawConverter2 converter = null;
+ /**
+ * The input LCIO collection name. This can be either a
+ * {@link org.lcsim.event.RawTrackerHit} or
+ * {@link org.lcsim.event.RawCalorimeterHit}. These have ADC and
+ * sample time information.
+ */
+ private String rawCollectionName = "EcalReadoutHits";
+
+ /**
+ * The output LCIO collection name. This will always a
+ * {@link org.lcsim.event.CalorimeterHit} with energy (GeV) and
+ * ns time information.
+ */
+ private String ecalCollectionName = "EcalCalHits";
+
+ /**
+ * 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";
+
+ /**
+ * Instantiates the <code>EcalOnlineRawConverter2</code> for this
+ * driver.
+ */
+ public EcalOnlineRawConverterDriver2() { converter = new EcalOnlineRawConverter2(); }
+
+ /**
+ * Checks that the required LCIO collection names are defined.
+ */
+ @Override
+ public void startOfData() {
+ if(ecalCollectionName == null) {
+ throw new RuntimeException("The parameter ecalCollectionName was not set!");
+ }
+ }
+
+ @Override
+ public void process(EventHeader event) {
+ // Do not process the event if the DAQ configuration is not
+ // initialized. All online raw converter parameters are obtained
+ // from this class, and this nothing can be done before they
+ // are available.
+ if(!ConfigurationManager.isInitialized()) {
+ return;
+ }
+
+ double timeOffset = 0.0;
+
+ // Define the LCIO data flags.
+ 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.
+
+ // Create a list to store hits.
+ ArrayList<CalorimeterHit> newHits = new ArrayList<CalorimeterHit>();
+
+ // Events that contain RawTrackerHit objects use mode-1 data.
+ if(event.hasCollection(RawTrackerHit.class, rawCollectionName)) {
+ // Get the list of mode-1 waveforms.
+ List<RawTrackerHit> hits = event.get(RawTrackerHit.class, rawCollectionName);
+
+ // Extract hits from each waveform and store them.
+ for(RawTrackerHit hit : hits) {
+ newHits.addAll(converter.HitDtoA(hit));
+ }
+
+ // Add the hits to the data stream.
+ event.put(ecalCollectionName, newHits, CalorimeterHit.class, flags, ecalReadoutName);
+ }
+
+ // Events that contain RawCalorimeterHit objects are either
+ // mode-3 or mode-7.
+ if(event.hasCollection(RawCalorimeterHit.class, rawCollectionName)) {
+ // Check for extra relations data. This indicates that the
+ // hits should be interpreted as mode-7.
+ 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();
+ GenericObject extraData = (GenericObject) rel.getTo();
+ newHits.add(converter.HitDtoA(hit, extraData, timeOffset));
+ }
+ }
+
+ // Otherwise, the hits should be treated as mode-3.
+ else {
+ List<RawCalorimeterHit> hits = event.get(RawCalorimeterHit.class, rawCollectionName);
+ for(RawCalorimeterHit hit : hits) {
+ newHits.add(converter.HitDtoA(hit, timeOffset));
+ }
+ }
+ event.put(ecalCollectionName, newHits, CalorimeterHit.class, flags, ecalReadoutName);
+ }
+ }
+
+ /**
+ * Sets the output {@link org.lcsim.event.CalorimeterHit} LCIO
+ * collection name.
+ * @param ecalCollectionName - The LCIO collection name for output
+ * data.
+ */
+ public void setEcalCollectionName(String ecalCollectionName) {
+ this.ecalCollectionName = ecalCollectionName;
+ }
+
+ /**
+ * Sets whether to use a constant integration window for the the
+ * purpose of determining the correct pedestal. This should be used
+ * in conjunction with Monte Carlo data during the readout cycle.
+ * @param state - <code>true</code> ignores the size of the readout
+ * window when calculating pedestals, and <code>false</code> accounts
+ * for it in the case of pulse-clipping.
+ */
+ public void setIsReadoutSimulation(boolean state) {
+ converter.setUseConstantWindow(state);
+ }
+
+ /**
+ * Sets the input raw hit data LCIO collection name. Depending on
+ * the driver configuration, this could be either a collection of
+ * {@link org.lcsim.event.RawTrackerHit} objects for mode-1 data or
+ * {@link org.lcsim.event.RawCalorimeterHit} objects for mode-3
+ * and mode-7 data.
+ * @param rawCollectionName - The LCIO collection name for raw data.
+ */
+ public void setRawCollectionName(String rawCollectionName) {
+ this.rawCollectionName = rawCollectionName;
+ }
}
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterer.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterer.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPOnlineClusterer.java Tue Dec 6 19:31:53 2016
@@ -108,7 +108,6 @@
private IHistogram1D clusterTotalEnergy = aida.histogram1D("GTP(O) Cluster Plots/Cluster Total Energy Distribution", 176, 0.0, 2.2);
private IHistogram2D hitDistribution = aida.histogram2D("GTP(O) Cluster Plots/Hit Distribution", 46, -23, 23, 11, -5.5, 5.5);
private IHistogram2D clusterDistribution = aida.histogram2D("GTP(O) Cluster Plots/Cluster Seed Distribution", 46, -23, 23, 11, -5.5, 5.5);
- private IHistogram1D energyDistribution = aida.histogram1D("GTP(O) Cluster Plots/Percent Negative Energy Distribution", 100, 0.0, 1.0);
/**
* Instantiates a new instance of a readout GTP clustering algorithm.
@@ -140,10 +139,10 @@
Collections.sort(hitList, new Comparator<CalorimeterHit>() {
@Override
public int compare(CalorimeterHit firstHit, CalorimeterHit secondHit) {
- int[] ix = { firstHit.getIdentifierFieldValue("ix"), secondHit.getIdentifierFieldValue("ix") };
+ int[] ix = { getHitX(firstHit), getHitX(secondHit) };
if(ix[0] != ix[1]) { return Integer.compare(ix[0], ix[1]); }
else {
- int iy[] = { firstHit.getIdentifierFieldValue("iy"), secondHit.getIdentifierFieldValue("iy") };
+ int iy[] = { getHitY(firstHit), getHitY(secondHit) };
return Integer.compare(iy[0], iy[1]);
}
}
@@ -152,12 +151,7 @@
// Print the hit collection.
System.out.println("Event Hit Collection:");
for(CalorimeterHit hit : hitList) {
- int ix = hit.getIdentifierFieldValue("ix");
- int iy = hit.getIdentifierFieldValue("iy");
- double energy = hit.getCorrectedEnergy();
- double time = hit.getTime();
-
- System.out.printf("\tHit --> %6.3f GeV at (%3d, %3d) and at t = %.2f%n", energy, ix, iy, time);
+ System.out.printf("\t%s%n", getHitText(hit));
}
System.out.println();
}
@@ -169,7 +163,7 @@
Collections.sort(hitList, new Comparator<CalorimeterHit>() {
@Override
public int compare(CalorimeterHit firstHit, CalorimeterHit secondHit) {
- return Double.compare(secondHit.getTime(), firstHit.getTime());
+ return Double.compare(getHitTime(secondHit), getHitTime(firstHit));
}
});
@@ -183,15 +177,24 @@
// Iterate over each hit and see if it qualifies as a seed hit.
seedLoop:
for(CalorimeterHit seed : hitList) {
+ // VERBOSE :: Output the seed that is being considered.
+ if(verbose) {
+ System.out.println("\n");
+ System.out.println("Considering seed " + getHitText(seed));
+ }
+
// Put the hit energy into the hit energy distribution.
- hitEnergy.fill(seed.getCorrectedEnergy());
- hitDistribution.fill(seed.getIdentifierFieldValue("ix"), seed.getIdentifierFieldValue("iy"));
+ hitEnergy.fill(getHitEnergy(seed));
+ hitDistribution.fill(getHitX(seed), getHitY(seed));
// Check whether the potential seed passes the seed
// energy cut.
- if(seed.getCorrectedEnergy() < seedThreshold) {
+ if(verbose) { System.out.printf("Checking seed energy threshold %5.3f >= %5.3f... ", getHitEnergy(seed), seedThreshold); }
+ if(getHitEnergy(seed) < seedThreshold) {
+ if(verbose) { System.out.println("[fail]"); }
continue seedLoop;
}
+ if(verbose) { System.out.println("[pass]"); }
// Create a cluster for the potential seed.
BaseCluster protoCluster = createBasicCluster();
@@ -247,16 +250,6 @@
clusterHitCount.fill(protoCluster.getCalorimeterHits().size());
clusterDistribution.fill(protoCluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"),
protoCluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy"));
-
- // Determine how much energy in the cluster is negative
- // and how is positive.
- double nenergy = 0.0;
- double penergy = 0.0;
- for(CalorimeterHit hit : protoCluster.getCalorimeterHits()) {
- if(hit.getCorrectedEnergy() > 0) { penergy += hit.getCorrectedEnergy(); }
- else { nenergy += hit.getCorrectedEnergy(); }
- }
- energyDistribution.fill(Math.abs(nenergy) / (penergy + Math.abs(nenergy)));
}
// VERBOSE :: Print out all the clusters in the event.
@@ -391,6 +384,26 @@
// windows appropriately.
timeAfter = cyclesAfter * 4;
timeWindow = Math.max(timeBefore, timeAfter);
+ }
+
+ private static final String getHitText(CalorimeterHit hit) {
+ return String.format("Hit --> %6.3f GeV at (%3d, %3d) and at t = %.2f", getHitEnergy(hit), getHitX(hit), getHitY(hit), getHitTime(hit));
+ }
+
+ private static final double getHitEnergy(CalorimeterHit hit) {
+ return hit.getCorrectedEnergy();
+ }
+
+ private static final int getHitX(CalorimeterHit hit) {
+ return hit.getIdentifierFieldValue("ix");
+ }
+
+ private static final int getHitY(CalorimeterHit hit) {
+ return hit.getIdentifierFieldValue("iy");
+ }
+
+ private static final double getHitTime(CalorimeterHit hit) {
+ return hit.getTime();
}
/**
@@ -561,4 +574,4 @@
// treated as within time.
else { return false; }
}
-}
+}
|