Fixes for reading zero-suppressed data. Also fixes to simulated raw
[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 <AliAltroBuffer.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   TClonesArray* digits = new TClonesArray("AliFMDDigit", 1000);
144   fFMD->SetTreeAddress();
145   TBranch* digitBranch = digitTree->GetBranch(fFMD->GetName());
146   if (!digitBranch) {
147     AliError(Form("no branch for %s", fFMD->GetName()));
148     return;
149   }
150   digitBranch->SetAddress(&digits);
151   
152   Int_t nEvents = Int_t(digitTree->GetEntries());
153   AliFMDDebug(5, ("Got a total of %5d events from tree", nEvents));
154   for (Int_t event = 0; event < nEvents; event++) {
155     fFMD->ResetDigits();
156     digitTree->GetEvent(event);
157     
158     // Write out the digits
159     WriteDigits(digits);
160   }
161   loader->UnloadDigits();
162 }
163
164 #if 1
165 //____________________________________________________________________
166 void
167 AliFMDRawWriter::WriteDigits(TClonesArray* digits)
168 {
169   // WRite an array of digits to disk file 
170   Int_t nDigits = digits->GetEntries();
171   if (nDigits < 1) return;
172   AliFMDDebug(5, ("Got a total of %5d digits from tree", nDigits));
173
174   AliFMDParameters* pars = AliFMDParameters::Instance();
175   UShort_t threshold    = 0;
176   UInt_t   prevddl      = 0xFFFF;
177   UInt_t   prevaddr     = 0xFFF;
178   // UShort_t prevStrip    = 0;
179   
180   // Which channel number in the ALTRO channel we're at 
181   UShort_t nWords       = 0;
182   UShort_t preSamples   = 0;
183   UShort_t sampleRate   = 0;
184   
185   // A buffer to hold 1 ALTRO channel - Normally, one ALTRO channel
186   // holds 128 VA1_ALICE channels, sampled at a rate of `sampleRate' 
187   TArrayI data(pars->GetChannelsPerAltro() * 8);
188   TArrayF peds(pars->GetChannelsPerAltro() * 8);
189   TArrayF noise(pars->GetChannelsPerAltro() * 8);
190
191   // The Altro buffer 
192   AliAltroBuffer* altro = 0;
193   
194   Int_t totalWords = 0;
195   Int_t nCounts    = 0;
196   
197   // Loop over the digits in the event.  Note, that we assume the
198   // the digits are in order in the branch.   If they were not, we'd
199   // have to cache all channels before we could write the data to
200   // the ALTRO buffer, or we'd have to set up a map of the digits. 
201   UShort_t oldDet = 1000;
202   for (Int_t i = 0; i < nDigits; i++) {
203     // Get the digit
204     AliFMDDigit* digit = static_cast<AliFMDDigit*>(digits->At(i));
205     UShort_t det    = digit->Detector();
206     Char_t   ring   = digit->Ring();
207     UShort_t sector = digit->Sector();
208     UShort_t strip  = digit->Strip();
209     UInt_t   ddl;
210     UInt_t   addr;  
211     UShort_t time;
212
213     AliFMDDebug(15, ("Processing digit # %5d FMD%d%c[%2d,%3d]", 
214                     i, det, ring, sector, strip));
215     threshold  = pars->GetZeroSuppression(det, ring, sector, strip);
216     sampleRate = pars->GetSampleRate(det, ring, sector, strip);
217     preSamples = pars->GetPreSamples(det, ring, sector, strip);
218
219     if (det != oldDet) {
220       AliFMDDebug(5, ("Got new detector: %d (was %d)", det, oldDet));
221       oldDet = det;
222     }
223     AliFMDDebug(15, ("Sample rate is %d", sampleRate));
224     
225     for (UShort_t j = 0; j < sampleRate; j++) { 
226       if (!pars->Detector2Hardware(det,ring,sector,strip,j,ddl,addr,time)){
227         AliError(Form("Failed to get hardware address for FMD%d%c[%2d,%3d]-%d", 
228                       det, ring, sector, strip, j));
229         continue;
230       }
231     
232       AliFMDDebug(10, ("FMD%d%c[%2d,%3d]-%d-> 0x%x/0x%x/%04d", 
233                        det, ring, sector, strip, j, ddl, addr, time));
234       if (addr != prevaddr) {
235         // Flush a channel to output 
236         AliFMDDebug(5, ("Now hardware address 0x%x from FMD%d%c[%2d,%3d]-%d"
237                          "(b: 0x%02x, a: 0x%01x, c: 0x%02x, t: %04d), "
238                          "flushing old channel at 0x%x with %d words", 
239                          addr, det, ring, sector, strip, j,
240                          (addr >> 7), (addr >> 4) & 0x7, addr & 0xf, 
241                          time, prevaddr, nWords));
242         totalWords += nWords;
243         ZeroSuppress(data.fArray, nWords, peds.fArray, noise.fArray, threshold);
244         if (altro) altro->WriteChannel(prevaddr,nWords,data.fArray,threshold);
245         data.Reset(-1);
246         peds.Reset(0);
247         noise.Reset(0);
248         nWords   = 0;
249         prevaddr = addr;
250       }
251       if (ddl != prevddl) {
252         AliFMDDebug(10, ("FMD: New DDL, was %d, now %d", prevddl, ddl));
253         // If an altro exists, delete the object, flushing the data to
254         // disk, and closing the file. 
255         if (altro) { 
256           // When the first argument is false, we write the real
257           // header. 
258           AliFMDDebug(15, ("Closing output"));
259           altro->Flush();
260           altro->WriteDataHeader(kFALSE, kFALSE);
261           delete altro;
262           altro = 0;
263         }
264         prevddl = ddl;
265         // Need to open a new DDL! 
266         TString filename(AliDAQ::DdlFileName(fFMD->GetName(),  ddl));
267         AliFMDDebug(5, ("New altro buffer with DDL file %s", filename.Data()));
268         // Create a new altro buffer - a `1' as the second argument
269         // means `write mode' 
270         altro = new AliAltroBuffer(filename.Data());
271         altro->SetMapping(pars->GetAltroMap());      
272         // Write a dummy (first argument is true) header to the DDL
273         // file - later on, when we close the file, we write the real
274         // header
275         altro->WriteDataHeader(kTRUE, kFALSE);
276       }
277     
278       // Get the pedestal value 
279       peds[time]  = pars->GetPedestal(det, ring, sector, strip);
280       noise[time] = pars->GetPedestalWidth(det, ring, sector, strip);
281
282       // Store the counts of the ADC in the channel buffer 
283       AliFMDDebug(15, ("Storing FMD%d%c[%02d,%03d]-%d in timebin %d (%d)",
284                       det, ring, sector, strip, j, time, preSamples));
285       data[time] = digit->Count(j);
286       nWords++;
287       nCounts++;
288       if (time == preSamples) {
289         AliFMDDebug(15, ("Filling in %4d for %d presamples", 
290                         data[time], preSamples));
291         for (int k = 0; k < preSamples; k++) { 
292           peds[k]  = peds[time];
293           noise[k] = noise[time];
294           data[k]  = data[time];
295         }
296         nWords += preSamples;
297       }
298     }
299   }
300   // Finally, we need to close the final ALTRO buffer if it wasn't
301   // already 
302   if (altro) {
303     ZeroSuppress(data.fArray, nWords, peds.fArray, noise.fArray, threshold);
304     if (nWords > 0) altro->WriteChannel(prevaddr,nWords,data.fArray,threshold);
305     altro->Flush();
306     altro->WriteDataHeader(kFALSE, kFALSE);
307     delete altro;
308   }
309   AliFMDDebug(5, ("Wrote a total of %d words for %d counts", 
310                   nWords, nCounts));
311 }
312 //____________________________________________________________________
313 void
314 AliFMDRawWriter::ZeroSuppress(Int_t*& data, Int_t nWords, 
315                               const Float_t* peds, 
316                               const Float_t* noise, UShort_t threshold) const
317 {
318   // Simulate the ALTRO zero-suppression filter.  The data passed in
319   // the first array is modified, such that all suppressed channels
320   // are set to some value below threshold.  
321   // 
322   // If threshold == 0 zero suppression is considered disabled, and no
323   // action is taken. 
324   if (threshold <= 0) return;
325
326   // const Short_t width  = 3;
327   // If fPedSubtract is false, compare data-(ped+f*noise), if true
328   // always modify data by -(ped+f*noise), and force negative values
329   // to zero.
330   Bool_t   pedSubtract = AliFMDParameters::Instance()->IsZSPedSubtract();
331   UShort_t pre         = AliFMDParameters::Instance()->GetZSPreSamples();
332   UShort_t post        = AliFMDParameters::Instance()->GetZSPostSamples();
333   Float_t  factor      = AliFMDParameters::Instance()->GetPedestalFactor();
334   
335   TArrayC mask(nWords+1);
336   for (Short_t i = 0; i < nWords; i++) { 
337     Float_t            val     = data[i] - peds[i] - factor * noise[i];
338     if (val < 0.5)     val     = 0;
339     if (pedSubtract)   data[i] = Int_t(val) & 0x3FF;
340
341     mask[i] = (val > threshold ? 1 : 0);
342     AliFMDDebug(10, ("Comparing sample %d %d-%f-%f*%f=%f to %d -> %s", 
343                      i, data[i], peds[i], factor, noise[i], val, threshold, 
344                     (mask[i] ? "above" : "below")));
345   }
346   
347   for (Short_t i = 0; i < nWords; i++) { 
348     if (mask[i]) { // Signal above, so do nothing 
349       AliFMDDebug(10, ("Sample %d, above", i));
350       if (i < nWords-1 && !mask[i+1]) { 
351         // After a valid sample.  Increase the pointer to the next
352         // possible data, thereby skipping over the post-samples 
353         AliFMDDebug(10, ("At sample %d, next is below, skipping %d to %d", 
354                         i, post, i+post));
355         i += post;
356       }
357       continue;
358     }
359     
360     Short_t lookahead = TMath::Min(Short_t(nWords), Short_t(i+pre));
361     AliFMDDebug(10, ("Sample %d, below, look to %d", i, lookahead));
362     if (mask[lookahead] && pre > 0) { 
363       AliFMDDebug(10, ("Sample %d is a pre-sample to %d", i, lookahead));
364       // We're in a presample, so don't modify the data, and increase
365       // counter by the number of pre-samples 
366       i += pre-1;
367       continue;
368     }
369     
370     // This sample must be surpressed 
371     data[i] = threshold - 1;
372   }
373 }
374
375 #else
376 //____________________________________________________________________
377 void
378 AliFMDRawWriter::WriteDigits(TClonesArray* digits)
379 {
380   Int_t nDigits = digits->GetEntries();
381   if (nDigits < 1) return;
382
383   AliFMDParameters*  pars    = AliFMDParameters::Instance();
384   AliFMDAltroWriter* writer  = 0;
385   Int_t          sampleRate  = -1;
386   UShort_t       hwaddr      = 0;
387   UShort_t       ddl         = 0;
388   std::ofstream* file        = 0;
389   Int_t          ret         = 0;
390   for (Int_t i = 0; i < nDigits; i++) {
391     // Get the digit
392     AliFMDDigit* digit = static_cast<AliFMDDigit*>(digits->At(i));
393     UInt_t   thisDDL, thisHwaddr;
394     UShort_t det    = digit->Detector();
395     Char_t   ring   = digit->Ring();
396     UShort_t sector = digit->Sector();
397     UShort_t strip  = digit->Strip();
398     if (!pars->Detector2Hardware(det,ring,sector,strip,thisDDL,thisHwaddr)) {
399       AliError(Form("Failed to get hardware address for FMD%d%c[%2d,%3d]",
400                     det, ring, sector, strip));
401       continue;
402     }
403     AliFMDDebug(40, ("Got DDL=%d and address=%d from FMD%d%c[%2d,%3d]", 
404                      thisDDL, thisHwaddr, det, ring, sector, strip));
405     // Check if we're still in the same channel
406     if (thisHwaddr != hwaddr) {
407       AliFMDDebug(30, ("Now hardware address 0x%x from FMD%d%c[%2d,%3d] "
408                        "(board 0x%x, chip 0x%x, channel 0x%x)",
409                        thisHwaddr, det, ring, sector, strip, 
410                        (thisHwaddr >> 7), (thisHwaddr >> 4) & 0x7, 
411                        thisHwaddr & 0xf));
412       if (writer) writer->AddChannelTrailer(hwaddr);
413       hwaddr = thisHwaddr;
414     }
415     // Check if we're still in the same detector (DDL)
416     if (ddl != thisDDL) {
417       if (writer) {
418         AliFMDDebug(1, ("Closing altro writer %p", writer));
419         if ((ret = writer->Close()) < 0) {
420           AliError(Form("Error: %s", writer->ErrorString(ret)));
421           return;
422         }
423         delete writer;
424         writer = 0;
425         file->close();
426         delete file;
427         file = 0;
428       }
429       ddl = thisDDL;
430     }
431     // If we haven't got a writer (either because none were made so
432     // far, or because we've switch DDL), make one. 
433     if (!writer) {
434       AliFMDDebug(1, ("Opening new ALTRO writer w/file %s", 
435                       AliDAQ::DdlFileName("FMD",ddl)));
436       file   = new std::ofstream(AliDAQ::DdlFileName("FMD",ddl));
437       if (!file || !*file) {
438         AliFatal(Form("Failed to open file %s", 
439                       AliDAQ::DdlFileName("FMD",ddl)));
440         return;
441       }
442       writer  = new AliFMDAltroWriter(*file);
443       writer->SetThreshold(pars->GetZeroSuppression(det, ring, sector, strip));
444     }
445     // Write out our signal
446     sampleRate =  pars->GetSampleRate(det,ring,sector,strip);
447     writer->AddSignal(digit->Count1());
448     if (sampleRate >= 2) writer->AddSignal(digit->Count2());
449     if (sampleRate >= 3) writer->AddSignal(digit->Count3());
450   }
451   if (writer) {
452     writer->AddChannelTrailer(hwaddr);
453     writer->Close();
454     delete writer;
455     file->close();
456     delete file;
457   }
458 }
459 #endif
460
461
462   
463
464 //____________________________________________________________________
465 // 
466 // EOF
467 //