|  | 
| 21 | 21 | #include "PulseDigi.h" | 
| 22 | 22 | 
 | 
| 23 | 23 | class HGCROCRawSample { | 
| 24 |  | -public: | 
| 25 |  | -  HGCROCRawSample(std::size_t n_meas) | 
| 26 |  | -      : meas_types(n_meas, 0), amplitudes(n_meas, 0), TOAs(n_meas, 0), TOTs(n_meas, 0) {} | 
| 27 |  | - | 
| 28 |  | -  void setAmplitude(std::size_t idx, double amp) { amplitudes[idx] = amp; } | 
| 29 |  | -  void setTOA(std::size_t idx, double toa) { | 
| 30 |  | -    meas_types[idx] = 1; | 
| 31 |  | -    TOAs[idx]       = toa; | 
| 32 |  | -  } | 
| 33 |  | -  void setTOT(std::size_t idx, double tot) { | 
| 34 |  | -    meas_types[idx] = 2; | 
| 35 |  | -    TOTs[idx]       = tot; | 
| 36 |  | -  } | 
| 37 |  | - | 
| 38 |  | -  const std::vector<uint8_t>& getMeasTypes() const { return meas_types; } | 
| 39 |  | -  const std::vector<double>& getAmplitudes() const { return amplitudes; } | 
| 40 |  | -  const std::vector<double>& getTOAs() const { return TOAs; } | 
| 41 |  | -  const std::vector<double>& getTOTs() const { return TOTs; } | 
| 42 |  | - | 
| 43 |  | -private: | 
| 44 |  | -  // meas_type 1: Pulse crossed below the threshold. | 
| 45 |  | -  // meas_type 2: Pulse didn't cross the threshold. | 
| 46 |  | -  // meas_type 0: Neither of the above cases. | 
| 47 |  | -  std::vector<uint8_t> meas_types; | 
| 48 |  | -  std::vector<double> amplitudes; | 
| 49 |  | -  std::vector<double> TOAs; | 
| 50 |  | -  std::vector<double> TOTs; | 
|  | 24 | +	public: | 
|  | 25 | +		struct RawEntry { | 
|  | 26 | +			double adc{0}; | 
|  | 27 | +			double toa{0}; | 
|  | 28 | +			double tot{0}; | 
|  | 29 | +			bool totProgress{false}; | 
|  | 30 | +			bool totComplete{false}; | 
|  | 31 | +		}; | 
|  | 32 | + | 
|  | 33 | +		HGCROCRawSample(std::size_t n_samp) { rawEntries.resize(n_samp); } | 
|  | 34 | + | 
|  | 35 | +		void setAmplitude(std::size_t idx, double amp) { rawEntries[idx].adc = amp; } | 
|  | 36 | +		void setTOA(std::size_t idx, double toa) { rawEntries[idx].toa = toa; } | 
|  | 37 | +		void setTOT(std::size_t idx, double cross_t) { rawEntries[idx].tot = cross_t - rawEntries[idx].toa; } | 
|  | 38 | +		void setTotProgress(std::size_t idx) { rawEntries[idx].totProgress = true; } | 
|  | 39 | +		void setTotComplete(std::size_t idx) { rawEntries[idx].totComplete = true; } | 
|  | 40 | + | 
|  | 41 | +		const std::vector<RawEntry>& getEntries() const { return rawEntries; } | 
|  | 42 | + | 
|  | 43 | +	private: | 
|  | 44 | +		std::vector<RawEntry> rawEntries; | 
| 51 | 45 | }; | 
| 52 | 46 | 
 | 
