New RAW I/O. I rolled my own, because I wasn't happy with the old
[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
16 /* $Id$ */
17
18 //____________________________________________________________________
19 //
20 // Class to write ADC values to a raw data file
21 //
22 // This class writes FMD Raw data to a file.   The sample rate (number
23 // of times the ALTRO ADC samples each pre-amp. channel - that is,
24 // data from a single strip), can be set via SetSampleRate. 
25 //
26 // Zero-suppression can be enabled by calling SetThreshold with a
27 // non-zero argument.   ADC values less than the value set will not be
28 // written to output.   Note, that if you use zero-suppression, you
29 // need to explicitly set the sample rate when reading back the data
30 // with AliFMDRawReader. 
31 // 
32 // This class uses the AliAltroBuffer class to write the data in the
33 // ALTRO format.  See the Exec member function for more information on
34 // that format.  
35 //
36 #include <AliLog.h>             // ALILOG_H
37 #include <AliLoader.h>          // ALILOADER_H
38 #include <AliAltroBuffer.h>     // ALIALTROBUFFER_H
39 #include "AliFMD.h"             // ALIFMD_H
40 #include "AliFMDParameters.h"   // ALIFMDPARAMETERS_H
41 #include "AliFMDDigit.h"        // ALIFMDDIGIT_H
42 #include "AliFMDRawWriter.h"    // ALIFMDRAWREADER_H 
43 #include "AliFMDAltroMapping.h" // ALIFMDALTROMAPPING_H
44 #include "AliFMDAltroIO.h"      // ALIFMDALTROWRITER_H
45 #include <TArrayI.h>            // ROOT_TArrayI
46 #include <TClonesArray.h>       // ROOT_TClonesArray
47 #include <fstream>
48
49 //____________________________________________________________________
50 ClassImp(AliFMDRawWriter)
51 #if 0
52   ; // This is here to keep Emacs for indenting the next line
53 #endif
54
55 //____________________________________________________________________
56 AliFMDRawWriter::AliFMDRawWriter(AliFMD* fmd) 
57   : TTask("FMDRawWriter", "Writer of Raw ADC values from the FMD"),
58     fFMD(fmd)
59 {
60   AliFMDParameters* pars = AliFMDParameters::Instance();
61   fSampleRate            = pars->GetSampleRate(AliFMDParameters::kBaseDDL);
62   fChannelsPerAltro      = pars->GetChannelsPerAltro();
63 }
64
65
66 //____________________________________________________________________
67 void
68 AliFMDRawWriter::Exec(Option_t*) 
69 {
70   // Turn digits into raw data. 
71   // 
72   // Digits are read from the Digit branch, and processed to make
73   // three DDL files, one for each of the sub-detectors FMD1, FMD2,
74   // and FMD3. 
75   //
76   // The raw data files consists of a header, followed by ALTRO
77   // formatted blocks.  
78   // 
79   //          +-------------+
80   //          | Header      |
81   //          +-------------+
82   //          | ALTRO Block |
83   //          | ...         |
84   //          +-------------+
85   //          DDL file 
86   // 
87   // An ALTRO formatted block, in the FMD context, consists of a
88   // number of counts followed by a trailer. 
89   // 
90   //          +------------------+
91   //          | Count            |
92   //          | ...              |
93   //          | possible fillers |
94   //          +------------------+
95   //          | Trailer          |
96   //          +------------------+
97   //          ALTRO block 
98   // 
99   // The counts are listed backwards, that is, starting with the
100   // latest count, and ending in the first. 
101   // 
102   // Each count consist of 1 or more ADC samples of the VA1_ALICE
103   // pre-amp. signal.  Just how many samples are used depends on
104   // whether the ALTRO over samples the pre-amp.  Each sample is a
105   // 10-bit word, and the samples are grouped into 40-bit blocks 
106   //
107   //          +------------------------------------+
108   //          |  S(n)   | S(n-1) | S(n-2) | S(n-3) |
109   //          |  ...    | ...    | ...    | ...    |
110   //          |  S(2)   | S(1)   | AA     | AA     |
111   //          +------------------------------------+
112   //          Counts + possible filler 
113   //
114   // The trailer of the number of words of signales, the starting
115   // strip number, the sector number, and the ring ID; each 10-bit
116   // words,  packed into 40-bits. 
117   // 
118   //          +------------------------------------+
119   //          | # words | start  | sector | ring   |
120   //          +------------------------------------+
121   //          Trailer
122   // 
123   // Note, that this method assumes that the digits are ordered. 
124   // 
125   AliLoader* loader = fFMD->GetLoader();
126   loader->LoadDigits();
127   TTree* digitTree = loader->TreeD();
128   if (!digitTree) {
129     Error("Digits2Raw", "no digit tree");
130     return;
131   }
132   
133   TClonesArray* digits = new TClonesArray("AliFMDDigit", 1000);
134   fFMD->SetTreeAddress();
135   TBranch* digitBranch = digitTree->GetBranch(fFMD->GetName());
136   if (!digitBranch) {
137     Error("Digits2Raw", "no branch for %s", fFMD->GetName());
138     return;
139   }
140   digitBranch->SetAddress(&digits);
141   
142   Int_t nEvents = Int_t(digitTree->GetEntries());
143   for (Int_t event = 0; event < nEvents; event++) {
144     fFMD->ResetDigits();
145     digitTree->GetEvent(event);
146     
147     // Write out the digits
148     WriteDigits(digits);
149   }
150   loader->UnloadDigits();
151 }
152
153 //____________________________________________________________________
154 void
155 AliFMDRawWriter::WriteDigits(TClonesArray* digits)
156 {
157   Int_t nDigits = digits->GetEntries();
158   if (nDigits < 1) return;
159
160   AliFMDParameters*  pars    = AliFMDParameters::Instance();
161   AliFMDAltroWriter* writer  = 0;
162   Int_t          sampleRate  = -1;
163   UShort_t       hwaddr      = 0;
164   UShort_t       ddl         = 0;
165   std::ofstream* file        = 0;
166   Int_t          ret         = 0;
167   for (Int_t i = 0; i < nDigits; i++) {
168     // Get the digit
169     AliFMDDigit* digit = static_cast<AliFMDDigit*>(digits->At(i));
170     UInt_t   thisDDL, thisHwaddr;
171     UShort_t det    = digit->Detector();
172     Char_t   ring   = digit->Ring();
173     UShort_t sector = digit->Sector();
174     UShort_t strip  = digit->Strip();
175     if (!pars->Detector2Hardware(det,ring,sector,strip,thisDDL,thisHwaddr)) {
176       AliError(Form("Failed to get hardware address for FMD%d%c[%2d,%3d]",
177                     det, ring, sector, strip));
178       continue;
179     }
180     AliDebug(40, Form("Got DDL=%d and address=%d from FMD%d%c[%2d,%3d]", 
181                      thisDDL, thisHwaddr, det, ring, sector, strip));
182     // Check if we're still in the same channel
183     if (thisHwaddr != hwaddr) {
184       AliDebug(30, Form("Now hardware address 0x%x from FMD%d%c[%2d,%3d] "
185                        "(board 0x%x, chip 0x%x, channel 0x%x)",
186                        thisHwaddr, det, ring, sector, strip, 
187                        (thisHwaddr >> 7), (thisHwaddr >> 4) & 0x7, 
188                        thisHwaddr & 0xf));
189       if (writer) writer->AddChannelTrailer(hwaddr);
190       hwaddr = thisHwaddr;
191     }
192     // Check if we're still in the same detector (DDL)
193     if (ddl != thisDDL) {
194       if (writer) {
195         AliDebug(1, Form("Closing altro writer %p", writer));
196         if ((ret = writer->Close()) < 0) {
197           AliError(Form("Error: %s", writer->ErrorString(ret)));
198           return;
199         }
200         delete writer;
201         writer = 0;
202         file->close();
203         delete file;
204         file = 0;
205       }
206       ddl = thisDDL;
207     }
208     // If we haven't got a writer (either because none were made so
209     // far, or because we've switch DDL), make one. 
210     if (!writer) {
211       AliDebug(1, Form("Opening new ALTRO writer w/file FMD_%d.ddl", ddl));
212       file   = new std::ofstream(Form("FMD_%d.ddl", ddl));
213       if (!file || !*file) {
214         AliFatal(Form("Failed to open file FMD_%d.ddl", ddl));
215         return;
216       }
217       writer  = new AliFMDAltroWriter(*file);
218       writer->SetThreshold(pars->GetZeroSuppression(det, ring, sector, strip));
219       sampleRate = pars->GetSampleRate(ddl);
220     }
221     // Write out our signal
222     writer->AddSignal(digit->Count1());
223     if (sampleRate >= 2) writer->AddSignal(digit->Count2());
224     if (sampleRate >= 3) writer->AddSignal(digit->Count3());
225   }
226   if (writer) {
227     writer->AddChannelTrailer(hwaddr);
228     writer->Close();
229     delete writer;
230     file->close();
231     delete file;
232   }
233 }
234
235       
236     
237 #if 0
238 //____________________________________________________________________
239 void
240 AliFMDRawWriter::WriteDigits(TClonesArray* digits)
241 {
242   Int_t nDigits = digits->GetEntries();
243   if (nDigits < 1) return;
244
245   AliFMDParameters* pars = AliFMDParameters::Instance();
246   UShort_t prevDetector = 0;
247   Char_t   prevRing     = '\0';
248   UShort_t prevSector   = 0;
249   // UShort_t prevStrip    = 0;
250   
251   // The first seen strip number for a channel 
252   UShort_t startStrip   = 0;
253   
254   // Which channel number in the ALTRO channel we're at 
255   UShort_t offset       = 0;
256   
257   // How many times the ALTRO Samples one VA1_ALICE channel 
258   Int_t sampleRate = 1;
259   
260   // A buffer to hold 1 ALTRO channel - Normally, one ALTRO channel
261   // holds 128 VA1_ALICE channels, sampled at a rate of `sampleRate' 
262   TArrayI channel(fChannelsPerAltro * sampleRate);
263
264   // The Altro buffer 
265   AliAltroBuffer* altro = 0;
266     
267   // Loop over the digits in the event.  Note, that we assume the
268   // the digits are in order in the branch.   If they were not, we'd
269   // have to cache all channels before we could write the data to
270   // the ALTRO buffer, or we'd have to set up a map of the digits. 
271   for (Int_t i = 0; i < nDigits; i++) {
272     // Get the digit
273     AliFMDDigit* digit = static_cast<AliFMDDigit*>(digits->At(i));
274
275     UShort_t det    = digit->Detector();
276     Char_t   ring   = digit->Ring();
277     UShort_t sector = digit->Sector();
278     UShort_t strip  = digit->Strip();
279     fThreshold      = pars->GetZeroSuppression(det, ring, sector, strip);
280     if (det != prevDetector) {
281       AliDebug(15, Form("FMD: New DDL, was %d, now %d",
282                         AliFMDParameters::kBaseDDL + prevDetector - 1,
283                         AliFMDParameters::kBaseDDL + det - 1));
284       // If an altro exists, delete the object, flushing the data to
285       // disk, and closing the file. 
286       if (altro) { 
287         // When the first argument is false, we write the real
288         // header. 
289         AliDebug(15, Form("New altro: Write channel at %d Strip: %d "
290                           "Sector: %d  Ring: %d", 
291                           i, startStrip, prevSector, prevRing));
292         // TPC to FMD translations 
293         // 
294         //    TPC                FMD
295         //    ----------+-----------
296         //    pad       |      strip
297         //    row       |     sector
298         //    sector    |       ring
299         // 
300         WriteChannel(altro, startStrip, prevSector, prevRing, channel);
301         altro->Flush();
302         altro->WriteDataHeader(kFALSE, kFALSE);
303         delete altro;
304         altro = 0;
305       }
306       prevDetector = det;
307       // Need to open a new DDL! 
308       Int_t ddlId = AliFMDParameters::kBaseDDL + det - 1;
309       TString filename(Form("%s_%d.ddl", fFMD->GetName(),  ddlId));
310       
311       AliDebug(15, Form("New altro buffer with DDL file %s", 
312                         filename.Data()));
313       AliDebug(15, Form("New altro at %d", i));
314       // Create a new altro buffer - a `1' as the second argument
315       // means `write mode' 
316       altro = new AliAltroBuffer(filename.Data(), 1);
317       altro->SetMapping(pars->GetAltroMap());
318       
319       // Write a dummy (first argument is true) header to the DDL
320       // file - later on, when we close the file, we write the real
321       // header
322       altro->WriteDataHeader(kTRUE, kFALSE);
323       
324       // Figure out the sample rate 
325       if (fSampleRate > 0) sampleRate = fSampleRate;
326       else {
327         if (digit->Count2() >= 0) sampleRate = 2;
328         if (digit->Count3() >= 0) sampleRate = 3;
329       }
330
331       channel.Set(fChannelsPerAltro * sampleRate);
332       offset     = 0;
333       prevRing   = ring;
334       prevSector = sector;
335       startStrip = strip;
336     }
337     else if (offset == fChannelsPerAltro
338              || digit->Ring() != prevRing 
339              || digit->Sector() != prevSector) {
340       // Force a new Altro channel
341       AliDebug(15, Form("Flushing channel to disk because %s",
342                         (offset == fChannelsPerAltro ? "channel is full" :
343                          (ring != prevRing ? "new ring up" :
344                           "new sector up"))));
345       AliDebug(15, Form("New Channel: Write channel at %d Strip: %d "
346                         "Sector: %d  Ring: %d", 
347                         i, startStrip, prevSector, prevRing));
348       WriteChannel(altro, startStrip, prevSector, prevRing, channel);
349       // Reset and update channel variables 
350       channel.Reset(0);
351       offset     = 0; 
352       startStrip = strip;
353       prevRing   = ring;
354       prevSector = sector;
355     }
356     
357     // Store the counts of the ADC in the channel buffer 
358     channel[offset * sampleRate] = digit->Count1();
359     if (sampleRate > 1) 
360       channel[offset * sampleRate + 1] = digit->Count2();
361     if (sampleRate > 2) 
362       channel[offset * sampleRate + 2] = digit->Count3();
363     offset++;
364   }
365   // Finally, we need to close the final ALTRO buffer if it wasn't
366   // already 
367   if (altro) {
368     altro->Flush();
369     altro->WriteDataHeader(kFALSE, kFALSE);
370     delete altro;
371   }
372 }
373 #endif
374
375 //____________________________________________________________________
376 void
377 AliFMDRawWriter::WriteChannel(AliAltroBuffer* altro, 
378                               UShort_t strip, UShort_t sector, Char_t ring, 
379                               const TArrayI& data) 
380 {
381   // Write out one ALTRO channel to the data file. 
382   // Derived classes can overload this method to use a per-ALTRO
383   // threshold.   This implementation uses the common threshold set by
384   // SetThreshold. 
385   altro->WriteChannel(Int_t(strip), 
386                       Int_t(sector), 
387                       Int_t((ring == 'I' ? 0 : 1)), 
388                       data.fN, data.fArray, fThreshold);
389 }
390
391   
392
393 //____________________________________________________________________
394 // 
395 // EOF
396 //