]>
Commit | Line | Data |
---|---|---|
c29371c1 | 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 IcePandel | |
20 | // TTask derived class to perform Pandel fitting. | |
21 | // | |
22 | // The code in this processor is based on the algorithms as developed | |
23 | // by Oladipo Fadiran and George Japaridze (Clark Atlanta University, USA). | |
24 | // | |
25 | // Use the UseTracks memberfunction to specify the first guess tracks | |
26 | // to be processed by the minimiser. | |
27 | // By default only the first encountered IceDwalk track will be processed. | |
28 | // | |
29 | // Use the SelectHits memberfunction to specify the hits to be used. | |
30 | // By default only the hits associated to the first guess track are used. | |
31 | // | |
32 | // The fit processor (TFitter which is basically Minuit) printlevel | |
33 | // can be selected via the memberfunction SetPrintLevel. | |
34 | // By default all printout is suppressed (i.e. level=-2). | |
35 | // | |
36 | // An example of how to invoke this processor after Xtalk, hit cleaning | |
37 | // and a direct walk first guess estimate can be found in the ROOT example | |
38 | // macro icepandel.cc which resides in the /macros subdirectory. | |
39 | // | |
40 | // The minimisation results are stored in the IceEvent structure as | |
d6860cf1 | 41 | // tracks with as default the name "IcePandel" (just like the first guess |
42 | // results of e.g. IceDwalk). | |
43 | // This track name identifier can be modified by the user via the | |
44 | // SetTrackName() memberfunction. This will allow unique identification | |
45 | // of tracks which are produced when re-processing existing data with | |
46 | // different criteria. | |
c29371c1 | 47 | // A pointer to the first guess track which was used as input is available |
48 | // via the GetParentTrack facility of these "IcePandel" tracks. | |
49 | // Furthermore, all the hits that were used in the minisation are available | |
50 | // via the GetSignal facility of a certain track. | |
51 | // The statistics of the TFitter result are stored as an AliSignal object | |
52 | // in the track, which can be obtained via the GetFitDetails memberfunction. | |
53 | // Currently no overall probability has yet been defined to indicate the | |
54 | // quality of a certain fit result. So, for the time being the user has | |
55 | // to judge the fit quality his/herself by means of the various TFitter stats | |
56 | // as accessible via the GetFitDetails facility. | |
57 | // | |
58 | // An example of how the various data can be accessed is given below, | |
59 | // where "evt" indicates the pointer to the IceEvent structure. | |
60 | // | |
61 | // Example for accessing data : | |
62 | // ---------------------------- | |
63 | // TObjArray* tracks=evt->GetTracks("IcePandel"); | |
64 | // if (!tracks) return; | |
65 | // AliPosition* r0=0; | |
66 | // Float_t fcn=0; | |
67 | // for (Int_t jtk=0; jtk<tracks->GetEntries(); jtk++) | |
68 | // { | |
69 | // AliTrack* tx=(AliTrack*)tracks->At(jtk); | |
70 | // if (!tx) continue; | |
71 | // tx->Data("sph"); | |
72 | // r0=tx->GetReferencePoint(); | |
73 | // if (r0) r0->Data(); | |
74 | // sx=(AliSignal*)tx->GetFitDetails(); | |
75 | // if (sx) fcn=sx->GetSignal("FCN"); | |
76 | // AliTrack* tx2=tx->GetParentTrack(); | |
77 | // if (!tx2) continue; | |
78 | // tx2->Data("sph"); | |
79 | // r0=tx2->GetReferencePoint(); | |
80 | // if (r0) r0->Data(); | |
81 | // } | |
82 | // | |
83 | // Notes : | |
84 | // ------- | |
85 | // 1) This processor only works properly on data which are Time and ADC | |
86 | // calibrated and contain tracks from first guess algorithms like | |
87 | // e.g. IceDwalk. | |
88 | // 2) In view of the usage of TFitter/Minuit minimisation, a global pointer | |
89 | // to the instance of this class (gIcePandel) and a global static | |
90 | // wrapper function (IcePandelFCN) have been introduced, to allow the | |
91 | // actual minimisation to be performed via the memberfunction FitFCN. | |
92 | // This implies that in a certain processing job only 1 instance of | |
93 | // this IcePandel class may occur. | |
94 | // | |
95 | //--- Author: Nick van Eijndhoven 09-feb-2006 Utrecht University | |
96 | //- Modified: NvE $Date$ Utrecht University | |
97 | /////////////////////////////////////////////////////////////////////////// | |
98 | ||
99 | #include "IcePandel.h" | |
100 | #include "Riostream.h" | |
101 | ||
102 | // Global pointer to the instance of this object | |
103 | IcePandel* gIcePandel=0; | |
104 | ||
105 | // TFitter/Minuit interface to IcePandel::FitFCN | |
106 | void IcePandelFCN(Int_t& npar,Double_t* gin,Double_t& f,Double_t* u,Int_t flag) | |
107 | { | |
108 | if (gIcePandel) gIcePandel->FitFCN(npar,gin,f,u,flag); | |
109 | } | |
110 | ||
111 | ClassImp(IcePandel) // Class implementation to enable ROOT I/O | |
112 | ||
113 | IcePandel::IcePandel(const char* name,const char* title) : TTask(name,title) | |
114 | { | |
115 | // Default constructor. | |
116 | fFirst=1; | |
117 | fPrint=-2; | |
118 | fSelhits=1; | |
119 | fEvt=0; | |
120 | fUseNames=0; | |
121 | fUseNtk=0; | |
122 | fTrack=0; | |
123 | fHits=0; | |
124 | fFitter=0; | |
d6860cf1 | 125 | fTrackname="IcePandel"; |
c29371c1 | 126 | |
127 | // Set the global pointer to this instance | |
128 | gIcePandel=this; | |
129 | } | |
130 | /////////////////////////////////////////////////////////////////////////// | |
131 | IcePandel::~IcePandel() | |
132 | { | |
133 | // Default destructor. | |
134 | if (fUseNames) | |
135 | { | |
136 | delete fUseNames; | |
137 | fUseNames=0; | |
138 | } | |
139 | if (fUseNtk) | |
140 | { | |
141 | delete fUseNtk; | |
142 | fUseNtk=0; | |
143 | } | |
144 | if (fHits) | |
145 | { | |
146 | delete fHits; | |
147 | fHits=0; | |
148 | } | |
149 | if (fFitter) | |
150 | { | |
151 | delete fFitter; | |
152 | fFitter=0; | |
153 | } | |
154 | } | |
155 | /////////////////////////////////////////////////////////////////////////// | |
156 | void IcePandel::Exec(Option_t* opt) | |
157 | { | |
158 | // Implementation of the hit fitting procedure. | |
159 | ||
160 | TString name=opt; | |
161 | AliJob* parent=(AliJob*)(gROOT->GetListOfTasks()->FindObject(name.Data())); | |
162 | ||
163 | if (!parent) return; | |
164 | ||
165 | fEvt=(IceEvent*)parent->GetObject("IceEvent"); | |
166 | if (!fEvt) return; | |
167 | ||
168 | if (!fUseNames) UseTracks("IceDwalk",1); | |
169 | ||
170 | Int_t nclasses=fUseNames->GetEntries(); // Number of first guess classes to be processed | |
171 | Int_t ntkmax=0; // Max. number of tracks for a certain class | |
172 | TObjString* strx=0; | |
173 | TString str; | |
174 | ||
175 | if (fFirst) | |
176 | { | |
177 | cout << " *IcePandel* First guess selections to be processed (-1=all)." << endl; | |
178 | for (Int_t i=0; i<nclasses; i++) | |
179 | { | |
180 | strx=(TObjString*)fUseNames->At(i); | |
181 | if (!strx) continue; | |
182 | str=strx->GetString(); | |
183 | ntkmax=fUseNtk->At(i); | |
184 | cout << " Maximally " << ntkmax << " track(s) per event for procedure : " << str.Data() << endl; | |
185 | } | |
186 | cout << " *IcePandel* Hit selection mode : " << fSelhits << endl; | |
187 | cout << endl; | |
188 | fFirst=0; | |
189 | } | |
190 | ||
191 | Double_t pi=acos(-1.); | |
192 | ||
193 | // Initialisation of the minimisation processor | |
194 | Double_t arglist[100]; | |
195 | if (!fFitter) fFitter=new TFitter(); | |
196 | ||
197 | // The number of reconstructed tracks already present in the event | |
198 | Int_t ntkreco=fEvt->GetNtracks(1); | |
199 | ||
200 | if (!fHits) | |
201 | { | |
202 | fHits=new TObjArray(); | |
203 | } | |
204 | else | |
205 | { | |
206 | fHits->Clear(); | |
207 | } | |
208 | ||
209 | // If selected, use all the good quality hits of the complete event | |
210 | if (fSelhits==0) | |
211 | { | |
212 | TObjArray* hits=fEvt->GetHits("IceGOM"); | |
213 | for (Int_t ih=0; ih<hits->GetEntries(); ih++) | |
214 | { | |
215 | AliSignal* sx=(AliSignal*)hits->At(ih); | |
216 | if (!sx) continue; | |
217 | if (sx->GetDeadValue("ADC") || sx->GetDeadValue("LE") || sx->GetDeadValue("TOT")) continue; | |
218 | fHits->Add(sx); | |
219 | } | |
220 | } | |
221 | ||
222 | // Track by track processing of the selected first guess classes | |
223 | Float_t vec[3]; | |
224 | Float_t err[3]; | |
225 | Float_t margin,minmarg=25; // (minimal) margin for the fitter r0 search area | |
226 | Ali3Vector p; | |
227 | AliPosition pos; | |
228 | AliTrack tkfit; | |
d6860cf1 | 229 | tkfit.SetNameTitle(fTrackname.Data(),"IcePandel fit result"); |
c29371c1 | 230 | AliSignal fitstats; |
231 | fitstats.SetNameTitle("Fitstats","TFitter stats for Pandel fit"); | |
232 | fitstats.SetSlotName("IER",1); | |
233 | fitstats.SetSlotName("FCN",2); | |
234 | fitstats.SetSlotName("EDM",3); | |
235 | fitstats.SetSlotName("NVARS",4); | |
d6860cf1 | 236 | Float_t x,y,z,theta,phi; |
237 | Float_t xmin,xmax,ymin,ymax,zmin,zmax,thetamin,thetamax,phimin,phimax; | |
c29371c1 | 238 | Double_t amin,edm,errdef; // Minimisation stats |
239 | Int_t ier,nvpar,nparx; // Minimisation stats | |
240 | Int_t ntk=0; | |
241 | Int_t nsig=0; | |
242 | for (Int_t iclass=0; iclass<nclasses; iclass++) // Loop over first guess classes | |
243 | { | |
244 | strx=(TObjString*)fUseNames->At(iclass); | |
245 | if (!strx) continue; | |
246 | str=strx->GetString(); | |
247 | ntkmax=fUseNtk->At(iclass); | |
248 | TObjArray* tracks=fEvt->GetTracks(str); | |
249 | ntk=tracks->GetEntries(); | |
250 | if (ntkmax>0 && ntk>ntkmax) ntk=ntkmax; | |
251 | ||
252 | for (Int_t jtk=0; jtk<ntk; jtk++) // Loop over tracks of a certain class | |
253 | { | |
254 | fTrack=(AliTrack*)tracks->At(jtk); | |
255 | if (!fTrack) continue; | |
256 | ||
257 | AliPosition* r0=fTrack->GetReferencePoint(); | |
258 | if (!r0) continue; | |
259 | ||
260 | // If selected, use only the first guess track associated hits | |
261 | if (fSelhits==1) | |
262 | { | |
263 | fHits->Clear(); | |
264 | nsig=fTrack->GetNsignals(); | |
265 | for (Int_t is=1; is<=nsig; is++) | |
266 | { | |
267 | AliSignal* sx=fTrack->GetSignal(is); | |
268 | if (!sx) continue; | |
269 | if (!sx->GetDevice()->InheritsFrom("IceGOM")) continue; | |
270 | if (sx->GetDeadValue("ADC") || sx->GetDeadValue("LE") || sx->GetDeadValue("TOT")) continue; | |
271 | fHits->Add(sx); | |
272 | } | |
273 | } | |
274 | ||
275 | if (!fHits->GetEntries()) continue; | |
276 | ||
277 | r0->GetVector(vec,"car"); | |
278 | r0->GetErrors(err,"car"); | |
279 | ||
280 | x=vec[0]; | |
281 | y=vec[1]; | |
282 | z=vec[2]; | |
283 | margin=5.*err[0]; | |
284 | if (margin<minmarg) margin=minmarg; | |
285 | xmin=x-margin; | |
286 | xmax=x+margin; | |
287 | margin=5.*err[1]; | |
288 | if (margin<minmarg) margin=minmarg; | |
289 | ymin=y-margin; | |
290 | ymax=y+margin; | |
291 | margin=5.*err[2]; | |
292 | if (margin<minmarg) margin=minmarg; | |
293 | zmin=z-margin; | |
294 | zmax=z+margin; | |
295 | ||
296 | p=fTrack->Get3Momentum(); | |
297 | p.GetVector(vec,"sph"); | |
298 | ||
299 | theta=vec[1]; | |
300 | phi=vec[2]; | |
301 | thetamin=theta-(pi/3.); | |
302 | if (thetamin<0) thetamin=0; | |
303 | thetamax=theta+(pi/3.); | |
304 | if (thetamax>pi) thetamax=pi; | |
305 | phimin=phi-(pi/2.); | |
306 | phimax=phi+(pi/2.); | |
307 | ||
308 | // Process this first guess track with its associated hits | |
309 | fFitter->Clear(); | |
310 | ||
311 | arglist[0]=fPrint; | |
312 | if (fPrint==-2) arglist[0]=-1; | |
313 | fFitter->ExecuteCommand("SET PRINT",arglist,1); | |
314 | if (fPrint==-2) fFitter->ExecuteCommand("SET NOWARNINGS",arglist,0); | |
315 | ||
316 | fFitter->SetFitMethod("loglikelihood"); | |
317 | ||
318 | fFitter->SetParameter(0,"r0x",x,0.001,xmin,xmax); | |
319 | fFitter->SetParameter(1,"r0y",y,0.001,ymin,ymax); | |
320 | fFitter->SetParameter(2,"r0z",z,0.001,zmin,zmax); | |
321 | fFitter->SetParameter(3,"theta",theta,0.001,thetamin,thetamax); | |
322 | fFitter->SetParameter(4,"phi",phi,0.001,phimin,phimax); | |
323 | ||
324 | fFitter->SetFCN(IcePandelFCN); | |
325 | ||
326 | arglist[0]=0; | |
327 | ier=fFitter->ExecuteCommand("MINIMIZE",arglist,0); | |
328 | ||
329 | fFitter->GetStats(amin,edm,errdef,nvpar,nparx); | |
330 | fitstats.Reset(); | |
331 | fitstats.SetSignal(ier,1); | |
332 | fitstats.SetSignal(amin,2); | |
333 | fitstats.SetSignal(edm,3); | |
334 | fitstats.SetSignal(nvpar,4); | |
335 | ||
336 | // Resulting parameters after minimisation | |
337 | vec[0]=fFitter->GetParameter(0); | |
338 | vec[1]=fFitter->GetParameter(1); | |
339 | vec[2]=fFitter->GetParameter(2); | |
340 | err[0]=fFitter->GetParError(0); | |
341 | err[1]=fFitter->GetParError(1); | |
342 | err[2]=fFitter->GetParError(2); | |
343 | pos.SetPosition(vec,"car"); | |
344 | pos.SetPositionErrors(err,"car"); | |
345 | ||
346 | vec[0]=1.; | |
347 | vec[1]=fFitter->GetParameter(3); | |
348 | vec[2]=fFitter->GetParameter(4); | |
349 | err[0]=0.; | |
350 | err[1]=fFitter->GetParError(3); | |
351 | err[2]=fFitter->GetParError(4); | |
352 | p.SetVector(vec,"sph"); | |
353 | p.SetErrors(err,"sph"); | |
354 | ||
355 | // Enter the fit result as a track in the event structure | |
356 | tkfit.Reset(); | |
357 | ntkreco++; | |
358 | tkfit.SetId(ntkreco); | |
359 | tkfit.SetParentTrack(fTrack); | |
360 | AliTimestamp* tt0=r0->GetTimestamp(); | |
361 | pos.SetTimestamp(*tt0); | |
362 | tkfit.SetTimestamp(*tt0); | |
363 | tkfit.SetReferencePoint(pos); | |
364 | tkfit.Set3Momentum(p); | |
365 | tkfit.SetFitDetails(fitstats); | |
366 | for (Int_t ihit=0; ihit<fHits->GetEntries(); ihit++) | |
367 | { | |
368 | AliSignal* sx=(AliSignal*)fHits->At(ihit); | |
369 | if (sx) tkfit.AddSignal(*sx); | |
370 | } | |
371 | fEvt->AddTrack(tkfit); | |
372 | } // End loop over tracks | |
373 | } // End loop over first guess classes | |
374 | ||
375 | } | |
376 | /////////////////////////////////////////////////////////////////////////// | |
377 | void IcePandel::SetPrintLevel(Int_t level) | |
378 | { | |
379 | // Set the fitter (Minuit) print level. | |
380 | // See the TFitter and TMinuit docs for details. | |
381 | // | |
382 | // Note : level=-2 suppresses also all fit processor warnings. | |
383 | // | |
384 | // The default in the constructor is level=-2. | |
385 | ||
386 | fPrint=level; | |
387 | } | |
388 | /////////////////////////////////////////////////////////////////////////// | |
389 | void IcePandel::UseTracks(TString classname,Int_t n) | |
390 | { | |
391 | // Specification of the first guess tracks to be used. | |
392 | // | |
393 | // classname : Specifies the first guess algorithm (e.g. "IceDwalk"); | |
394 | // n : Specifies the max. number of these tracks to be used | |
395 | // | |
396 | // Note : n<0 will use all the existing tracks of the specified classname | |
397 | // | |
398 | // The default is n=-1. | |
399 | // | |
400 | // Consecutive invokations of this memberfunction with different classnames | |
401 | // will result in an incremental effect. | |
402 | // | |
403 | // Example : | |
404 | // --------- | |
405 | // UseTracks("IceDwalk",5); | |
406 | // UseTracks("IceLfit",2); | |
407 | // UseTracks("IceJams"); | |
408 | // | |
409 | // This will use the first 5 IceDwalk, the first 2 IceLfit and all the | |
410 | // IceJams tracks which are encountered in the event structure. | |
411 | ||
412 | if (!fUseNames) | |
413 | { | |
414 | fUseNames=new TObjArray(); | |
415 | fUseNames->SetOwner(); | |
416 | } | |
417 | ||
418 | if (!fUseNtk) fUseNtk=new TArrayI(); | |
419 | ||
420 | // Check if this classname has already been specified before | |
421 | TString s; | |
422 | Int_t nen=fUseNames->GetEntries(); | |
423 | for (Int_t i=0; i<nen; i++) | |
424 | { | |
425 | TObjString* sx=(TObjString*)fUseNames->At(i); | |
426 | if (!sx) continue; | |
427 | s=sx->GetString(); | |
428 | if (s==classname) return; | |
429 | } | |
430 | ||
431 | // New classname to be added into the storage | |
432 | if (nen >= fUseNames->GetSize()) fUseNames->Expand(nen+1); | |
433 | if (nen >= fUseNtk->GetSize()) fUseNtk->Set(nen+1); | |
434 | ||
435 | TObjString* name=new TObjString(); | |
436 | name->SetString(classname); | |
437 | fUseNames->Add(name); | |
438 | fUseNtk->AddAt(n,nen); | |
439 | } | |
440 | /////////////////////////////////////////////////////////////////////////// | |
441 | void IcePandel::SelectHits(Int_t mode) | |
442 | { | |
443 | // Specification of the hits to be used in the minimisation. | |
444 | // | |
445 | // mode = 0 : All hit cleaning survived hits of the complete event are used | |
446 | // 1 : Only the associated hits are used for each first guess track | |
447 | // | |
448 | // The default is mode=1. | |
449 | ||
450 | if (mode==0 || mode==1) fSelhits=mode; | |
451 | } | |
452 | /////////////////////////////////////////////////////////////////////////// | |
d6860cf1 | 453 | void IcePandel::SetTrackName(TString s) |
454 | { | |
455 | // Set (alternative) name identifier for the produced tracks. | |
456 | // This allows unique identification of (newly) produced pandel tracks | |
457 | // in case of re-processing of existing data with different criteria. | |
458 | // By default the produced tracks have the name "IcePandel" which is | |
459 | // set in the constructor of this class. | |
460 | fTrackname=s; | |
461 | } | |
462 | /////////////////////////////////////////////////////////////////////////// | |
c29371c1 | 463 | void IcePandel::FitFCN(Int_t&,Double_t*,Double_t& f,Double_t* x,Int_t) |
464 | { | |
465 | // The Pandel function used for the minimisation process. | |
466 | ||
467 | const Float_t c=0.299792; // Light speed in vacuum in meters per ns | |
468 | const Float_t nice=1.35634; // Refractive index of ice | |
469 | const Float_t thetac=acos(1./nice); // Cherenkov angle (in radians) | |
470 | const Float_t lambda=33.3; // Light scattering length in ice | |
471 | const Float_t labs=98; // Light absorbtion length in ice | |
472 | const Float_t cice=c/nice; // Light speed in ice in meters per ns | |
473 | const Float_t tau=557; | |
474 | const Double_t rho=((1./tau)+(cice/labs)); | |
475 | ||
476 | f=0; | |
477 | ||
478 | // The first original guess track parameters | |
479 | AliPosition* tr0=fTrack->GetReferencePoint(); | |
480 | AliTimestamp* tt0=tr0->GetTimestamp(); | |
481 | Float_t t0=fEvt->GetDifference(tt0,"ns"); | |
482 | Ali3Vector tp=fTrack->Get3Momentum(); | |
483 | ||
484 | // The new r0 and p vectors from the minimisation | |
485 | Float_t vec[3]; | |
486 | ||
487 | AliPosition r0; | |
488 | vec[0]=x[0]; | |
489 | vec[1]=x[1]; | |
490 | vec[2]=x[2]; | |
491 | r0.SetPosition(vec,"car"); | |
492 | ||
493 | Ali3Vector p; | |
494 | vec[0]=1; | |
495 | vec[1]=x[3]; | |
496 | vec[2]=x[4]; | |
497 | p.SetVector(vec,"sph"); | |
498 | ||
499 | Int_t nhits=fHits->GetEntries(); | |
500 | AliPosition rhit; | |
501 | Ali3Vector r12; | |
502 | Float_t d,dist,thit,tgeo; | |
503 | Double_t tres,zeta,pandel; | |
504 | for (Int_t i=0; i<nhits; i++) | |
505 | { | |
506 | AliSignal* sx=(AliSignal*)fHits->At(i); | |
507 | if (!sx) continue; | |
508 | IceGOM* omx=(IceGOM*)sx->GetDevice(); | |
509 | if (!omx) continue; | |
510 | rhit=omx->GetPosition(); | |
511 | d=fTrack->GetDistance(rhit); | |
512 | zeta=d/lambda; | |
513 | d*=sin(thetac); | |
514 | r12=rhit-r0; | |
515 | dist=p.Dot(r12)+d*tan(thetac); | |
516 | tgeo=t0+dist/c; | |
517 | thit=sx->GetSignal("LE",7); | |
518 | tres=thit-tgeo; | |
519 | ||
520 | // The Pandel function evaluation | |
521 | // Avoid minimiser problem for infinite derivative on Pandel surface | |
522 | // This problem will be absent when using a smooth convoluted Pandel | |
523 | if (tres>0 && zeta>=1) | |
524 | { | |
525 | pandel=pow(rho,zeta)*pow(tres,(zeta-1.))*exp(-rho*tres)/TMath::Gamma(zeta); | |
526 | } | |
527 | else | |
528 | { | |
529 | pandel=1.e-6; | |
530 | } | |
531 | ||
532 | // Use 10*log10 expression to obtain intuitive decibel scale | |
533 | f-=10.*log10(pandel); | |
534 | } | |
535 | } | |
536 | /////////////////////////////////////////////////////////////////////////// |