source: trunk/MagicSoft/Mars/mcalib/MCalibrationCalc.cc@ 3181

Last change on this file since 3181 was 3180, checked in by gaug, 22 years ago
*** empty log message ***
File size: 21.8 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC 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 appear 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): Markus Gaug 09/2003 <mailto:markus@ifae.es>
19!
20! Copyright: MAGIC Software Development, 2000-2001
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MCalibrationCalc
28//
29// Task to calculate the calibration conversion factors from the FADC
30// time slices. The integrated time slices have to be delivered by an
31// MExtractedSignalCam. The pedestals by an MPedestalCam.
32//
33// The output container MCalibrationCam holds one entry of type MCalibrationPix
34// for every pixel. It is filled in the following way:
35//
36// ProProcess: Search for MPedestalCam, MExtractedSignalCam
37// Initialize MCalibrationCam
38// Initialize pulser light wavelength
39//
40// ReInit: MCalibrationCam::InitSize(NumPixels) is called which allocates
41// memory in a TClonesArray of type MCalibrationPix
42// Initialize number of used FADC slices
43// Optionally exclude pixels from calibration
44//
45// Process: Every MCalibrationPix holds a histogram class,
46// MHCalibrationPixel which itself hold histograms of type:
47// HCharge(npix) (distribution of summed FADC time slice
48// entries)
49// HTime(npix) (distribution of position of maximum)
50// HChargevsN(npix) (distribution of charges vs. event number.
51//
52// PostProcess: All histograms HCharge(npix) are fitted to a Gaussian
53// All histograms HTime(npix) are fitted to a Gaussian
54// The histogram HBlindPixelCharge (blind pixel) is fitted to
55// a single PhE fit
56//
57// The histograms of the PIN Diode are fitted to Gaussians
58//
59// Fits can be excluded via the commands:
60// MalibrationCam::SkipBlindPixelFits() (skip all blind
61// pixel fits)
62//
63// Hi-Gain vs. Lo-Gain Calibration (very memory-intensive)
64// can be skipped with the command:
65// MalibrationCam::SkipHiLoGainCalibration()
66//
67// Input Containers:
68// MRawEvtData
69// MPedestalCam
70// MExtractedSignalCam
71//
72// Output Containers:
73// MCalibrationCam
74//
75//////////////////////////////////////////////////////////////////////////////
76#include "MCalibrationCalc.h"
77
78// FXIME: Usage of fstream is a preliminary workaround!
79#include <fstream>
80
81// FXIME: This has to be removed!!!! (YES, WHEN WE HAVE ACCESS TO THE DATABASE!!!!!)
82#include "MCalibrationConfig.h"
83
84#include <TSystem.h>
85#include <TH1.h>
86
87#include "MLog.h"
88#include "MLogManip.h"
89
90#include "MParList.h"
91
92#include "MGeomCam.h"
93#include "MRawRunHeader.h"
94#include "MRawEvtPixelIter.h"
95
96#include "MPedestalCam.h"
97#include "MPedestalPix.h"
98
99#include "MCalibrationCam.h"
100#include "MCalibrationPix.h"
101
102#include "MExtractedSignalCam.h"
103#include "MExtractedSignalPix.h"
104
105#include "MCalibrationBlindPix.h"
106
107ClassImp(MCalibrationCalc);
108
109using namespace std;
110
111const UInt_t MCalibrationCalc::fgBlindPixelId = 559;
112const UInt_t MCalibrationCalc::fgPINDiodeId = 9999;
113const Byte_t MCalibrationCalc::fgSaturationLimit = 254;
114const Int_t MCalibrationCalc::fgBlindPixelFirst = 3;
115const Int_t MCalibrationCalc::fgBlindPixelLast = 14;
116const Int_t MCalibrationCalc::fgBlindPixelSinglePheCut = 400;
117
118// --------------------------------------------------------------------------
119//
120// Default constructor.
121//
122MCalibrationCalc::MCalibrationCalc(const char *name, const char *title)
123 : fPedestals(NULL), fCalibrations(NULL), fSignals(NULL),
124 fRawEvt(NULL), fRunHeader(NULL), fEvtTime(NULL)
125{
126
127 fName = name ? name : "MCalibrationCalc";
128 fTitle = title ? title : "Task to calculate the calibration constants and MCalibrationCam ";
129
130 AddToBranchList("MRawEvtData.fHiGainPixId");
131 AddToBranchList("MRawEvtData.fLoGainPixId");
132 AddToBranchList("MRawEvtData.fHiGainFadcSamples");
133 AddToBranchList("MRawEvtData.fLoGainFadcSamples");
134
135 Clear();
136 SetBlindPixelRange();
137}
138
139void MCalibrationCalc::Clear(const Option_t *o)
140{
141
142 SETBIT(fFlags, kUseBlindPixelFit);
143 SETBIT(fFlags, kUseQualityChecks);
144 SETBIT(fFlags, kHiLoGainCalibration);
145
146 CLRBIT(fFlags, kHiGainOverFlow);
147 CLRBIT(fFlags, kLoGainOverFlow);
148
149 fBlindPixelFirst = 0;
150 fBlindPixelLast = 0;
151
152 fNumBlindPixelSinglePhe = 0;
153 fNumBlindPixelPedestal = 0;
154
155 fNumHiGainSamples = 0;
156 fNumLoGainSamples = 0;
157 fConversionHiLo = 0;
158 fNumExcludedPixels = 0;
159
160 fColor = kECT1;
161}
162
163void MCalibrationCalc::SetBlindPixelRange(Int_t first, Int_t last)
164{
165
166 fBlindPixelFirst = first;
167 fBlindPixelLast = last;
168
169}
170
171
172MCalibrationBlindPix *MCalibrationCalc::GetBlindPixel() const
173{
174 return fCalibrations->GetBlindPixel();
175}
176
177// --------------------------------------------------------------------------
178//
179// The PreProcess searches for the following input containers:
180// - MRawEvtData
181// - MPedestalCam
182//
183// The following output containers are also searched and created if
184// they were not found:
185//
186// - MHCalibrationBlindPixel
187// - MCalibrationCam
188//
189// The following output containers are only searched, but not created
190//
191// - MTime
192//
193Int_t MCalibrationCalc::PreProcess(MParList *pList)
194{
195
196 fRawEvt = (MRawEvtData*)pList->FindObject("MRawEvtData");
197 if (!fRawEvt)
198 {
199 *fLog << err << dbginf << "MRawEvtData not found... aborting." << endl;
200 return kFALSE;
201 }
202
203 const MRawRunHeader *runheader = (MRawRunHeader*)pList->FindObject("MRawRunHeader");
204 if (!runheader)
205 *fLog << warn << dbginf << "Warning - cannot check file type, MRawRunHeader not found." << endl;
206 else
207 if (runheader->GetRunType() == kRTMonteCarlo)
208 {
209 return kTRUE;
210 }
211
212 fCalibrations = (MCalibrationCam*)pList->FindCreateObj("MCalibrationCam");
213 if (!fCalibrations)
214 {
215 *fLog << err << dbginf << "MCalibrationCam could not be created ... aborting." << endl;
216 return kFALSE;
217 }
218
219 fEvtTime = (MTime*)pList->FindObject("MTime");
220
221 switch (fColor)
222 {
223 case kEBlue:
224 fCalibrations->SetColor(MCalibrationCam::kECBlue);
225 break;
226 case kEGreen:
227 fCalibrations->SetColor(MCalibrationCam::kECGreen);
228 break;
229 case kEUV:
230 fCalibrations->SetColor(MCalibrationCam::kECUV);
231 break;
232 case kECT1:
233 fCalibrations->SetColor(MCalibrationCam::kECCT1);
234 break;
235 default:
236 fCalibrations->SetColor(MCalibrationCam::kECCT1);
237 }
238
239 fPedestals = (MPedestalCam*)pList->FindObject("MPedestalCam");
240 if (!fPedestals)
241 {
242 *fLog << err << dbginf << "Cannot find MPedestalCam ... aborting" << endl;
243 return kFALSE;
244 }
245
246
247 fSignals = (MExtractedSignalCam*)pList->FindObject("MExtractedSignalCam");
248 if (!fSignals)
249 {
250 *fLog << err << dbginf << "Cannot find MExtractedSignalCam ... aborting" << endl;
251 return kFALSE;
252 }
253
254 return kTRUE;
255}
256
257
258// --------------------------------------------------------------------------
259//
260// The ReInit searches for the following input containers:
261// - MRawRunHeader
262//
263Bool_t MCalibrationCalc::ReInit(MParList *pList )
264{
265
266 fRunHeader = (MRawRunHeader*)pList->FindObject("MRawRunHeader");
267 if (!fRunHeader)
268 {
269 *fLog << err << dbginf << ": MRawRunHeader not found... aborting." << endl;
270 return kFALSE;
271 }
272
273
274 MGeomCam *cam = (MGeomCam*)pList->FindObject("MGeomCam");
275 if (!cam)
276 {
277 *fLog << err << GetDescriptor() << ": No MGeomCam found... aborting." << endl;
278 return kFALSE;
279 }
280
281
282 fCalibrations->SetGeomCam(cam);
283
284 fNumHiGainSamples = fSignals->GetNumUsedHiGainFADCSlices();
285 fNumLoGainSamples = fSignals->GetNumUsedLoGainFADCSlices();
286 fSqrtHiGainSamples = TMath::Sqrt((Float_t)fNumHiGainSamples);
287
288 UInt_t npixels = cam->GetNumPixels();
289
290 for (UInt_t i=0; i<npixels; i++)
291 {
292
293 MCalibrationPix &pix = (*fCalibrations)[i];
294 pix.DefinePixId(i);
295
296 pix.SetAbsTimeBordersHiGain(fSignals->GetFirstUsedSliceHiGain(),
297 fSignals->GetLastUsedSliceHiGain());
298 pix.SetAbsTimeBordersLoGain(fSignals->GetFirstUsedSliceLoGain(),
299 fSignals->GetLastUsedSliceLoGain());
300
301 if (!TESTBIT(fFlags,kUseQualityChecks))
302 pix.SetExcludeQualityCheck();
303
304 // Exclude the blind pixel and the PIN Diode from normal pixel calibration:
305 if (i == fgBlindPixelId)
306 pix.SetExcluded();
307
308 if (i == fgPINDiodeId)
309 pix.SetExcluded();
310
311 }
312
313 //
314 // Look for file to exclude pixels from analysis
315 //
316 if (!fExcludedPixelsFile.IsNull())
317 {
318
319 fExcludedPixelsFile = gSystem->ExpandPathName(fExcludedPixelsFile.Data());
320
321 //
322 // Initialize reading the file
323 //
324 ifstream in(fExcludedPixelsFile.Data(),ios::in);
325
326 if (in)
327 {
328 *fLog << inf << "Use excluded pixels from file: '" << fExcludedPixelsFile.Data() << "'" << endl;
329 //
330 // Read the file and count the number of entries
331 //
332 UInt_t pixel = 0;
333
334 while (++fNumExcludedPixels)
335 {
336
337 in >> pixel;
338
339 if (!in.good())
340 break;
341 //
342 // Check for out of range
343 //
344 if (pixel > npixels)
345 {
346 *fLog << warn << "WARNING: To be excluded pixel: " << pixel
347 << " is out of range " << endl;
348 continue;
349 }
350 //
351 // Exclude pixel
352 //
353 MCalibrationPix &pix = (*fCalibrations)[pixel];
354 pix.SetExcluded();
355
356 *fLog << GetDescriptor() << inf << ": Exclude Pixel: " << pixel << endl;
357
358 }
359
360 if (--fNumExcludedPixels == 0)
361 *fLog << warn << "WARNING: File '" << fExcludedPixelsFile.Data()
362 << "'" << " is empty " << endl;
363 else
364 fCalibrations->SetNumPixelsExcluded(fNumExcludedPixels);
365
366 }
367 else
368 *fLog << warn << dbginf << "Cannot open file '" << fExcludedPixelsFile.Data() << "'" << endl;
369 }
370
371 return kTRUE;
372}
373
374
375// --------------------------------------------------------------------------
376//
377// Calculate the integral of the FADC time slices and store them as a new
378// pixel in the MCerPhotEvt container.
379//
380Int_t MCalibrationCalc::Process()
381{
382
383 //
384 // Initialize pointers to blind pixel, PIN Diode and individual pixels
385 //
386 MCalibrationBlindPix &blindpixel = *(fCalibrations->GetBlindPixel());
387
388 MRawEvtPixelIter pixel(fRawEvt);
389
390 //
391 // Create a loop to fill the calibration histograms
392 // Search for: a signal in MExtractedSignalCam
393 // Fill histograms with:
394 // charge
395 // charge vs. event nr.
396 //
397 while (pixel.Next())
398 {
399
400 const UInt_t pixid = pixel.GetPixelId();
401
402 MCalibrationPix &pix = (*fCalibrations)[pixid];
403
404 MExtractedSignalPix &sig = (*fSignals)[pixid];
405
406 const Float_t sumhi = sig.GetExtractedSignalHiGain();
407 const Float_t sumlo = sig.GetExtractedSignalLoGain();
408
409 Float_t abstime = 0.;
410 if (sig.IsLoGainUsed())
411 abstime = (Float_t)pixel.GetIdxMaxLoGainSample();
412 else
413 abstime = (Float_t)pixel.GetIdxMaxHiGainSample();
414
415 switch(pixid)
416 {
417
418 case fgBlindPixelId:
419
420 if (TESTBIT(fFlags,kUseBlindPixelFit))
421 {
422
423 Byte_t *ptr = pixel.GetHiGainSamples();
424
425 Int_t blindpixelsumhi = 0;
426 Int_t blindpixelsumlo = 0;
427 //
428 // We need a dedicated signal extractor for the blind pixel
429 //
430 Int_t diff = 0;
431 Int_t last = fBlindPixelLast;
432 Int_t first = fBlindPixelFirst;
433
434 if (last > 15)
435 {
436 diff = last - 15;
437 last = 15;
438 }
439
440 Byte_t *start = ptr + first - 1;
441 Byte_t *end = ptr + last - 1;
442
443 ptr = start;
444
445 Int_t sum = 0;
446
447 while (ptr<=end)
448 {
449 sum += *ptr;
450 ptr++;
451 }
452
453 if (diff > 0)
454 {
455 ptr = pixel.GetLoGainSamples();
456 end = ptr + diff - 1;
457
458 while (ptr<=end)
459 {
460 sum += *ptr;
461 ptr++;
462 }
463 }
464
465 blindpixelsumhi = sum;
466
467 ptr = pixel.GetLoGainSamples();
468
469 start = ptr + fBlindPixelFirst - 1;
470 end = ptr + fBlindPixelLast;
471
472 ptr = start;
473
474 sum = 0;
475
476 while (++ptr<end)
477 sum += *ptr;
478
479 blindpixelsumlo = sum;
480
481 // if (!CalcSignalBlindPixel(hiptr, blindpixelsumhi))
482 // return kFALSE;
483
484 if (!blindpixel.FillCharge(blindpixelsumhi))
485 *fLog << warn <<
486 "Overflow or Underflow occurred filling Blind Pixel sum = " << blindpixelsumhi << endl;
487
488 // Byte_t *loptr = pixel.GetLoGainSamples();
489 // CalcSignalBlindPixel(loptr, blindpixelsumlo);
490
491 blindpixel.FillGraphs(blindpixelsumhi,blindpixelsumlo);
492
493 TH1I *hist;
494
495 if (blindpixelsumhi > fBlindPixelSinglePheCut)
496 {
497 hist = (blindpixel.GetHist())->GetHSinglePheFADCSlices();
498 fNumBlindPixelSinglePhe++;
499 }
500 else
501 {
502 hist = (blindpixel.GetHist())->GetHPedestalFADCSlices();
503 fNumBlindPixelPedestal++;
504 }
505
506 ptr = pixel.GetHiGainSamples();
507 for (Int_t i=1;i<16;i++)
508 hist->Fill(i,*ptr++);
509
510 ptr = pixel.GetLoGainSamples();
511 for (Int_t i=16;i<31;i++)
512 hist->Fill(i,*ptr++);
513
514 } /* if use blind pixel */
515
516 break;
517 default:
518
519 if (pix.IsExcluded())
520 continue;
521
522 pix.FillGraphs(sumhi,sumlo);
523
524 if (sig.IsLoGainUsed())
525 {
526
527 if (!pix.FillChargeLoGain(sumlo))
528 *fLog << warn << "Could not fill Lo Gain Charge of pixel: " << pixid
529 << " signal = " << sumlo << endl;
530
531 if (!pix.FillAbsTimeLoGain(abstime))
532 *fLog << warn << "Could not fill Lo Gain Abs. Time of pixel: "
533 << pixid << " time = " << abstime << endl;
534 /*
535 if (!pix.FillRelTimeLoGain(reltime))
536 *fLog << warn << "Could not fill Lo Gain Rel. Time of pixel: "
537 << pixid << " time = " << reltime << endl;
538 */
539 } /* if (sig.IsLoGainUsed()) */
540 else
541 {
542 if (!pix.FillChargeHiGain(sumhi))
543 *fLog << warn << "Could not fill Hi Gain Charge of pixel: " << pixid
544 << " signal = " << sumhi << endl;
545
546 if (!pix.FillAbsTimeHiGain(abstime))
547 *fLog << warn << "Could not fill Hi Gain Abs. Time of pixel: "
548 << pixid << " time = " << abstime << endl;
549 /*
550 if (!pix.FillRelTimeHiGain(reltime))
551 *fLog << warn << "Could not fill Hi Gain Rel. Time of pixel: "
552 << pixid << " time = " << reltime << endl;
553 */
554 } /* else (sig.IsLoGainUsed()) */
555 break;
556
557 } /* switch(pixid) */
558
559 } /* while (pixel.Next()) */
560
561 return kTRUE;
562}
563
564Int_t MCalibrationCalc::PostProcess()
565{
566
567 *fLog << inf << endl;
568
569 *fLog << inf << GetDescriptor() << ": Cut Histogram Edges" << endl;
570
571 //
572 // Cut edges to make fits and viewing of the hists easier
573 //
574 fCalibrations->CutEdges();
575
576 //
577 // Fit the blind pixel
578 //
579 if (TESTBIT(fFlags,kUseBlindPixelFit))
580 {
581 //
582 // Get pointer to blind pixel
583 //
584 MCalibrationBlindPix &blindpixel = *(fCalibrations->GetBlindPixel());
585
586 *fLog << inf << GetDescriptor() << ": Fitting the Blind Pixel" << endl;
587
588 //
589 // retrieve mean and sigma of the blind pixel pedestal,
590 // so that we can use it for the fit
591 //
592 Float_t pedestal;
593 Float_t pederr;
594 Float_t pedsigma;
595 Float_t pedsigmaerr;
596
597 const ULong_t nentries = fPedestals->GetTotalEntries();
598 const Int_t nslices = fBlindPixelLast-fBlindPixelFirst+1;
599 const Float_t sqrslice = TMath::Sqrt((Float_t)nslices);
600
601 MPedestalPix &pedpix = (*fPedestals)[fgBlindPixelId];
602 pedestal = pedpix.GetPedestal()*nslices;
603 pederr = pedpix.GetPedestalRms()*nslices/nentries;
604 pedsigma = pedpix.GetPedestalRms()*sqrslice;
605 pedsigmaerr = pederr/2.;
606 //
607 // retrieve the histogram containers
608 //
609 MHCalibrationBlindPixel *hist = blindpixel.GetHist();
610
611 hist->SetMeanPedestal(pedestal);
612 hist->SetMeanPedestalErr(pederr);
613 hist->SetSigmaPedestal(pedsigma);
614 hist->SetSigmaPedestalErr(pedsigmaerr);
615
616 if (!blindpixel.FitCharge())
617 {
618 *fLog << warn << "Could not fit the blind pixel! " << endl;
619 *fLog << warn << "Setting bit kBlindPixelMethodValid to FALSE in MCalibrationCam" << endl;
620 fCalibrations->SetBlindPixelMethodValid(kFALSE);
621 }
622 else
623 fCalibrations->SetBlindPixelMethodValid(kTRUE);
624
625 if (blindpixel.CheckOscillations())
626 fCalibrations->SetBlindPixelMethodValid(kFALSE);
627
628 TH1I *sphehist = hist->GetHSinglePheFADCSlices();
629 TH1I *pedhist = hist->GetHPedestalFADCSlices();
630
631 if (fNumBlindPixelSinglePhe > 1)
632 sphehist->Scale(1./fNumBlindPixelSinglePhe);
633 if (fNumBlindPixelPedestal > 1)
634 pedhist->Scale(1./fNumBlindPixelPedestal);
635
636 blindpixel.DrawClone();
637 }
638 else
639 *fLog << inf << GetDescriptor() << ": Skipping Blind Pixel Fit " << endl;
640
641 *fLog << err << "total: " << GetNumExecutions() << " sphe: " << fNumBlindPixelSinglePhe << " ped: " << fNumBlindPixelPedestal << endl;
642
643
644 *fLog << inf << GetDescriptor() << ": Fitting the Normal Pixels" << endl;
645
646 //
647 // loop over the pedestal events and check if we have calibration
648 //
649 for (Int_t pixid=0; pixid<fPedestals->GetSize(); pixid++)
650 {
651
652 MCalibrationPix &pix = (*fCalibrations)[pixid];
653
654 //
655 // Check if the pixel has been excluded from the fits
656 //
657 if (pix.IsExcluded())
658 continue;
659
660 //
661 // get the pedestals
662 //
663 const Float_t ped = (*fPedestals)[pixid].GetPedestal();
664 const Float_t prms = (*fPedestals)[pixid].GetPedestalRms();
665
666 //
667 // set them in the calibration camera
668 //
669 pix.SetPedestal(ped,prms,(Float_t)fNumHiGainSamples,(Float_t)fNumLoGainSamples);
670
671 //
672 // perform the Gauss fits to the charges
673 //
674 pix.FitCharge();
675
676 //
677 // check also for oscillations
678 //
679 pix.CheckOscillations();
680
681 }
682
683 if (TESTBIT(fFlags,kUseBlindPixelFit) && fCalibrations->IsBlindPixelMethodValid())
684 {
685 if (!fCalibrations->CalcFluxInsidePlexiglass())
686 {
687 *fLog << err
688 << "Could not calculate the number of photons from the blind pixel " << endl;
689 *fLog << err
690 << "You can try to calibrate using the MCalibrationCalc::SkipBlindPixelFit()" << endl;
691 fCalibrations->SetBlindPixelMethodValid(kFALSE);
692 }
693 }
694 else
695 *fLog << inf << GetDescriptor() << ": Skipping Blind Pixel Calibration! " << endl;
696
697
698 if (fCalibrations->IsPINDiodeMethodValid())
699 {
700 if (!fCalibrations->CalcFluxOutsidePlexiglass())
701 {
702 *fLog << err
703 << "Could not calculate the number of photons from the PIN Diode " << endl;
704 fCalibrations->SetPINDiodeMethodValid(kFALSE);
705 }
706 }
707 else
708 *fLog << inf << GetDescriptor() << ": Skipping PIN Diode Calibration! " << endl;
709
710 fCalibrations->SetReadyToSave();
711
712 return kTRUE;
713}
714
715
716Bool_t MCalibrationCalc::CalcSignalBlindPixel(Byte_t *ptr, Float_t &signal) const
717{
718
719 Byte_t *newptr = ptr;
720 Byte_t *start = ptr + fBlindPixelFirst-1;
721 Byte_t *end = ptr + fBlindPixelLast;
722
723 Byte_t sum = 0;
724 Int_t sat = 0;
725
726 newptr = start;
727
728 while (ptr<end)
729 {
730 sum += *ptr;
731 if (*ptr++ >= fgSaturationLimit)
732 sat++;
733 }
734
735 if (sat)
736 {
737 *fLog << err << "HI Gain Saturation occurred in the blind pixel! "
738 << " Do not know yet how to treat this ... aborting " << endl;
739 *fLog << err << "If you need absolutely any other kind of calibration, "
740 << " use SkipBlindPixelFit() " << endl;
741 return kFALSE;
742 }
743
744 signal = (Float_t)sum;
745
746 return kTRUE;
747}
Note: See TracBrowser for help on using the repository browser.