| 53 | 47 | namespace eicrecon { | 
| 54 | 48 | 
 | 
| 55 |  | -void PulseDigi::init() {} | 
| 56 |  | - | 
| 57 |  | -void PulseDigi::process(const PulseDigi::Input& input, const PulseDigi::Output& output) const { | 
| 58 |  | -  const auto [in_pulses] = input; | 
| 59 |  | -  auto [out_digi_hits]   = output; | 
| 60 |  | - | 
| 61 |  | -  for (const auto& pulse : *in_pulses) { | 
| 62 |  | -    double pulse_t     = pulse.getTime(); | 
| 63 |  | -    double pulse_dt    = pulse.getInterval(); | 
| 64 |  | -    std::size_t n_amps = pulse.getAmplitude().size(); | 
| 65 |  | - | 
| 66 |  | -    // Estimate the number of samples. | 
| 67 |  | -    const std::size_t timeIdx_begin = | 
| 68 |  | -        static_cast<std::size_t>(std::floor(pulse_t / m_cfg.time_window)); | 
| 69 |  | -    const std::size_t timeIdx_end = static_cast<std::size_t>( | 
| 70 |  | -        std::floor((pulse_t + (n_amps - 1) * pulse_dt) / m_cfg.time_window)); | 
| 71 |  | - | 
| 72 |  | -    HGCROCRawSample raw_sample(timeIdx_end - timeIdx_begin + 1); | 
| 73 |  | - | 
| 74 |  | -    int sample_tick = std::llround(m_cfg.sample_period / pulse_dt); | 
| 75 |  | -    int adc_counter = | 
| 76 |  | -        std::llround((timeIdx_begin * m_cfg.sample_period + m_cfg.adc_phase - pulse_t) / pulse_dt); | 
| 77 |  | - | 
| 78 |  | -    bool tot_progress  = false; | 
| 79 |  | -    bool tot_complete  = false; | 
| 80 |  | -    std::size_t toaIdx = 0; | 
| 81 |  | - | 
| 82 |  | -    for (std::size_t i = 0; i < n_amps; i++) { | 
| 83 |  | -      double t = pulse_t + i * pulse_dt; | 
| 84 |  | -      const std::size_t sampleIdx = | 
| 85 |  | -          static_cast<std::size_t>(std::floor(t / m_cfg.sample_period)) - timeIdx_begin; | 
| 86 |  | - | 
| 87 |  | -      adc_counter++; | 
| 88 |  | -      // Measure amplitudes | 
| 89 |  | -      if (adc_counter == sample_tick) { | 
| 90 |  | -        raw_sample.setAmplitude(sampleIdx, pulse.getAmplitude()[i]); | 
| 91 |  | -        adc_counter = 0; | 
| 92 |  | -      } | 
| 93 |  | - | 
| 94 |  | -      // Measure crossing point for TOA | 
| 95 |  | -      if (!tot_progress && pulse.getAmplitude()[i] > m_cfg.toa_thres) { | 
| 96 |  | -        toaIdx = sampleIdx; | 
| 97 |  | -        raw_sample.setTOA(sampleIdx, | 
| 98 |  | -                          get_crossing_time(m_cfg.threshold, t, pulse_dt, pulse.getAmplitude()[i], | 
| 99 |  | -                                            pulse.getAmplitude()[i - 1])); | 
| 100 |  | -        tot_progress = true; | 
| 101 |  | -        tot_complete = false; | 
| 102 |  | -      } | 
| 103 |  | - | 
| 104 |  | -      // Measure crossing point for TOT | 
| 105 |  | -      if (tot_progress && !tot_complete && pulse.getAmplitude()[i] < m_cfg.threshold) { | 
| 106 |  | -        raw_sample.setTOT(toaIdx, | 
| 107 |  | -                          get_crossing_time(m_cfg.threshold, t, pulse_dt, pulse.getAmplitude()[i], | 
| 108 |  | -                                            pulse.getAmplitude()[i - 1])); | 
| 109 |  | -        tot_complete = true; | 
| 110 |  | -        tot_progess  = false; | 
| 111 |  | -      } | 
| 112 |  | -    } | 
| 113 |  | -  } | 
| 114 |  | -} // PulseDigi:process | 
| 115 |  | - | 
| 116 |  | -double PulseDigi::get_crossing_time(double thres, double t1, double t2, double amp1, | 
| 117 |  | -                                    double amp2) const { | 
| 118 |  | -  double numerator   = (thres - amp2) * (t2 - t1); | 
| 119 |  | -  double denominator = amp2 - amp1; | 
| 120 |  | -  double added       = t2; | 
| 121 |  | -  return (numerator / denominator) + added; | 
| 122 |  | -} | 
|  | 49 | +	void PulseDigi::init() {} | 
|  | 50 | + | 
|  | 51 | +	void PulseDigi::process(const PulseDigi::Input& input, const PulseDigi::Output& output) const { | 
|  | 52 | +		const auto [in_pulses] = input; | 
|  | 53 | +		auto [out_digi_hits]   = output; | 
|  | 54 | + | 
|  | 55 | +		for (const auto& pulse : *in_pulses) { | 
|  | 56 | +			double pulse_t     = pulse.getTime(); | 
|  | 57 | +			double pulse_dt    = pulse.getInterval(); | 
|  | 58 | +			std::size_t n_amps = pulse.getAmplitude().size(); | 
|  | 59 | + | 
|  | 60 | +			// Estimate the number of samples. | 
|  | 61 | +			const std::size_t timeIdx_begin = | 
|  | 62 | +				static_cast<std::size_t>(std::floor(pulse_t / m_cfg.time_window)); | 
|  | 63 | +			const std::size_t timeIdx_end = static_cast<std::size_t>( | 
|  | 64 | +					std::floor((pulse_t + (n_amps - 1) * pulse_dt) / m_cfg.time_window)); | 
|  | 65 | + | 
|  | 66 | +			HGCROCRawSample raw_sample(timeIdx_end - timeIdx_begin + 1); | 
|  | 67 | +	 | 
|  | 68 | +			// For ADC, amplitude is measured with a fixed phase. | 
|  | 69 | +			// This was reproduced by sample_tick and adc_counter as follows. | 
|  | 70 | +			// Amplitude is measured whenever adc_counter reaches sample_tick. | 
|  | 71 | +			int sample_tick = std::llround(m_cfg.time_window / pulse_dt); | 
|  | 72 | +			int adc_counter = | 
|  | 73 | +				std::llround((timeIdx_begin * m_cfg.time_window + m_cfg.adc_phase - pulse_t) / pulse_dt); | 
|  | 74 | + | 
|  | 75 | +			bool tot_progress  = false; | 
|  | 76 | +			bool tot_complete  = false; | 
|  | 77 | +			std::size_t toaIdx = 0; | 
|  | 78 | + | 
|  | 79 | +			for (std::size_t i = 0; i < n_amps; i++) { | 
|  | 80 | +				double t = pulse_t + i * pulse_dt; | 
|  | 81 | +				const std::size_t sampleIdx = | 
|  | 82 | +					static_cast<std::size_t>(std::floor(t / m_cfg.time_window)) - timeIdx_begin; | 
|  | 83 | + | 
|  | 84 | +				adc_counter++; | 
|  | 85 | + | 
|  | 86 | +				// Measure amplitudes for ADC | 
|  | 87 | +				if (adc_counter == sample_tick) { | 
|  | 88 | +					raw_sample.setAmplitude(sampleIdx, pulse.getAmplitude()[i]); | 
|  | 89 | +					adc_counter = 0; | 
|  | 90 | +					if(tot_progress) raw_sample.setTotProgress(sampleIdx); | 
|  | 91 | +				} | 
|  | 92 | + | 
|  | 93 | +				// Measure up-crossing time for TOA | 
|  | 94 | +				if (!tot_progress && pulse.getAmplitude()[i] > m_cfg.toa_thres) { | 
|  | 95 | +					toaIdx = sampleIdx; | 
|  | 96 | +					raw_sample.setTOA(sampleIdx, | 
|  | 97 | +							get_crossing_time(m_cfg.toa_thres, pulse_dt, t, pulse.getAmplitude()[i], | 
|  | 98 | +								pulse.getAmplitude()[i - 1])); | 
|  | 99 | +					tot_progress = true; | 
|  | 100 | +					tot_complete = false; | 
|  | 101 | +					raw_sample.setTotProgress(sampleIdx); | 
|  | 102 | +				} | 
|  | 103 | + | 
|  | 104 | +				// Measure down-crossing time for TOT | 
|  | 105 | +				if (tot_progress && !tot_complete && pulse.getAmplitude()[i] < m_cfg.tot_thres) {  | 
|  | 106 | +					raw_sample.setTOT(toaIdx, | 
|  | 107 | +							get_crossing_time(m_cfg.tot_thres, pulse_dt, t, pulse.getAmplitude()[i], | 
|  | 108 | +								pulse.getAmplitude()[i - 1])); | 
|  | 109 | +					tot_progress = false; | 
|  | 110 | +					tot_complete = true; | 
|  | 111 | +					raw_sample.setTotComplete(sampleIdx); | 
|  | 112 | +				} | 
|  | 113 | +			} | 
|  | 114 | + | 
|  | 115 | +			// Fill HGCROCSamples and RawHGCROCHit | 
|  | 116 | +			auto out_digi_hit = out_digi_hits->create(); | 
|  | 117 | +			out_digi_hit.setCellID(pulse.getCellID()); | 
|  | 118 | + | 
|  | 119 | +			const auto& entries = raw_sample.getEntries(); | 
|  | 120 | + | 
|  | 121 | +			for (const auto& entry : entries) { | 
|  | 122 | +				edm4eic::HGCROCSample sample;	 | 
|  | 123 | +				auto adc = std::max(std::llround(entry.adc / m_cfg.dyRangeADC * m_cfg.capADC), 0LL); | 
|  | 124 | +				sample.ADC = adc > m_cfg.capADC ? m_cfg.capADC : adc; | 
|  | 125 | +				auto toa = std::max(std::llround(entry.toa / m_cfg.dyRangeTOA * m_cfg.capTOA), 0LL); | 
|  | 126 | +                                sample.timeOfArrival = toa > m_cfg.capTOA ? m_cfg.capTOA : toa; | 
|  | 127 | +				auto tot = std::max(std::llround(entry.tot / m_cfg.dyRangeTOT * m_cfg.capTOT), 0LL); | 
|  | 128 | +                                sample.timeOverThreshold = tot > m_cfg.capTOT ? m_cfg.capTOT : tot; | 
|  | 129 | +				sample.TOTInProgress = entry.totProgress; | 
|  | 130 | +				sample.TOTComplete = entry.totComplete; | 
|  | 131 | +				out_digi_hit.addToSamples(sample); | 
|  | 132 | +			} | 
|  | 133 | +		} | 
|  | 134 | +	} // PulseDigi:process | 
|  | 135 | + | 
|  | 136 | +	double PulseDigi::get_crossing_time(double thres, double dt, double t, double amp1, | 
|  | 137 | +			double amp2) const { | 
|  | 138 | +		double numerator   = (thres - amp2) * dt; | 
|  | 139 | +		double denominator = amp2 - amp1; | 
|  | 140 | +		double added       = t; | 
|  | 141 | +		return (numerator / denominator) + added; | 
|  | 142 | +	} | 
| 123 | 143 | } // namespace eicrecon | 
0 commit comments