LDMX Software
SiStripWaveformBuilder.cxx
1#include "Tracking/Reco/SiStripWaveformBuilder.h"
2
3#include <algorithm>
4#include <map>
5
6#include "Tracking/Digitization/SiStripConstants.h"
7#include "Tracking/Digitization/StripPulseFitter.h"
8#include "Tracking/Event/RawSiStripHit.h"
9#include "Tracking/Reco/SiStripChannelMap.h"
10#include "Tracking/Reco/TrackerPedestals.h"
11
12namespace tracking::reco {
13
15 input_collection_ =
16 ps.get<std::string>("input_collection", input_collection_);
17 input_pass_name_ = ps.get<std::string>("input_pass_name", input_pass_name_);
18 output_collection_ =
19 ps.get<std::string>("output_collection", output_collection_);
20 high_threshold_ = ps.get<double>("high_threshold", high_threshold_);
21 min_high_samples_ = ps.get<int>("min_high_samples", min_high_samples_);
22 low_threshold_ = ps.get<double>("low_threshold", low_threshold_);
24 ps.get<int>("min_consecutive_low", min_consecutive_low_);
25 n_triggers_ = ps.get<int>("n_triggers", n_triggers_);
26}
27
29 const auto& peds =
31
32 const auto& hits = event.getCollection<ldmx::RawSiStripHit>(input_collection_,
33 input_pass_name_);
34
35 // Build the pulse shape once (reused for every channel fit this job).
36 if (!pulse_shape_) {
38 std::string(tracking::digitization::PULSE_SHAPE_NAME),
39 tracking::digitization::PEAKING_TIME_NS,
40 tracking::digitization::SECOND_TIME_CONST_NS);
41 }
42
43 // Key: encodes (feb, hybrid, pchannel) uniquely.
44 // Value: vector of (apv_trigger, samples) pairs.
45 struct TriggerSamples {
46 uint16_t apv_trigger_;
47 std::vector<short> samples_;
48 };
49 struct ChannelInfo {
50 float noise_{0};
51 uint8_t hybrid_id_{0};
52 uint8_t feb_id_{0};
53 std::vector<TriggerSamples> triggers_;
54 };
55
56 // Use a map keyed by (feb, hybrid, pchannel) for grouping.
57 std::map<uint32_t, ChannelInfo> channel_map;
58
59 for (const auto& hit : hits) {
60 const float noise = peds.noise(hit.getFebId(), hit.getHybridId(),
61 hit.getApvId(), hit.getChannel());
62 if (noise <= 0) continue;
63
64 const int16_t pchannel =
65 channelmap::pchannel(hit.getApvId(), hit.getChannel());
66 uint32_t key =
67 channelmap::groupKey(hit.getFebId(), hit.getHybridId(), pchannel);
68
69 auto& ch = channel_map[key];
70 ch.noise_ = noise;
71 ch.hybrid_id_ = hit.getHybridId();
72 ch.feb_id_ = hit.getFebId();
73 ch.triggers_.push_back({hit.getApvTrigger(), hit.getSamples()});
74 }
75
76 std::vector<ldmx::SiStripWaveform> waveforms;
77
78 for (auto& [key, ch] : channel_map) {
79 // Sort triggers by apv_trigger index.
80 std::sort(ch.triggers_.begin(), ch.triggers_.end(),
81 [](const TriggerSamples& a, const TriggerSamples& b) {
82 return a.apv_trigger_ < b.apv_trigger_;
83 });
84
85 // Assemble full waveform: [s0_t0, s1_t0, s2_t0, s0_t1, ...].
86 std::vector<short> samples;
87 samples.reserve(ch.triggers_.size() *
89 for (const auto& trig : ch.triggers_) {
90 for (short s : trig.samples_) samples.push_back(s);
91 }
92
93 if (samples.empty()) continue;
94
95 // Condition 1: at least min_high_samples_ samples exceed high_threshold_.
96 int n_high = 0;
97 for (short s : samples)
98 if (static_cast<float>(s) / ch.noise_ > high_threshold_) ++n_high;
99 if (n_high < min_high_samples_) continue;
100
101 // Condition 2: at least min_consecutive_low_ consecutive samples exceed
102 // low_threshold_.
103 int max_streak = 0, cur_streak = 0;
104 for (short s : samples) {
105 if (static_cast<float>(s) / ch.noise_ > low_threshold_) {
106 max_streak = std::max(max_streak, ++cur_streak);
107 } else {
108 cur_streak = 0;
109 }
110 }
111 if (max_streak < min_consecutive_low_) continue;
112
113 int16_t pchannel = channelmap::pchannelFromGroupKey(key);
114 uint8_t n_trig = static_cast<uint8_t>(
115 std::min(static_cast<int>(ch.triggers_.size()), 255));
116
117 // --- TEST: fit a CR-RC pulse shape to the full assembled waveform. ---
118 // Samples are pedestal-subtracted (ped = 0) and lie on a uniform 25 ns
119 // grid, so the scan range is sized to the waveform: T may peak anywhere
120 // from before sample 0 to the last sample.
121 const int n_samples = static_cast<int>(samples.size());
122 const double t_scan_max =
123 n_samples * tracking::digitization::SAMPLING_INTERVAL_NS;
126 /*t0_offset_ns=*/0.0,
127 tracking::digitization::SAMPLING_INTERVAL_NS,
128 /*pedestal_adc=*/0.0,
129 /*noise_sigma_adc=*/ch.noise_,
130 /*t_scan_min_ns=*/-50.0, t_scan_max, /*t_scan_step_ns=*/1.0);
131 const auto fit = fitter.fit(samples);
133 if (!fit.converged) ++n_fit_failed_;
134
135 ldmx_log(trace) << "fit feb=" << static_cast<int>(ch.feb_id_)
136 << " hyb=" << static_cast<int>(ch.hybrid_id_)
137 << " pch=" << pchannel << " nsamp=" << n_samples
138 << " noise=" << ch.noise_
139 << " -> converged=" << (fit.converged ? "yes" : "no")
140 << " amp=" << fit.amplitude << " t0=" << fit.t0 << "ns"
141 << " chi2/ndf=" << fit.chi2 << "/" << fit.ndf << " ("
142 << (fit.ndf > 0 ? fit.chi2 / fit.ndf : 0.0) << ")";
143
144 waveforms.emplace_back(std::move(samples), pchannel, ch.hybrid_id_,
145 ch.feb_id_, n_trig);
146 waveforms.back().setFitResult(
147 static_cast<float>(fit.amplitude), static_cast<float>(fit.t0),
148 static_cast<float>(fit.chi2), fit.ndf, fit.converged);
149 }
150
151 ldmx_log(debug) << "Built " << waveforms.size()
152 << " waveforms (>=" << min_high_samples_ << " samples @"
153 << high_threshold_ << "s, streak>=" << min_consecutive_low_
154 << " @" << low_threshold_ << "s) from " << hits.size()
155 << " hits";
156
157 for (const auto& wf : waveforms) {
158 ldmx_log(trace) << wf;
159 }
160
161 event.add(output_collection_, waveforms);
162}
163
165 const long n_ok = n_fit_attempted_ - n_fit_failed_;
166 const double fail_pct =
168 ? 100.0 * static_cast<double>(n_fit_failed_) / n_fit_attempted_
169 : 0.0;
170 ldmx_log(info) << "Fit summary: " << n_fit_attempted_ << " attempted, "
171 << n_ok << " converged, " << n_fit_failed_ << " failed ("
172 << fail_pct << "%)";
173}
174
175} // namespace tracking::reco
176
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
const T & getCondition(const std::string &condition_name)
Access a conditions object for the current event.
Implements an event buffer system for storing event data.
Definition Event.h:42
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:78
Implementation of a raw digitized hit from a silicon strip detector.
static std::unique_ptr< PulseShape > make(const std::string &name, double tp, double tp2=0.0)
Factory: construct a pulse shape by name.
Fits a pulse shape to the ADC samples of a single silicon-strip readout channel to extract hit amplit...
FitResult fit(const std::vector< short > &samples) const
Fit the pulse to the given ADC sample vector.
Assemble per-trigger pedestal-subtracted hits into full per-channel waveforms.
double high_threshold_
per-sample significance for high-threshold cut
std::unique_ptr< tracking::digitization::PulseShape > pulse_shape_
Pulse shape used for the per-waveform fit test (built lazily in produce).
int min_consecutive_low_
min consecutive samples exceeding low_threshold
long n_fit_attempted_
waveforms passed to the fitter
void configure(framework::config::Parameters &ps) override
Callback for the EventProcessor to configure itself from the given set of parameters.
int n_triggers_
expected APV triggers per RoR
int min_high_samples_
min samples exceeding high_threshold
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
double low_threshold_
per-sample significance for consecutive-streak cut
void produce(framework::Event &event) override
Process the event and put new data products into it.
long n_fit_failed_
fits that did not converge
static const std::string CONDITIONS_NAME
Name of the conditions object (must match the python registration).
constexpr int K_SAMPLES_PER_APV_TRIGGER
Number of ADC samples the APV25 reads out per channel per trigger.
constexpr int16_t pchannelFromGroupKey(uint32_t key)
Extract the pchannel back out of a key produced by groupKey().
constexpr uint32_t groupKey(uint8_t feb, uint8_t hybrid, int16_t pchannel)
Pack (feb, hybrid, pchannel) into a single 32-bit waveform-grouping key.
constexpr int16_t pchannel(uint8_t apv_id, uint8_t channel)
Convert an (APV id, APV channel) pair into the physical strip number (pchannel) within a hybrid,...