f223bc25a40be03907f3364441a08120eeb96f0c
[u/mrichter/AliRoot.git] / FMD / AliFMDRawWriter.cxx
1 /**************************************************************************
2  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3  *                                                                        *
4  * Author: The ALICE Off-line 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. The authors make no claims     *
12  * about the suitability of this software for any purpose. It is          *
13  * provided "as is" without express or implied warranty.                  *
14  **************************************************************************/
15 /* $Id$ */
16 /** @file    AliFMDRawWriter.cxx
17     @author  Christian Holm Christensen <cholm@nbi.dk>
18     @date    Mon Mar 27 12:45:56 2006
19     @brief   Class to write raw data 
20 */
21 //____________________________________________________________________
22 //
23 // Class to write ADC values to a raw data file
24 //
25 // This class writes FMD Raw data to a file.   The sample rate (number
26 // of times the ALTRO ADC samples each pre-amp. channel - that is,
27 // data from a single strip), can be set via SetSampleRate. 
28 //
29 // Zero-suppression can be enabled by calling SetThreshold with a
30 // non-zero argument.   ADC values less than the value set will not be
31 // written to output.   Note, that if you use zero-suppression, you
32 // need to explicitly set the sample rate when reading back the data
33 // with AliFMDRawReader. 
34 // 
35 // This class uses the AliAltroBuffer class to write the data in the
36 // ALTRO format.  See the Exec member function for more information on
37 // that format.  
38 //
39 // #include <AliLog.h>          // ALILOG_H
40 #include "AliFMDDebug.h"        // Better debug macros
41 #include <AliLoader.h>          // ALILOADER_H
42 #include <AliAltroBufferV3.h>   // ALIALTROBUFFER_H
43 #include "AliFMD.h"             // ALIFMD_H
44 #include "AliFMDParameters.h"   // ALIFMDPARAMETERS_H
45 #include "AliFMDDigit.h"        // ALIFMDDIGIT_H
46 #include "AliFMDRawWriter.h"    // ALIFMDRAWREADER_H 
47 #include "AliFMDAltroMapping.h" // ALIFMDALTROMAPPING_H
48 // #include "AliFMDAltroIO.h"   // ALIFMDALTROWRITER_H
49 #include <TArrayI.h>            // ROOT_TArrayI
50 #include <TArrayF.h>            // ROOT_TArrayI
51 #include <TArrayC.h>            // ROOT_TArrayI
52 #include <TClonesArray.h>       // ROOT_TClonesArray
53 // #include <fstream>
54 #include "AliDAQ.h"
55
56 //____________________________________________________________________
57 ClassImp(AliFMDRawWriter)
58 #if 0
59   ; // This is here to keep Emacs for indenting the next line
60 #endif
61
62 //____________________________________________________________________
63 AliFMDRawWriter::AliFMDRawWriter(AliFMD* fmd) 
64   : TTask("FMDRawWriter", "Writer of Raw ADC values from the FMD"),
65     fFMD(fmd),
66     fSampleRate(0), 
67     fChannelsPerAltro(0), 
68     fThreshold(0)
69 {
70   // CTOR 
71   AliFMDDebug(5, ("Created AliFMDRawWriter object"));
72 }
73
74
75
76 //____________________________________________________________________
77 void
78 AliFMDRawWriter::Exec(Option_t*) 
79 {
80   // Turn digits into raw data. 
81   // 
82   // Digits are read from the Digit branch, and processed to make
83   // three DDL files, one for each of the sub-detectors FMD1, FMD2,
84   // and FMD3. 
85   //
86   // The raw data files consists of a header, followed by ALTRO
87   // formatted blocks.  
88   // 
89   //          +-------------+
90   //          | Header      |
91   //          +-------------+
92   //          | ALTRO Block |
93   //          | ...         |
94   //          +-------------+
95   //          DDL file 
96   // 
97   // An ALTRO formatted block, in the FMD context, consists of a
98   // number of counts followed by a trailer. 
99   // 
100   //          +------------------+
101   //          | Count            |
102   //          | ...              |
103   //          | possible fillers |
104   //          +------------------+
105   //          | Trailer          |
106   //          +------------------+
107   //          ALTRO block 
108   // 
109   // The counts are listed backwards, that is, starting with the
110   // latest count, and ending in the first. 
111   // 
112   // Each count consist of 1 or more ADC samples of the VA1_ALICE
113   // pre-amp. signal.  Just how many samples are used depends on
114   // whether the ALTRO over samples the pre-amp.  Each sample is a
115   // 10-bit word, and the samples are grouped into 40-bit blocks 
116   //
117   //          +------------------------------------+
118   //          |  S(1)   | S(2)   | S(3)   | S(4)   |
119   //          |  ...    | ...    | ...    | ...    |
120   //          |  S(n)   | T(n)   | n+2    | 2AA    |
121   //          +------------------------------------+
122   //          Counts + possible filler 
123   //
124   // The trailer of the number of words of signales, the starting
125   // strip number, the sector number, and the ring ID; each 10-bit
126   // words,  packed into 40-bits. 
127   // 
128   //          +------------------------------------+
129   //          |   2AAA   |  Len   |  A |  Address  |
130   //          +------------------------------------+
131   //          Trailer
132   // 
133   // Note, that this method assumes that the digits are ordered. 
134   // 
135   AliLoader* loader = fFMD->GetLoader();
136   loader->LoadDigits("READ");
137   TTree* digitTree = loader->TreeD();
138   if (!digitTree) {
139     AliError("no digit tree");
140     return;
141   }
142   
143   fFMD->SetTreeAddress();
144   TClonesArray* digits = fFMD->Digits(); 
145   // new TClonesArray("AliFMDDigit", 1000);
146   // TBranch* digitBranch = digitTree->GetBranch(fFMD->GetName());
147   // if (!digitBranch) {
148   //   AliError(Form("no branch for %s", fFMD->GetName()));
149   //   return;
150   // }
151   // digitBranch->SetAddress(&digits);
152   
153   Int_t nEvents = Int_t(digitTree->GetEntries());
154   AliFMDDebug(5, ("Got a total of %5d events from tree", nEvents));
155   for (Int_t event = 0; event < nEvents; event++) {
156     fFMD->ResetDigits();
157     digitTree->GetEvent(event);
158     
159     // Write out the digits
160     WriteDigits(digits);
161   }
162   loader->UnloadDigits();
163   //delete digits;
164 }
165
166 #if 1
167 //____________________________________________________________________
168 Long_t
169 AliFMDRawWriter::WriteDigits(TClonesArray* digits)
170 {
171   // WRite an array of digits to disk file 
172   Int_t nDigits = digits->GetEntries();
173   if (nDigits < 1) return 0;
174   AliFMDDebug(5, ("Got a total of %5d digits from tree", nDigits));
175
176   AliFMDParameters* pars = AliFMDParameters::Instance();
177   UShort_t threshold    = 0;
178   UInt_t   prevddl      = 0xFFFF;
179   UInt_t   prevaddr     = 0xFFF;
180   // UShort_t prevStrip    = 0;
181   
182   // Which channel number in the ALTRO channel we're at 
183   UShort_t nWords       = 0;
184   UShort_t preSamples   = 0;
185   UShort_t sampleRate   = 0;
186   
187   // A buffer to hold 1 ALTRO channel - Normally, one ALTRO channel
188   // holds 128 VA1_ALICE channels, sampled at a rate of `sampleRate' 
189   TArrayI data(pars->GetChannelsPerAltro() * 8);
190   TArrayF peds(pars->GetChannelsPerAltro() * 8);
191   TArrayF noise(pars->GetChannelsPerAltro() * 8);
192
193   // The Altro buffer 
194   AliAltroBufferV3* altro = 0;
195   
196   Int_t  totalWords = 0;
197   Int_t  nCounts    = 0;
198   Long_t nBits      = 0;
199
200   // Loop over the digits in the event.  Note, that we assume the
201   // the digits are in order in the branch.   If they were not, we'd
202   // have to cache all channels before we could write the data to
203   // the ALTRO buffer, or we'd have to set up a map of the digits. 
204   UShort_t oldDet = 1000;
205   for (Int_t i = 0; i < nDigits; i++) {
206     // Get the digit
207     AliFMDDigit* digit = static_cast<AliFMDDigit*>(digits->At(i));
208     UShort_t det    = digit->Detector();
209     Char_t   ring   = digit->Ring();
210     UShort_t sector = digit->Sector();
211     UShort_t strip  = digit->Strip();
212     UShort_t ddl, addr, time;
213
214     AliFMDDebug(15, ("Processing digit # %5d FMD%d%c[%2d,%3d]", 
215                     i, det, ring, sector, strip));
216     threshold  = pars->GetZeroSuppression(det, ring, sector, strip);
217     sampleRate = pars->GetSampleRate(det, ring, sector, strip);
218     preSamples = pars->GetPreSamples(det, ring, sector, strip);
219
220     if (det != oldDet) {
221       AliFMDDebug(5, ("Got new detector: %d (was %d)", det, oldDet));
222       oldDet = det;
223     }
224     AliFMDDebug(15, ("Sample rate is %d", sampleRate));
225     
226     for (UShort_t j = 0; j < sampleRate; j++) { 
227       if (!pars->Detector2Hardware(det,ring,sector,strip,j,ddl,addr,time)){
228         AliError(Form("Failed to get hardware address for FMD%d%c[%2d,%3d]-%d", 
229                       det, ring, sector, strip, j));
230         continue;
231       }
232     
233       AliFMDDebug(10, ("FMD%d%c[%2d,%3d]-%d-> 0x%x/0x%x/%04d", 
234                        det, ring, sector, strip, j, ddl, addr, time));
235       if (addr != prevaddr) {
236         // Flush a channel to output 
237         AliFMDDebug(5, ("Now hardware address 0x%x from FMD%d%c[%2d,%3d]-%d"
238                          "(b: 0x%02x, a: 0x%01x, c: 0x%02x, t: %04d), "
239                          "flushing old channel at 0x%x with %d words", 
240                          addr, det, ring, sector, strip, j,
241                          (addr >> 7), (addr >> 4) & 0x7, addr & 0xf, 
242                          time, prevaddr, nWords));
243         totalWords += nWords;
244         ZeroSuppress(data.fArray, nWords, peds.fArray, noise.fArray, threshold);
245         if (altro) 
246           /*nBits+=*/altro->WriteChannel(prevaddr,nWords,data.fArray,threshold);
247         data.Reset(-1);
248         peds.Reset(0);
249         noise.Reset(0);
250         nWords   = 0;
251         prevaddr = addr;
252       }
253       if (ddl != prevddl) {
254         AliFMDDebug(10, ("FMD: New DDL, was %d, now %d", prevddl, ddl));
255         // If an altro exists, delete the object, flushing the data to
256         // disk, and closing the file. 
257         if (altro) { 
258           // When the first argument is false, we write the real
259           // header. 
260           AliFMDDebug(15, ("Closing output"));
261           /* nBits += */ altro->Flush();
262           /* nBits += */ altro->WriteDataHeader(kFALSE, kFALSE);
263           delete altro;
264           altro = 0;
265         }
266         prevddl = ddl;
267         // Need to open a new DDL! 
268         TString filename(AliDAQ::DdlFileName(fFMD ? fFMD->GetName() : "FMD",  ddl));
269         AliFMDDebug(5, ("New altro buffer with DDL file %s", filename.Data()));
270         // Create a new altro buffer - a `1' as the second argument
271         // means `write mode' 
272         altro = new AliAltroBufferV3(filename.Data());
273         altro->SetMapping(pars->GetAltroMap());      
274         // Write a dummy (first argument is true) header to the DDL
275         // file - later on, when we close the file, we write the real
276         // header
277         altro->WriteDataHeader(kTRUE, kFALSE);
278       }
279     
280       // Get the pedestal value 
281       peds[time]  = pars->GetPedestal(det, ring, sector, strip);
282       noise[time] = pars->GetPedestalWidth(det, ring, sector, strip);
283
284       // Store the counts of the ADC in the channel buffer 
285       AliFMDDebug(15, ("Storing FMD%d%c[%02d,%03d]-%d in timebin %d (%d)",
286                       det, ring, sector, strip, j, time, preSamples));
287       data[time] = digit->Count(j);
288       nWords++;
289       nCounts++;
290       if (time == preSamples) {
291         AliFMDDebug(15, ("Filling in %4d for %d presamples", 
292                         data[time], preSamples));
293         for (int k = 0; k < preSamples; k++) { 
294           peds[k]  = peds[time];
295           noise[k] = noise[time];
296           data[k]  = data[time];
297         }
298         nWords += preSamples;
299       }
300     }
301   }
302   // Finally, we need to close the final ALTRO buffer if it wasn't
303   // already 
304   if (altro) {
305     ZeroSuppress(data.fArray, nWords, peds.fArray, noise.fArray, threshold);
306     if (nWords > 0) 
307       /* nBits += */ altro->WriteChannel(prevaddr,nWords,data.fArray,threshold);
308     /* nBits += */ altro->Flush();
309     /* nBits += */ altro->WriteDataHeader(kFALSE, kFALSE);
310     delete altro;
311   }
312   AliFMDDebug(5, ("Wrote a total of %d words in %ld bytes for %d counts", 
313                   nWords, nBits / 8, nCounts));
314   return nBits;
315 }
316 //____________________________________________________________________
317 void
318 AliFMDRawWriter::ZeroSuppress(Int_t*& data, Int_t nWords, 
319                               const Float_t* peds, 
320                               const Float_t* noise, UShort_t threshold) const
321 {
322   // Simulate the ALTRO zero-suppression filter.  The data passed in
323   // the first array is modified, such that all suppressed channels
324   // are set to some value below threshold.  
325   // 
326   // If threshold == 0 zero suppression is considered disabled, and no
327   // action is taken. 
328   if (threshold <= 0) return;
329
330   // const Short_t width  = 3;
331   // If fPedSubtract is false, compare data-(ped+f*noise), if true
332   // always modify data by -(ped+f*noise), and force negative values
333   // to zero.
334   Bool_t   pedSubtract = AliFMDParameters::Instance()->IsZSPedSubtract();
335   UShort_t pre         = AliFMDParameters::Instance()->GetZSPreSamples();
336   UShort_t post        = AliFMDParameters::Instance()->GetZSPostSamples();
337   Float_t  factor      = AliFMDParameters::Instance()->GetPedestalFactor();
338   
339   TArrayC mask(nWords+1);
340   for (Short_t i = 0; i < nWords; i++) { 
341     Float_t            val     = data[i] - peds[i] - factor * noise[i];
342     if (val < 0.5)     val     = 0;
343     if (pedSubtract)   data[i] = Int_t(val) & 0x3FF;
344
345     mask[i] = (val > threshold ? 1 : 0);
346     AliFMDDebug(10, ("Comparing sample %d %d-%f-%f*%f=%f to %d -> %s", 
347                      i, data[i], peds[i], factor, noise[i], val, threshold, 
348                     (mask[i] ? "above" : "below")));
349   }
350   
351   for (Short_t i = 0; i < nWords; i++) { 
352     if (mask[i]) { // Signal above, so do nothing 
353       AliFMDDebug(10, ("Sample %d, above", i));
354       if (i < nWords-1 && !mask[i+1]) { 
355         // After a valid sample.  Increase the pointer to the next
356         // possible data, thereby skipping over the post-samples 
357         AliFMDDebug(10, ("At sample %d, next is below, skipping %d to %d", 
358                         i, post, i+post));
359         i += post;
360       }
361       continue;
362     }
363     
364     Short_t lookahead = TMath::Min(Short_t(nWords), Short_t(i+pre));
365     AliFMDDebug(10, ("Sample %d, below, look to %d", i, lookahead));
366     if (mask[lookahead] && pre > 0) { 
367       AliFMDDebug(10, ("Sample %d is a pre-sample to %d", i, lookahead));
368       // We're in a presample, so don't modify the data, and increase
369       // counter by the number of pre-samples 
370       i += pre-1;
371       continue;
372     }
373     
374     // This sample must be surpressed 
375     data[i] = threshold - 1;
376   }
377 }
378
379 #else
380 //____________________________________________________________________
381 void
382 AliFMDRawWriter::WriteDigits(TClonesArray* digits)
383 {
384   // 
385   // Write digits to file 
386   //
387   Int_t nDigits = digits->GetEntries();
388   if (nDigits < 1) return;
389
390   AliFMDParameters*  pars    = AliFMDParameters::Instance();
391   AliFMDAltroWriter* writer  = 0;
392   Int_t          sampleRate  = -1;
393   UShort_t       hwaddr      = 0;
394   UShort_t       ddl         = 0;
395   std::ofstream* file        = 0;
396   Int_t          ret         = 0;
397   for (Int_t i = 0; i < nDigits; i++) {
398     // Get the digit
399     AliFMDDigit* digit = static_cast<AliFMDDigit*>(digits->At(i));
400     UInt_t   thisDDL, thisHwaddr;
401     UShort_t det    = digit->Detector();
402     Char_t   ring   = digit->Ring();
403     UShort_t sector = digit->Sector();
404     UShort_t strip  = digit->Strip();
405     if (!pars->Detector2Hardware(det,ring,sector,strip,thisDDL,thisHwaddr)) {
406       AliError(Form("Failed to get hardware address for FMD%d%c[%2d,%3d]",
407                     det, ring, sector, strip));
408       continue;
409     }
410     AliFMDDebug(40, ("Got DDL=%d and address=%d from FMD%d%c[%2d,%3d]", 
411                      thisDDL, thisHwaddr, det, ring, sector, strip));
412     // Check if we're still in the same channel
413     if (thisHwaddr != hwaddr) {
414       AliFMDDebug(30, ("Now hardware address 0x%x from FMD%d%c[%2d,%3d] "
415                        "(board 0x%x, chip 0x%x, channel 0x%x)",
416                        thisHwaddr, det, ring, sector, strip, 
417                        (thisHwaddr >> 7), (thisHwaddr >> 4) & 0x7, 
418                        thisHwaddr & 0xf));
419       if (writer) writer->AddChannelTrailer(hwaddr);
420       hwaddr = thisHwaddr;
421     }
422     // Check if we're still in the same detector (DDL)
423     if (ddl != thisDDL) {
424       if (writer) {
425         AliFMDDebug(1, ("Closing altro writer %p", writer));
426         if ((ret = writer->Close()) < 0) {
427           AliError(Form("Error: %s", writer->ErrorString(ret)));
428           return;
429         }
430         delete writer;
431         writer = 0;
432         file->close();
433         delete file;
434         file = 0;
435       }
436       ddl = thisDDL;
437     }
438     // If we haven't got a writer (either because none were made so
439     // far, or because we've switch DDL), make one. 
440     if (!writer) {
441       AliFMDDebug(1, ("Opening new ALTRO writer w/file %s", 
442                       AliDAQ::DdlFileName("FMD",ddl)));
443       file   = new std::ofstream(AliDAQ::DdlFileName("FMD",ddl));
444       if (!file || !*file) {
445         AliFatal(Form("Failed to open file %s", 
446                       AliDAQ::DdlFileName("FMD",ddl)));
447         return;
448       }
449       writer  = new AliFMDAltroWriter(*file);
450       writer->SetThreshold(pars->GetZeroSuppression(det, ring, sector, strip));
451     }
452     // Write out our signal
453     sampleRate =  pars->GetSampleRate(det,ring,sector,strip);
454     writer->AddSignal(digit->Count1());
455     if (sampleRate >= 2) writer->AddSignal(digit->Count2());
456     if (sampleRate >= 3) writer->AddSignal(digit->Count3());
457   }
458   if (writer) {
459     writer->AddChannelTrailer(hwaddr);
460     writer->Close();
461     delete writer;
462     file->close();
463     delete file;
464   }
465 }
466 #endif
467
468
469   
470
471 //____________________________________________________________________
472 // 
473 // EOF
474 //
475