source: trunk/Mars/msimcamera/MSimCamera.cc@ 19976

Last change on this file since 19976 was 19664, checked in by tbretz, 5 years ago
Added some longer comments on the meaning of some variables, the valid range must be aligned with the pulse width, not Xminand Xmax of the pulse. The pulse is now shifted such that a photon with t=0 (w.r.t. to the simulation sampling window) gets sampled fully into the window
File size: 20.4 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of CheObs, the Modular Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appears in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 1/2009 <mailto:tbretz@phys.ethz.ch>
19!
20! Copyright: CheObs Software Development, 2000-2013
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MSimCamera
28//
29// This task initializes the analog channels with analog noise and simulated
30// the analog pulses from the photon signal.
31//
32// Input Containers:
33// MPhotonEvent
34// MPhotonStatistics
35// MRawRunHeader
36//
37// Output Containers:
38// MAnalogChannels
39//
40//////////////////////////////////////////////////////////////////////////////
41#include "MSimCamera.h"
42
43#include <TF1.h>
44#include <TRandom.h> // Needed for TRandom
45
46#include "MLog.h"
47#include "MLogManip.h"
48
49#include "MTruePhotonsPerPixelCont.h"
50
51#include "MSpline3.h"
52#include "MParSpline.h"
53
54#include "MParList.h"
55
56#include "MPhotonEvent.h"
57#include "MPhotonData.h"
58
59#include "MPedestalCam.h"
60#include "MPedestalPix.h"
61
62#include "MAnalogSignal.h"
63#include "MAnalogChannels.h"
64
65#include "MParameters.h"
66
67#include "MMcEvt.hxx" // To be replaced by a CheObs class
68#include "MRawRunHeader.h"
69
70ClassImp(MSimCamera);
71
72using namespace std;
73
74// --------------------------------------------------------------------------
75//
76// Default Constructor.
77//
78MSimCamera::MSimCamera(const char* name, const char *title)
79 : fEvt(0), fStat(0), fRunHeader(0), fElectronicNoise(0), fGain(0),
80 fCamera(0), fMcEvt(0),fCrosstalkCoeffParam(0), fSpline(0), fBaselineGain(kFALSE),
81 fDefaultOffset(-1), fDefaultNoise(-1), fDefaultGain(-1), fACFudgeFactor(0),
82 fACTimeConstant(0)
83
84{
85 fName = name ? name : "MSimCamera";
86 fTitle = title ? title : "Task to simulate the electronic noise and to convert photons into pulses";
87}
88
89// --------------------------------------------------------------------------
90//
91// Search for the necessayr parameter containers.
92// Setup spline for pulse shape.
93//
94Int_t MSimCamera::PreProcess(MParList *pList)
95{
96 fMcEvt = (MMcEvt*)pList->FindCreateObj("MMcEvt");
97 if (!fMcEvt)
98 return kFALSE;
99
100 fCamera = (MAnalogChannels*)pList->FindCreateObj("MAnalogChannels");
101 if (!fCamera)
102 return kFALSE;
103
104 fEvt = (MPhotonEvent*)pList->FindObject("MPhotonEvent");
105 if (!fEvt)
106 {
107 *fLog << err << "MPhotonEvent not found... aborting." << endl;
108 return kFALSE;
109 }
110
111 fStat = (MPhotonStatistics*)pList->FindObject("MPhotonStatistics");
112 if (!fStat)
113 {
114 *fLog << err << "MPhotonStatistics not found... aborting." << endl;
115 return kFALSE;
116 }
117
118 fRunHeader = (MRawRunHeader *)pList->FindObject("MRawRunHeader");
119 if (!fRunHeader)
120 {
121 *fLog << err << "MRawRunHeader not found... aborting." << endl;
122 return kFALSE;
123 }
124 // -------------------------------------------------------------------
125 // Dominik Neise and Sebastian Mueller on fix time offsets:
126 // We obtain the fix temporal offsets for the FACT camera pixels out of
127 // a text file. The textfile must be mentioned in the ceres.rc file.
128 // There are no default offsets on purporse. The filename must be specified
129 // in ceres.rc and the file must be parsed without errors and it must
130 // provide exactly 1440 floating point numbers.
131 fFixTimeOffsetsBetweenPixelsInNs =
132 (MMatrix*)pList->FindObject("MFixTimeOffset");
133 if (!fFixTimeOffsetsBetweenPixelsInNs)
134 {
135 // the key value pair providing the text file is not present in the
136 // environment env.
137 *fLog << err << "In Source: "<< __FILE__ <<" in line: "<< __LINE__;
138 *fLog << " in function: "<< __func__ <<"\n";
139 *fLog << "MFixTimeOffset not found... aborting." << endl;
140 return kFALSE;
141
142 }
143 else if ( fFixTimeOffsetsBetweenPixelsInNs->fM.size() != 1440 )
144 {
145 // The number of time offsets must match the number of pixels in the
146 // FACT camera.
147 *fLog << err << "In Source: "<< __FILE__ <<" in line: "<< __LINE__;
148 *fLog << " in function: "<< __func__ <<"\n";
149 *fLog << "MFixTimeOffset has the wrong dimension! ";
150 *fLog << "There should be "<< 1440 <<" time offsets ";
151 *fLog << "(one for each pixel in FACT) but there are: ";
152 *fLog << fFixTimeOffsetsBetweenPixelsInNs->fM.size() << "! ";
153 *fLog << "... aborting." << endl;
154 return kFALSE;
155 }
156 // Check all entries for inf and nan. Those are not accepted here.
157 for(size_t row_index=0; row_index<fFixTimeOffsetsBetweenPixelsInNs->fM.size(); row_index++){
158 const vector<double> row = fFixTimeOffsetsBetweenPixelsInNs->fM.at(row_index);
159 for(size_t col_index=0; col_index<row.size(); col_index++){
160 const double specific_delay = row.at(col_index);
161 if (!TMath::Finite(specific_delay)) {
162 *fLog << err << "In Source: "<< __FILE__ <<" in line: ";
163 *fLog << __LINE__;
164 *fLog << " in function: "<< __func__ <<"\n";
165 *fLog << "There is a non normal specific_delay in the fix temporal ";
166 *fLog << "pixel offsets. This is that at least one specific_delay is ";
167 *fLog << "NaN or Inf. This here is >"<< specific_delay;
168 *fLog << "<... aborting." << endl;
169 return kFALSE;
170 }
171 }
172
173 }
174 // -------------------------------------------------------------------
175/*
176 fPulsePos = (MParameterD*)pList->FindObject("IntendedPulsePos", "MParameterD");
177 if (!fPulsePos)
178 {
179 *fLog << err << "IntendedPulsePos [MParameterD] not found... aborting." << endl;
180 return kFALSE;
181 }
182 */
183 fResidualTimeSpread = (MParameterD*)pList->FindObject("ResidualTimeSpread");
184 if (!fResidualTimeSpread)
185 {
186 *fLog << err << "ResidualTimeSpread [MParameterD] not found... aborting." << endl;
187 return kFALSE;
188 }
189
190 // Get GapdTimeJitter from parameter list
191 fGapdTimeJitter = (MParameterD*)pList->FindObject("GapdTimeJitter");
192 if (!fGapdTimeJitter)
193 {
194 *fLog << err << "GapdTimeJitter [MParameterD] not found... aborting." << endl;
195 return kFALSE;
196 }
197
198 // Create it here to make sure that MGeomApply will set the correct size
199 fElectronicNoise = (MPedestalCam*)pList->FindCreateObj("MPedestalCam", "ElectronicNoise");
200 if (!fElectronicNoise)
201 return kFALSE;
202
203 fGain = (MPedestalCam*)pList->FindCreateObj("MPedestalCam", "Gain");
204 if (!fGain)
205 return kFALSE;
206
207 fAccidentalPhotons = (MPedestalCam*)pList->FindObject("AccidentalPhotonRates","MPedestalCam");
208 if(!fAccidentalPhotons)
209 {
210 *fLog << err << "AccidentalPhotonRates [MPedestalCam] not found... aborting." << endl;
211 return kFALSE;
212 }
213
214 fCrosstalkCoeffParam = (MParameterD*)pList->FindCreateObj("MParameterD","CrosstalkCoeffParam");
215 if (!fCrosstalkCoeffParam)
216 return kFALSE;
217
218 fTruePhotons = (MTruePhotonsPerPixelCont*)pList->FindCreateObj("MTruePhotonsPerPixelCont");
219 if (!fTruePhotons)
220 return kFALSE;
221
222 MParSpline *pulse = (MParSpline*)pList->FindObject("PulseShape", "MParSpline");
223 if (!pulse)
224 {
225 *fLog << err << "PulseShape [MParSpline] not found... aborting." << endl;
226 return kFALSE;
227 }
228
229// if (fRunHeader->GetFreqSampling()!=1000)
230// {
231// *fLog << err << "ERROR - Sampling frequencies others than 1GHz are not yet supported." << endl;
232// *fLog << warn << "FIXME - SCALE MPulsShape WITH THE SAMPLING FREQUENCY." << endl;
233// return kFALSE;
234// }
235
236 fSpline = pulse->GetSpline();
237 if (!fSpline)
238 {
239 *fLog << err << "No spline initialized." << endl;
240 return kFALSE;
241 }
242
243 // ---------------- Information output ----------------------
244
245 if (fBaselineGain)
246 *fLog << inf << "Gain is also applied to the electronic noise." << endl;
247
248 // To get the area of one Pulse, I only need to calculate the Integral
249 // of the Pulse Shape, which is stored in fSpline. Because the spline is
250 // normalized to a maximal amplitude of 1.0, I had to multiply it with
251 // the Default gain [ADC-Counts * s]
252 fAreaOfOnePulse = fSpline->Integral() * fDefaultGain;
253
254 return kTRUE;
255}
256
257// --------------------------------------------------------------------------
258//
259// FIXME: For now this is a workaround to set a baseline and the
260// electronic (guassian noise)
261//
262Bool_t MSimCamera::ReInit(MParList *plist)
263{
264 for (int i=0; i<fElectronicNoise->GetSize(); i++)
265 {
266 MPedestalPix &ped = (*fElectronicNoise)[i];
267 ped.SetPedestal(fDefaultOffset);
268 if (fDefaultNoise>0)
269 ped.SetPedestalRms(fDefaultNoise);
270
271 ped.SetPedestalABoffset(0);
272 ped.SetNumEvents(0);
273
274
275 MPedestalPix &gain = (*fGain)[i];
276 if (fDefaultGain>0)
277 gain.SetPedestal(fDefaultGain);
278
279 gain.SetPedestalRms(0);
280 gain.SetPedestalABoffset(0);
281 gain.SetNumEvents(0);
282 }
283
284 return kTRUE;
285}
286
287// --------------------------------------------------------------------------
288//
289// fStat->GetMaxIndex must return the maximum index possible
290// (equiv. number of pixels) not just the maximum index stored!
291//
292Int_t MSimCamera::Process()
293{
294 // Calculate start time, end time and corresponding number of samples
295 const Double_t freq = fRunHeader->GetFreqSampling()/1000.;
296
297 // FIXME: Should we use a higher sampling here?
298
299 // Start end end of sampling window (in ns, converted to samples)
300 // The sampling window starts at start+pulsewidth to have a potential
301 // previous pulse in the widdow. The trigger should then be at
302 // start+pulsewidth+triggerpos
303 const Double_t start = fStat->GetTimeFirst()*freq;
304 const Double_t end = fStat->GetTimeLast() *freq;
305
306 // Length of the simulated window (in units of samples)
307 // nlen = (last-first)+roi+pulsewidth
308 const UInt_t nlen = TMath::CeilNint(end-start);
309
310 // Get number of pixels/channels
311 const UInt_t npix = fStat->GetMaxIndex()+1;
312
313 if (npix>(UInt_t)fElectronicNoise->GetSize())
314 {
315 *fLog << err << "ERROR - More indices (" << npix << ") ";
316 *fLog << "assigned than existing in camera (";
317 *fLog << fElectronicNoise->GetSize() << ")!" << endl;
318 return kERROR;
319 }
320
321 const Double_t pw = fSpline->GetXmax()-fSpline->GetXmin(); // [samples] Width of pulse
322
323 // Init the arrays and set the range which will contain valid data
324 // FIXME: There must be some contingency for all the time offsets...
325 // otherwise, we might loose late triggers!!!!!
326 fCamera->Init(npix, nlen);
327 fCamera->SetValidRange(TMath::FloorNint(pw), nlen);
328
329 Double_t timeoffset[npix];
330
331 // Add electronic noise to empty channels
332 for (UInt_t i=0; i<npix; i++)
333 {
334
335 // Get the ResidualTimeSpread Parameter
336 const Double_t residualTimeSpread = fResidualTimeSpread->GetVal();
337
338 // Jens Buss on residual time spread:
339 // randomly draw an additional time offset to be added to the arrivaltime
340 // from a gaussian normal distribution with a given standard deviation
341 timeoffset[i] = gRandom->Gaus(0.0, residualTimeSpread);
342 const MPedestalPix &pix = (*fElectronicNoise)[i];
343
344 const Double_t val = pix.GetPedestal();
345 const Double_t rms = pix.GetPedestalRms();
346
347 // FTemme: Implementation of AC-coupling:
348 // to calculate the value of the accoupling per slice I use the
349 // following equation:
350 // accouplingPerSlice = accidentalPhotonRate * (1 + crossTalkProb)
351 // * areaOfOnePulse / samplingRate;
352 // Therefore I need the following variables
353 // Double_t accidentalPhotonRate; // [MHz]
354 // Float_t crossTalkProb; // [1]
355 // Double_t areaOfOnePulse; // [ADC-Counts * s]
356 // Double_t samplingRate; // [slices * MHz]
357
358 // The accidental photon rate is stored in GHz, so we have to multiply
359 // with 1E3 to get MHz:
360 const MPedestalPix &accPhoPix = (*fAccidentalPhotons)[i];
361
362 const Double_t accidentalPhotonRate = accPhoPix.GetPedestal() * 1e3; //[MHz]
363
364 Double_t currentAccidentalPhotonRate = accidentalPhotonRate;
365 if (fACTimeConstant!=0)
366 {
367 const Double_t accidentalPhotons = fACTimeConstant * accidentalPhotonRate;
368 const Double_t sigmaAccidentalPhotons = TMath::Sqrt(accidentalPhotons);
369
370 const Double_t gaus = gRandom->Gaus(accidentalPhotons,sigmaAccidentalPhotons);
371
372 currentAccidentalPhotonRate = gaus / fACTimeConstant;
373 }
374
375 // Get the CrosstalkCoefficient Parameter
376 const Double_t crossTalkProb = fCrosstalkCoeffParam->GetVal();
377
378 // The sampling rate I get from the RunHeader:
379 const Double_t samplingRate = fRunHeader->GetFreqSampling(); // [slices * MHz]
380
381 const Double_t accouplingPerSlice = currentAccidentalPhotonRate
382 * (1 + crossTalkProb + fACFudgeFactor)
383 * fAreaOfOnePulse / samplingRate;
384
385 // The accoupling is substracted from the timeline by decreasing the
386 // mean of the gaussian noise which is added
387
388 if (!fBaselineGain)
389 {
390 (*fCamera)[i].AddGaussianNoise(rms, val - accouplingPerSlice);
391 continue;
392 }
393 // Sorry, the name "pedestal" is misleading here
394 // FIXME: Simulate gain fluctuations
395 const Double_t gain = (*fGain)[i].GetPedestal();
396
397 // FIXME: We might add the base line here already.
398 // FIXME: How stable is the offset?
399 // FIXME: Should we write a container AppliedGain for MSImTrigger?
400
401 (*fCamera)[i].AddGaussianNoise(rms*gain, (val - accouplingPerSlice)*gain);
402 }
403
404 // FIXME: Simulate correlations with neighboring pixels
405
406 const Int_t num = fEvt->GetNumPhotons();
407
408 // A random shift, uniformely distributed within one slice, to make sure that
409 // the first photon is not always aligned identically with a sample edge.
410 // FIXME: Make it switchable
411 const Float_t rndm = gRandom->Uniform();
412
413 // FIXME: Shell we add a random shift of [0,1] samples per channel?
414 // Or maybe per channel and run?
415
416 Double_t tot = 0;
417
418 // Sanity check for bad programming style
419 if (npix==1440)
420 {
421 for (int i=0 ; i<1440 ; i++)
422 {
423 (*fTruePhotons->cherenkov_photons_weight)[i] = 0;
424 (*fTruePhotons->cherenkov_photons_number)[i] = 0;
425 (*fTruePhotons->cherenkov_arrival_time_mean)[i] = 0;
426 (*fTruePhotons->cherenkov_arrival_time_variance)[i] = 0;
427 (*fTruePhotons->muon_cherenkov_photons_weight)[i] = 0;
428 (*fTruePhotons->muon_cherenkov_photons_number)[i] = 0;
429 (*fTruePhotons->cherenkov_arrival_time_min)[i] = 10000;
430 (*fTruePhotons->cherenkov_arrival_time_max)[i] = 0;
431 (*fTruePhotons->noise_photons_weight)[i] = 0;
432 }
433 }
434
435 //--------------------------------------------------------------------------
436
437 // Get the ResidualTimeSpread Parameter
438 const Double_t gapdTimeJitter = fGapdTimeJitter->GetVal();
439
440 // Simulate pulses
441 for (Int_t i=0; i<num; i++)
442 {
443 const MPhotonData &ph = (*fEvt)[i];
444
445 const UInt_t idx = ph.GetTag();
446 Double_t t = (ph.GetTime()-fStat->GetTimeFirst())*freq+rndm;// - fSpline->GetXmin();
447
448 // Sebastian Mueller and Dominik Neise on fix time offsets:
449 // We add a fix temporal offset to the relative arrival time of the
450 // individual pixel. The offsets are stored in the
451 // fFixTimeOffsetsBetweenPixelsInNs -> fM matrix. We identify the first
452 // column to hold the offsets in ns.
453 t += freq*fFixTimeOffsetsBetweenPixelsInNs->fM[idx][0];
454
455 // Jens Buss on residual time spread:
456 // add random time offset to the arrivaltimes
457 t += timeoffset[idx];
458
459 // FIXME: Time jitter?
460 // Jens Buss on GapdTimeJitter
461 // add also a time offset to arrival times of single photons
462 // TODO: change to ns, use: fRunHeader->GetFreqSampling()
463 t += gRandom->Gaus(0.0, gapdTimeJitter);
464
465 // FIXME: Add additional routing here?
466 // FIMXE: How stable is the gain?
467
468 if (ph.GetPrimary()!=MMcEvt::kNightSky && ph.GetPrimary()!=MMcEvt::kArtificial)
469 {
470 tot += ph.GetWeight();
471
472 // Sanity check for bad programming style
473 if (npix==1440)
474 {
475 (*fTruePhotons->cherenkov_photons_weight)[idx] += ph.GetWeight();
476 (*fTruePhotons->cherenkov_photons_number)[idx] += 1;
477
478 (*fTruePhotons->cherenkov_arrival_time_mean)[idx] += t;
479 (*fTruePhotons->cherenkov_arrival_time_variance)[idx] += t*t;
480
481 if (ph.GetPrimary()==MMcEvt::kMUON)
482 {
483 (*fTruePhotons->muon_cherenkov_photons_weight)[idx] += ph.GetWeight();
484 (*fTruePhotons->muon_cherenkov_photons_number)[idx] += 1;
485 }
486
487 // find min
488 if (t < (*fTruePhotons->cherenkov_arrival_time_min)[idx] )
489 {
490 (*fTruePhotons->cherenkov_arrival_time_min)[idx] = t;
491 }
492 // find max
493 if (t > (*fTruePhotons->cherenkov_arrival_time_max)[idx] )
494 {
495 (*fTruePhotons->cherenkov_arrival_time_max)[idx] = t;
496 }
497 }
498 }
499 else
500 {
501 // Sanity check for bad programming style
502 if (npix==1440)
503 {
504 (*fTruePhotons->noise_photons_weight)[idx] += ph.GetWeight();
505 }
506 }
507
508 // Sorry, the name "pedestal" is misleading here
509 // FIXME: Simulate gain fluctuations
510 const Double_t gain = (*fGain)[idx].GetPedestal();
511
512 // The "-GetXmin" ensures that a photon at t=0 does not get sampled
513 // before the sampling window!
514 // === FIXME === FIXME === FIXME === Frequency!!!!
515 (*fCamera)[idx].AddPulse(*fSpline, t - fSpline->GetXmin(), ph.GetWeight()*gain);
516 }
517
518 // Sanity check for bad programming style
519 if (npix==1440)
520 {
521 for (unsigned int i=0 ; i < 1440 ; i++)
522 {
523 float number = (*fTruePhotons->cherenkov_photons_number)[i];
524 (*fTruePhotons->cherenkov_arrival_time_mean)[i] /= number;
525 float mean = (*fTruePhotons->cherenkov_arrival_time_mean)[i];
526 float sum_tt = (*fTruePhotons->cherenkov_arrival_time_variance)[i];
527 (*fTruePhotons->cherenkov_arrival_time_variance)[i] = (sum_tt / number - mean*mean) /(number - 1);
528 }
529 }
530
531 fMcEvt->SetPhotElfromShower(TMath::Nint(tot));
532
533 return kTRUE;
534}
535
536// --------------------------------------------------------------------------
537//
538// BaselineGain: Off
539//
540Int_t MSimCamera::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
541{
542 Bool_t rc = kFALSE;
543 if (IsEnvDefined(env, prefix, "BaselineGain", print))
544 {
545 rc = kTRUE;
546 fBaselineGain = GetEnvValue(env, prefix, "BaselineGain", fBaselineGain);
547 }
548
549 if (IsEnvDefined(env, prefix, "DefaultOffset", print))
550 {
551 rc = kTRUE;
552 fDefaultOffset = GetEnvValue(env, prefix, "DefaultOffset", fDefaultOffset);
553 }
554 if (IsEnvDefined(env, prefix, "DefaultNoise", print))
555 {
556 rc = kTRUE;
557 fDefaultNoise = GetEnvValue(env, prefix, "DefaultNoise", fDefaultNoise);
558 }
559 if (IsEnvDefined(env, prefix, "DefaultGain", print))
560 {
561 rc = kTRUE;
562 fDefaultGain = GetEnvValue(env, prefix, "DefaultGain", fDefaultGain);
563 }
564 if (IsEnvDefined(env, prefix, "ACFudgeFactor", print))
565 {
566 rc = kTRUE;
567 fACFudgeFactor = GetEnvValue(env, prefix, "ACFudgeFactor", fACFudgeFactor);
568 }
569 if (IsEnvDefined(env, prefix, "ACTimeConstant", print))
570 {
571 rc = kTRUE;
572 fACTimeConstant = GetEnvValue(env, prefix, "ACTimeConstant", fACTimeConstant);
573 }
574
575 return rc;
576}
Note: See TracBrowser for help on using the repository browser.