]>
Commit | Line | Data |
---|---|---|
cf3100fa | 1 | /******************************************************************************* |
2 | * Copyright(c) 2003, IceCube Experiment at the South Pole. All rights reserved. | |
3 | * | |
4 | * Author: The IceCube RALICE-based Offline Project. | |
5 | * Contributors are mentioned in the code where appropriate. | |
6 | * | |
7 | * Permission to use, copy, modify and distribute this software and its | |
8 | * documentation strictly for non-commercial purposes is hereby granted | |
9 | * without fee, provided that the above copyright notice appears in all | |
10 | * copies and that both the copyright notice and this permission notice | |
11 | * appear in the supporting documentation. | |
12 | * The authors make no claims about the suitability of this software for | |
13 | * any purpose. It is provided "as is" without express or implied warranty. | |
14 | *******************************************************************************/ | |
15 | ||
16 | // $Id$ | |
17 | ||
18 | /////////////////////////////////////////////////////////////////////////// | |
19 | // Class IceMakeHits | |
20 | // TTask derived class to perform hit extraction from waveforms. | |
21 | // | |
22 | // The code in this processor is based on the algorithms as developed by | |
23 | // Nick van Eijndhoven and Garmt de Vries-Uiterweerd (Utrecht University, The Netherlands). | |
24 | // | |
25 | // Procedure applied for Amanda TWR data : | |
26 | // --------------------------------------- | |
27 | // | |
28 | // 1) The waveform is fed to a TSpectrum object, and the peak locations | |
29 | // are determined with the TSpectrum::Search() function. | |
30 | // | |
31 | // 2) The waveform is divided into regions corresponding to the peaks found by | |
32 | // TSpectrum. The region boundary between two peaks is at the location of | |
33 | // the minimum between the two peaks. | |
34 | // | |
35 | // 3) For each region the "effective baseline" (used in the | |
36 | // evaluation of the leading edge value) is determined as : | |
37 | // effective baseline = fBasefracXXX * value at lower region boundary. | |
38 | // This takes into account the effect from the previous pulse. | |
39 | // For the first pulse, the effective baseline is equal to the overall | |
40 | // baseline. | |
41 | // | |
42 | // 4) For each region, the point of steepest rise between the lower region | |
43 | // boundary and the peak location is determined. The tangent at this point | |
44 | // is extrapolated to the effective baseline. The point of intersection yields the | |
45 | // leading edge. | |
46 | // | |
47 | // 5) For each region the range of charge integration is determined as : | |
48 | // - Start of integration at the lower region boundary or at the leading edge, | |
49 | // whichever comes last; | |
50 | // - End of integration at the upper region boundary or at the point where the | |
51 | // signal drops below the overall baseline, whichever comes first. | |
52 | // | |
53 | // 6) For each region the integrated charge is determined as : | |
54 | // Sum over bins in integration range of (value in bin - overall baseline). | |
55 | // | |
56 | // 7) For each pulse the quality is evaluated by requiring that : | |
57 | // peak location - lower region boundary > lower region boundary - leading edge. | |
58 | // For a too shallow steepest rise, the leading edge value is unreliable, in | |
59 | // which case the pulse is merged with the previous pulse. | |
60 | // | |
61 | // 8) Each pulse is checked for saturation and discarded if necessary. | |
62 | // | |
63 | // 9) TSpectrum needs a minimum number of bins for its Search function, otherwise | |
64 | // the clipping window is too large, which causes an error. If a waveform does | |
65 | // not contain enough bins, the following alternative approach is used : | |
66 | // - A loop over all bins is performed. | |
67 | // - As soon as the signal exceeds a given threshold, a pulse is started. | |
68 | // - The pulse ends when the signal drops below the threshold again. | |
69 | // - While looping, the charge is integrated for each pulse. | |
70 | // | |
71 | // The defaults of the various parameters can be changed by the corresponding | |
72 | // Set memberfunctions. | |
73 | // | |
74 | // Information about the actual parameter settings can be found in the event | |
75 | // structure itself via the device named "IceMakeHits". | |
76 | // | |
77 | //--- Author: Nick van Eijndhoven and Garmt de Vries-Uiterweerd 15-jan-2007 Utrecht University | |
78 | //- Modified: NvE $Date$ Utrecht University | |
79 | /////////////////////////////////////////////////////////////////////////// | |
80 | ||
81 | #include "IceMakeHits.h" | |
82 | #include "Riostream.h" | |
83 | ||
84 | ClassImp(IceMakeHits) // Class implementation to enable ROOT I/O | |
85 | ||
86 | IceMakeHits::IceMakeHits(const char* name,const char* title) : TTask(name,title) | |
87 | { | |
88 | // Default constructor. | |
89 | fEvt=0; | |
90 | fDaq=0; | |
91 | fBasefracA=0.5; | |
92 | fSigmaA=1.5; | |
93 | fMaxPeaksA=10; | |
94 | fMinPulseHeightA=50; | |
95 | fThresholdA=0.2; | |
96 | } | |
97 | /////////////////////////////////////////////////////////////////////////// | |
98 | IceMakeHits::~IceMakeHits() | |
99 | { | |
100 | // Default destructor. | |
101 | } | |
102 | /////////////////////////////////////////////////////////////////////////// | |
103 | void IceMakeHits::SetBasefracA(Float_t val) | |
104 | { | |
105 | // Set baseline fractional update for Amanda TWR extraction. | |
106 | // The default as set in the constructor of this class is 0.5. | |
107 | fBasefracA=val; | |
108 | } | |
109 | /////////////////////////////////////////////////////////////////////////// | |
110 | void IceMakeHits::SetSigmaA(Float_t val) | |
111 | { | |
112 | // Set clipping window width for Amanda TWR extraction. | |
113 | // The default as set in the constructor of this class is 1.5. | |
114 | fSigmaA=val; | |
115 | } | |
116 | /////////////////////////////////////////////////////////////////////////// | |
117 | void IceMakeHits::SetMaxPeaksA(Int_t val) | |
118 | { | |
119 | // Set maximum number of peaks in a waveform for Amanda TWR extraction. | |
120 | // The default as set in the constructor of this class is 10. | |
121 | fMaxPeaksA=val; | |
122 | } | |
123 | /////////////////////////////////////////////////////////////////////////// | |
124 | void IceMakeHits::SetMinPulseHeightA(Float_t val) | |
125 | { | |
126 | // Set minimum required pulse height for Amanda TWR extraction. | |
127 | // This is used only for narrow pulses that cannot be handled with TSpectrum. | |
128 | // The default as set in the constructor of this class is 50. | |
129 | fMinPulseHeightA=val; | |
130 | } | |
131 | /////////////////////////////////////////////////////////////////////////// | |
132 | void IceMakeHits::SetThresholdA(Float_t val) | |
133 | { | |
134 | // Set threshold for use in analysis of narrow pulses for Amanda TWR extraction. | |
135 | // A peak is assumed to start when the signal rises above threshold*maxval, | |
136 | // where maxval is the maximum value found in the waveform. | |
137 | // The default as set in the constructor of this class is 0.2. | |
138 | fThresholdA=val; | |
139 | } | |
140 | /////////////////////////////////////////////////////////////////////////// | |
141 | void IceMakeHits::Exec(Option_t* opt) | |
142 | { | |
143 | // Implementation of the hit cleaning procedures. | |
144 | ||
145 | TString name=opt; | |
146 | AliJob* parent=(AliJob*)(gROOT->GetListOfTasks()->FindObject(name.Data())); | |
147 | ||
148 | if (!parent) return; | |
149 | ||
150 | fEvt=(IceEvent*)parent->GetObject("IceEvent"); | |
151 | if (!fEvt) return; | |
152 | ||
153 | fDaq=(AliDevice*)fEvt->GetDevice("Daq"); | |
154 | if (!fDaq) return; | |
155 | ||
156 | // Storage of the used parameters in the IceMakeHits device | |
157 | AliSignal params; | |
158 | params.SetNameTitle("IceMakeHits","IceMakeHits processor parameters"); | |
159 | params.SetSlotName("BasefracA",1); | |
160 | params.SetSlotName("SigmaA",2); | |
161 | params.SetSlotName("MaxPeaksA",3); | |
162 | params.SetSignal(fBasefracA,1); | |
163 | params.SetSignal(fSigmaA,2); | |
164 | params.SetSignal(fMaxPeaksA,3); | |
165 | ||
166 | fEvt->AddDevice(params); | |
167 | ||
168 | Amanda(); | |
169 | InIce(); | |
170 | IceTop(); | |
a32bc6c6 | 171 | |
cf3100fa | 172 | } |
173 | /////////////////////////////////////////////////////////////////////////// | |
174 | void IceMakeHits::Amanda() | |
175 | { | |
176 | // Hit extraction from the Amanda TWR data. | |
177 | ||
178 | // Arrays for storing info | |
179 | Float_t* baseline=new Float_t[fMaxPeaksA]; | |
180 | Int_t* lowend=new Int_t[fMaxPeaksA]; | |
181 | Int_t* upend=new Int_t[fMaxPeaksA]; | |
182 | Int_t* startcharge=new Int_t[fMaxPeaksA]; | |
183 | Int_t* stopcharge=new Int_t[fMaxPeaksA]; | |
a32bc6c6 | 184 | Int_t* status=new Int_t[fMaxPeaksA]; // 0=OK, 1=rejected, 2=saturation, 3=completely below baseline |
cf3100fa | 185 | Float_t* leadingedge=new Float_t[fMaxPeaksA]; |
186 | Float_t* charge=new Float_t[fMaxPeaksA]; | |
187 | Float_t* tot=new Float_t[fMaxPeaksA]; | |
188 | ||
189 | // Some objects and variables we will need | |
190 | TH1F foo, diff; | |
a32bc6c6 | 191 | TSpectrum spec(fMaxPeaksA); |
cf3100fa | 192 | Int_t nrIterations=(Int_t)(7*fSigmaA+0.5); // Number of iterations used in TSpectrum::SearchHighRes() |
193 | Int_t npeaks=0, ibin=0, lookforsteepestuntilbin=0, steep=0; | |
194 | Float_t maxval=0, rise=0, rc=0, yyy=0; | |
195 | Bool_t pulsegoingon=false; | |
196 | Int_t* index=new Int_t[fMaxPeaksA]; | |
197 | ||
198 | // Update the DAQ device data for the nature of the hit info | |
199 | fDaq->AddNamedSlot("TWR"); | |
200 | fDaq->SetSignal(1,"TWR"); | |
201 | Int_t idx=fDaq->GetSlotIndex("Muon"); | |
202 | if (idx) fDaq->SetSignal(0,idx); | |
203 | ||
204 | // All Amanda OMs with a signal | |
205 | TObjArray* aoms=fEvt->GetDevices("IceAOM"); | |
206 | ||
207 | // OM, waveform and hit | |
208 | IceAOM* omx=0; | |
209 | TH1F* wf=0; | |
210 | AliSignal hit; | |
211 | hit.SetSlotName("ADC",1); | |
212 | hit.SetSlotName("LE",2); | |
213 | hit.SetSlotName("TOT",3); | |
214 | ||
215 | // Loop over all fired OMs and extract the hit info | |
216 | for (Int_t iom=0; iom<aoms->GetEntries(); iom++) | |
217 | { | |
218 | omx=(IceAOM*)aoms->At(iom); | |
219 | if (!omx) continue; | |
220 | // Remove all existing hits of this OM | |
221 | omx->RemoveHits(); | |
cf3100fa | 222 | // Should we skip OMs that we know from the dbase to have problems ? |
223 | //// if (omx->GetDeadValue("ADC") || omx->GetDeadValue("LE") || omx->GetDeadValue("TOT")) continue; | |
224 | ||
225 | // Investigate all waveforms for this OM | |
226 | for (Int_t iwf=1; iwf<=omx->GetNwaveforms(); iwf++) | |
227 | { | |
228 | wf=omx->GetWaveform(iwf); | |
229 | if (!wf) continue; | |
cf3100fa | 230 | maxval=wf->GetMaximum(); |
cf3100fa | 231 | // Check if clipping window is not too large |
232 | if(wf->GetNbinsX() > 2*nrIterations+1) | |
233 | { | |
234 | // Find peaks with TSpectrum | |
a32bc6c6 | 235 | npeaks=spec.Search(wf,fSigmaA,"goff"); |
cf3100fa | 236 | // Discard waveform if no or too many peaks found |
237 | if(npeaks<1 || npeaks>fMaxPeaksA) continue; | |
238 | ||
239 | // Get differential of WF | |
240 | diff=*wf; | |
241 | for(ibin=2;ibin<diff.GetNbinsX();ibin++) | |
242 | { | |
243 | diff.SetBinContent(ibin,wf->GetBinContent(ibin)-wf->GetBinContent(ibin-1)); | |
244 | } | |
245 | diff.SetBinContent(1,0); | |
cf3100fa | 246 | // Set baseline and lower end for first peak, |
247 | baseline[0]=0; | |
248 | lowend[0]=1; | |
249 | ||
250 | // Sort peaks in time | |
a32bc6c6 | 251 | TMath::Sort(npeaks,spec.GetPositionX(),index,false); |
cf3100fa | 252 | // For each of the peaks, |
253 | for(Int_t ipeak=0; ipeak<npeaks; ipeak++) | |
254 | { | |
255 | // Find baseline and region around peak | |
256 | foo=*wf; | |
257 | // (Second and later peaks: lower edge = upper edge previous peak, | |
258 | // baseline is average of previous baseline and minimum value between two | |
259 | // peaks) | |
260 | if(ipeak>0) | |
261 | { | |
262 | lowend[ipeak]=upend[ipeak-1]+1; | |
263 | baseline[ipeak]=fBasefracA*foo.GetBinContent(lowend[ipeak]); | |
264 | } | |
265 | // (Upper edge range is minimum between this and next peak) | |
266 | if(ipeak<npeaks-1) | |
267 | { | |
a32bc6c6 | 268 | foo.SetAxisRange(spec.GetPositionX()[index[ipeak]],spec.GetPositionX()[index[ipeak+1]]); |
cf3100fa | 269 | upend[ipeak]=foo.GetMinimumBin(); |
270 | } | |
271 | // (Last peak: upper edge is end of histo) | |
272 | else | |
273 | { | |
274 | upend[ipeak]=wf->GetNbinsX(); | |
275 | } | |
cf3100fa | 276 | // Find steepest rise |
a32bc6c6 | 277 | lookforsteepestuntilbin=wf->FindBin(spec.GetPositionX()[index[ipeak]]); |
cf3100fa | 278 | foo=diff; |
a32bc6c6 | 279 | // Look for steepest rise between lower edge and peak position |
280 | foo.SetAxisRange(wf->GetBinCenter(lowend[ipeak]),wf->GetBinCenter(lookforsteepestuntilbin)); | |
281 | // Signal should be above baseline at location of steepest rise | |
cf3100fa | 282 | do |
283 | { | |
284 | steep=foo.GetMaximumBin(); | |
285 | rise=foo.GetBinContent(steep); | |
a32bc6c6 | 286 | if(rise==-1e9) break; |
cf3100fa | 287 | foo.SetBinContent(steep,-1e9); |
288 | } while(wf->GetBinContent(steep)<baseline[ipeak]); | |
a32bc6c6 | 289 | if(rise==-1e9) |
290 | { | |
291 | status[ipeak]=3; | |
292 | continue; | |
293 | } | |
cf3100fa | 294 | |
295 | // Extrapolate tangent to find leading edge | |
296 | yyy=wf->GetBinContent(steep)-baseline[ipeak]; | |
297 | rc=rise/foo.GetBinWidth(steep); | |
298 | if(rc>0) leadingedge[ipeak]=wf->GetBinCenter(steep)-yyy/rc; else leadingedge[ipeak]=0; | |
299 | ||
300 | // Determine peak status | |
301 | status[ipeak]=0; | |
302 | // Check for saturation | |
a32bc6c6 | 303 | if(rc<0.1 && wf->GetBinContent(wf->FindBin(spec.GetPositionX()[index[ipeak]])) == maxval) |
cf3100fa | 304 | { |
305 | status[ipeak]=2; | |
306 | } | |
307 | // Check quality: LE should not be too far below lower edge | |
308 | // Otherwise, ignore this peak and set baseline back to what it was | |
a32bc6c6 | 309 | else if(wf->GetBinLowEdge(lowend[ipeak]) - leadingedge[ipeak] > spec.GetPositionX()[index[ipeak]] - wf->GetBinLowEdge(lowend[ipeak])) |
cf3100fa | 310 | { |
311 | status[ipeak]=1; | |
312 | if(ipeak>0) baseline[ipeak]=baseline[ipeak-1]; | |
313 | } | |
314 | ||
315 | // Start charge integration at LE, or at lower edge of range | |
316 | startcharge[ipeak]=wf->FindBin(leadingedge[ipeak]); | |
317 | if(lowend[ipeak]>startcharge[ipeak]) startcharge[ipeak]=lowend[ipeak]; | |
318 | ||
319 | // Integrate charge until pulse drop below baseline, or else until edge of range | |
320 | stopcharge[ipeak]=upend[ipeak]; | |
a32bc6c6 | 321 | for(ibin=wf->FindBin(spec.GetPositionX()[index[ipeak]]); ibin<=upend[ipeak]; ibin++) |
cf3100fa | 322 | { |
323 | if(wf->GetBinContent(ibin)<0) | |
324 | { | |
325 | stopcharge[ipeak]=ibin-1; | |
326 | break; | |
327 | } | |
328 | } | |
329 | ||
330 | // Determine time over threshold | |
331 | tot[ipeak]=wf->GetBinLowEdge(stopcharge[ipeak]+1)-wf->GetBinLowEdge(startcharge[ipeak]); | |
332 | ||
333 | // Determine charge | |
334 | charge[ipeak]=0; | |
335 | for(ibin=startcharge[ipeak]; ibin<=stopcharge[ipeak]; ibin++) | |
336 | { | |
337 | charge[ipeak]+=wf->GetBinContent(ibin); | |
338 | } | |
339 | ||
340 | } // end loop over peaks | |
341 | ||
342 | // Check all peaks, from latest to earliest | |
343 | for(int ipeak=npeaks-1; ipeak>=0; ipeak--) | |
344 | { | |
345 | ||
346 | // If this peak was rejected, add charge and TOT to previous peak (if there is one) | |
347 | if(status[ipeak]==1 && ipeak>0) | |
348 | { | |
349 | charge[ipeak-1]+=charge[ipeak]; | |
350 | charge[ipeak]=0; | |
351 | tot[ipeak-1]+=tot[ipeak]; | |
352 | tot[ipeak]=0; | |
353 | } | |
354 | ||
355 | // If this peak is OK, add hit info | |
356 | if(status[ipeak]==0) | |
357 | { | |
358 | hit.Reset(); | |
359 | hit.SetSignal(charge[ipeak],"ADC"); | |
360 | hit.SetSignal(leadingedge[ipeak],"LE"); | |
361 | hit.SetSignal(tot[ipeak],"TOT"); | |
362 | omx->AddHit(hit); | |
363 | } | |
364 | ||
365 | } // end loop over peaks | |
366 | ||
367 | // If number of bins too small, use different method | |
368 | } | |
369 | else | |
370 | { | |
371 | // If maximum value high enough to suspect presence of peak, | |
372 | if(maxval>fMinPulseHeightA) | |
373 | { | |
374 | // Loop over bins | |
375 | pulsegoingon=false; | |
376 | npeaks=0; | |
377 | for(ibin=1; ibin<=wf->GetNbinsX(); ibin++) | |
378 | { | |
379 | // If bin content above threshold, start pulse | |
380 | if(wf->GetBinContent(ibin)>fThresholdA*maxval){ | |
381 | if(!pulsegoingon) | |
382 | { | |
383 | // Pulse starts here | |
384 | pulsegoingon=true; | |
385 | leadingedge[npeaks]=wf->GetBinLowEdge(ibin); | |
386 | charge[npeaks]=wf->GetBinContent(ibin); | |
387 | } | |
388 | else | |
389 | { | |
390 | // Pulse continues | |
391 | charge[npeaks]+=wf->GetBinContent(ibin); | |
392 | } | |
393 | } | |
394 | else | |
395 | { | |
396 | if(pulsegoingon) | |
397 | { | |
398 | // Pulse ends here | |
399 | tot[npeaks]=wf->GetBinLowEdge(ibin)-leadingedge[npeaks]; | |
400 | ||
401 | // Store pulse information | |
402 | hit.Reset(); | |
403 | hit.SetSignal(charge[npeaks],"ADC"); | |
404 | hit.SetSignal(leadingedge[npeaks],"LE"); | |
405 | hit.SetSignal(tot[npeaks],"TOT"); | |
406 | omx->AddHit(hit); | |
407 | ||
408 | // Get ready for next pulse | |
409 | pulsegoingon=false; | |
410 | npeaks++; | |
411 | } | |
412 | } | |
413 | ||
414 | } // End of loop over bins | |
415 | } | |
416 | ||
417 | } // End of alternative method for narrow pulses | |
418 | ||
419 | } // End of WF loop | |
420 | } // End of OM loop | |
421 | ||
422 | // Clean up | |
423 | delete[] baseline; | |
424 | delete[] lowend; | |
425 | delete[] upend; | |
426 | delete[] startcharge; | |
427 | delete[] stopcharge; | |
428 | delete[] status; | |
429 | delete[] leadingedge; | |
430 | delete[] charge; | |
431 | delete[] tot; | |
432 | delete[] index; | |
433 | } | |
434 | /////////////////////////////////////////////////////////////////////////// | |
435 | void IceMakeHits::InIce() | |
436 | { | |
437 | // Hit extraction from IceCube InIce ATWD data. | |
438 | } | |
439 | /////////////////////////////////////////////////////////////////////////// | |
440 | void IceMakeHits::IceTop() | |
441 | { | |
442 | // Hit extraction from IceTop ATWD data. | |
443 | } | |
444 | /////////////////////////////////////////////////////////////////////////// |