ALIROOT-5433 Transition to CDHv3 in HLT
[u/mrichter/AliRoot.git] / HLT / RCU / AliHLTAltroGenerator.cxx
1 // $Id$
2
3 //**************************************************************************
4 //* This file is property of and copyright by the ALICE HLT Project        * 
5 //* ALICE Experiment at CERN, All rights reserved.                         *
6 //*                                                                        *
7 //* Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no>        *
8 //*                  for The ALICE HLT Project.                            *
9 //*                                                                        *
10 //* Permission to use, copy, modify and distribute this software and its   *
11 //* documentation strictly for non-commercial purposes is hereby granted   *
12 //* without fee, provided that the above copyright notice appears in all   *
13 //* copies and that both the copyright notice and this permission notice   *
14 //* appear in the supporting documentation. The authors make no claims     *
15 //* about the suitability of this software for any purpose. It is          *
16 //* provided "as is" without express or implied warranty.                  *
17 //**************************************************************************
18
19 /** @file   AliHLTAltroGenerator.cxx
20     @author Matthias Richter
21     @date   
22     @brief  Simulation class of 10/40bit Altro Data.
23 */
24
25 #include <cassert>
26 #include <cerrno>
27 #include "AliHLTAltroGenerator.h"
28 #include "TArrayS.h"
29 #include "TArrayC.h"
30 #include "TRandom.h"
31 #include "TDatime.h"
32 #include "AliRawDataHeaderV3.h"
33 #include "AliHLTAltroEncoder.h"
34
35 /** ROOT macro for the implementation of ROOT specific class methods */
36 ClassImp(AliHLTAltroGenerator)
37
38 AliHLTAltroGenerator::AliHLTAltroGenerator(int maxChannels,
39                                            int maxBunches,
40                                            int maxBunchLength,
41                                            int maxTimebin,
42                                            int maxSignal)
43   :
44   fpData(NULL),
45   fpSimData(NULL),
46   fChannelPositions(),
47   fNof10BitWords(0),
48   fpCDH(NULL),
49   fCDHSize(0),
50   fpTrailer(NULL),
51   fTrailerSize(0),
52   fMaxChannels(maxChannels),
53   fMaxBunches(maxBunches),
54   fMaxBunchLength(maxBunchLength),
55   fMaxTimebin(maxTimebin),
56   fMaxSignal(maxSignal),
57   fpRand(NULL),
58   fDirection(kBackwards),
59   fCurrentPosition(-1),
60   fCurrentBunch(-1),
61   fCurrentTimeOffset(-1)
62 {
63   // see header file for class documentation
64   // or
65   // refer to README to build package
66   // or
67   // visit http://web.ift.uib.no/~kjeks/doc/alice-hlt
68 }
69
70 AliHLTAltroGenerator::~AliHLTAltroGenerator()
71 {
72   // see header file for class documentation
73   if (fpTrailer) delete[] fpTrailer;
74   if (fpCDH) delete[] fpCDH;
75   if (fpSimData) delete fpSimData;
76   if (fpData) delete fpData;
77 }
78
79 int AliHLTAltroGenerator::Generate()
80 {
81   // see header file for class documentation
82   if (!fpSimData) fpSimData=new TArrayS;
83   if (!fpSimData) {
84     return -ENOMEM;
85   }
86
87   Reset();
88
89   int nofChannels=GetRandom(1, fMaxChannels);
90   if (nofChannels==0) nofChannels=1;
91
92   HLTDebug("number of channels: %d", nofChannels);
93   int channelAddress=-1;
94   int lastChannel=-1;
95   int dataPos=0;
96   int repetitions=0;
97   for (int channel=0; channel<nofChannels; channel++) {
98     channelAddress=GetRandom(0, fMaxChannels);
99     //HLTDebug("channel %d: address %d, %d bunch(es)", channel, channelAddress, nofBunches);
100     if (channelAddress==lastChannel) {
101       channel--;
102       if (repetitions++>5) break;
103       continue;
104     }
105     int nofBunches=GetRandom(1, fMaxBunches);
106     if (nofBunches==0) {
107       channel--;
108       if (repetitions++>5) break;
109       continue;
110     }
111     repetitions=0;
112     int totalBunches=0;
113     int bunchEndTime=0;
114     int lastBunchEndTime=0;
115
116     HLTDebug("simulate channel %d: address %d", channel, channelAddress);
117
118     // save beginning of this channel for better navigation
119     AliChannelPosition position={static_cast<AliHLTUInt16_t>(channelAddress), dataPos, 0};
120
121     // add channel address and bunch count at the beginning
122     if (fpSimData->GetSize()<dataPos+2) fpSimData->Set(dataPos+2);
123     (*fpSimData)[dataPos++]=channelAddress;
124     dataPos++; // placeholder for number of bunches
125
126     int bunch=0;
127
128     // Matthias Oct 2008:
129     // Data was simulated in the wrong order: bunches for the higher timebins
130     // first. But real data is the other way round: bunches are written in the order
131     // of ascending timebins. A new consistency check was added to AliAltroDecoder
132     // (AliAltroBunch) in revision 29090. Now the channels are checked for overlapping
133     // bunches, the time of a bunch must be smaller than time of the previous one
134     // minus its length.
135     // Data is now simulated in the right order in order to fullfil this check.
136     for (bunch=0; bunch<nofBunches && bunchEndTime<fMaxTimebin-3; bunch++) {
137       while ((bunchEndTime+=GetRandom(0, fMaxTimebin-bunchEndTime))-lastBunchEndTime<3) {/*empty body*/};
138       int bunchLength=GetRandom(0, bunchEndTime-lastBunchEndTime<fMaxBunchLength?bunchEndTime-lastBunchEndTime:fMaxBunchLength);
139       if (bunchLength==0) continue;
140       totalBunches++;
141
142       HLTDebug("       bunch %d, length %d, end time %d ", bunch, bunchLength, bunchEndTime);
143
144       if (fpSimData->GetSize()<dataPos+bunchLength+4) fpSimData->Set(dataPos+bunchLength+4);
145       // write bunch length and time at both ends
146       (*fpSimData)[dataPos++]=bunchLength;
147       int time=bunchEndTime-bunchLength+1;
148       (*fpSimData)[dataPos++]=time;
149       for (; time<=bunchEndTime; time++) {      
150         int signal=GetRandom(0, fMaxSignal);
151         (*fpSimData)[dataPos++]=signal;
152       }
153       (*fpSimData)[dataPos++]=bunchEndTime;
154       (*fpSimData)[dataPos++]=bunchLength;
155       fNof10BitWords+=bunchLength+2;
156       lastBunchEndTime=bunchEndTime;
157       bunchEndTime+=bunchLength;
158     }
159     if (totalBunches>0) {
160       (*fpSimData)[position.fPosition+1]=totalBunches;
161       if (fpSimData->GetSize()<dataPos+2) fpSimData->Set(dataPos+2);
162       (*fpSimData)[dataPos++]=totalBunches;
163       position.fEnd=dataPos;
164       (*fpSimData)[dataPos++]=channelAddress;
165       lastChannel=channelAddress;
166       fNof10BitWords=(fNof10BitWords+7)/4; fNof10BitWords*=4; // align to 4 and add 4
167       fChannelPositions.push_back(position);
168       assert((*fpSimData)[position.fPosition]==(*fpSimData)[position.fEnd]);
169       HLTDebug("       channel %d added: address %d, %d bunch(es)", channel, channelAddress, totalBunches);
170     } else {
171       dataPos-=2;
172       HLTDebug("       channel %d skipped: address %d, %d bunch(es)", channel, channelAddress, totalBunches);
173     }
174   }
175
176   assert(fNof10BitWords%4==0);
177   fpSimData->Set(dataPos);
178   return GetDataSize();
179 }
180
181 int AliHLTAltroGenerator::GetNof40BitAltroWords() const
182 {
183   // see header file for class documentation
184   assert(fNof10BitWords%4==0);
185   return fNof10BitWords/4;
186 }
187
188 int AliHLTAltroGenerator::GetDataSize()
189 {
190   // see header file for class documentation
191   int iResult=0;
192   iResult=(fNof10BitWords*5)/4;
193   if (fpTrailer) {
194     *(reinterpret_cast<AliHLTUInt32_t*>(fpTrailer))=GetNof40BitAltroWords();
195     iResult+=fTrailerSize;
196   }
197   if (fpCDH) iResult+=fCDHSize;
198   return iResult;
199 }
200
201 int AliHLTAltroGenerator::GetData(AliHLTUInt8_t* &pBuffer)
202 {
203   // see header file for class documentation
204   int iResult=GetDataSize();
205   if (iResult>0) {
206     if (!fpData) fpData=new TArrayC(iResult);
207     if (fpData) {
208       if (fpData->GetSize()<iResult) fpData->Set(iResult);
209       if ((iResult=GetData(reinterpret_cast<AliHLTUInt8_t*>(fpData->GetArray()), fpData->GetSize()))>=0) {
210         pBuffer=reinterpret_cast<AliHLTUInt8_t*>(fpData->GetArray());
211       }
212     } else {
213       iResult=-ENOMEM;
214     }
215   }
216   return iResult;
217 }
218
219 int AliHLTAltroGenerator::GetData(AliHLTUInt8_t* pBuffer, int size)
220 {
221   // see header file for class documentation
222   int iResult=0;
223   int dataPos=0;
224
225   if (size<GetDataSize()) return -ENOSPC;
226
227   // copy Common Data Header
228   if (fpCDH) {
229     fpCDH->fSize=GetDataSize();
230     memcpy(pBuffer+dataPos, fpCDH, fCDHSize);
231     dataPos+=fCDHSize;
232   }
233
234   // encode simulated data
235   if ((iResult=EncodeData(pBuffer+dataPos, size-dataPos))>=0) {
236     dataPos+=iResult;
237   }
238
239   // copy trailer
240   if (fpTrailer) {
241     memcpy(pBuffer+dataPos, fpTrailer, fTrailerSize);
242     AliHLTUInt32_t* pLast=reinterpret_cast<AliHLTUInt32_t*>(fpTrailer+fTrailerSize-sizeof(AliHLTUInt32_t));
243     *pLast=GetNof40BitAltroWords();
244     dataPos+=fTrailerSize;
245   }
246
247   if (iResult<0) return iResult;
248   assert(fpCDH==NULL || (int)fpCDH->fSize==dataPos);
249   return dataPos;
250 }
251
252 int AliHLTAltroGenerator::SetCDH(AliRawDataHeaderV3* pCDH, int size)
253 {
254   // see header file for class documentation
255   int iResult=0;
256   if (pCDH && size>0) {
257     if (fpCDH) delete[] fpCDH;
258     fpCDH=new AliRawDataHeaderV3;
259     if (fpCDH) {
260       memcpy(fpCDH, pCDH, size);
261       fCDHSize=size;
262     } else {
263       iResult=-ENOMEM;
264     }
265   } else {
266     iResult=-EINVAL;
267   }
268   return iResult;
269 }
270
271 int AliHLTAltroGenerator::SetRCUTrailer(AliHLTUInt8_t* pTrailer, int size)
272 {
273   // see header file for class documentation
274   int iResult=0;
275   if (pTrailer && size>=(int)sizeof(AliHLTUInt32_t)) {
276     AliHLTUInt32_t* pLast=reinterpret_cast<AliHLTUInt32_t*>(pTrailer+size-sizeof(AliHLTUInt32_t));
277     if (size!=sizeof(AliHLTUInt32_t)) {
278       // if more than one trailer words, the last one is the trailer length (# 32bit words)
279       if (*pLast!=size/sizeof(AliHLTUInt32_t)) {
280         HLTError("invalid trailer: trailer length (last 32bit word) does not match trailer size (bytes)");
281         return -EBADF;
282       }
283     }
284     if (fpTrailer) delete[] fpTrailer;
285     fpTrailer=new AliHLTUInt8_t[size];
286     if (fpTrailer) {
287       memcpy(fpTrailer, pTrailer, size);
288       fTrailerSize=size;
289     } else {
290       iResult=-ENOMEM;
291     }
292   } else {
293     iResult=-EINVAL;
294   }
295   return iResult;
296 }
297
298 int AliHLTAltroGenerator::GetChannels(vector<AliHLTUInt16_t> list)
299 {
300   // see header file for class documentation
301   int iResult=0;
302   list.clear();
303   for (vector<AliChannelPosition>::iterator element=fChannelPositions.begin();
304        element!=fChannelPositions.end();
305        element++) {
306     list.push_back(element->fChannel);
307   }
308   iResult=list.size();
309   return iResult;
310 }
311
312 int AliHLTAltroGenerator::SetSorting(AliHLTUInt16_t */*array*/, int /*arraySize*/)
313 {
314   // see header file for class documentation
315   int iResult=0;
316   HLTError("function not yet implemented");
317   return iResult;
318 }
319
320 int AliHLTAltroGenerator::EncodeData(AliHLTUInt8_t* pBuffer, int size)
321 {
322   // see header file for class documentation
323   int iResult=0;
324   if (!pBuffer) return -EINVAL;
325
326   AliHLTAltroEncoder encoder;
327   encoder.SetBuffer(pBuffer, size);
328
329   Short_t channelAddress=-1;
330   for (vector<AliChannelPosition>::iterator element=fChannelPositions.begin();
331        element!=fChannelPositions.end() && iResult>=0;
332        element++) {
333     if (!fpSimData ||
334         fpSimData->GetSize()<=element->fPosition ||
335         fNof10BitWords==0) {
336       iResult=-ENODATA;
337       break;
338     }
339     channelAddress=element->fChannel;
340     assert(fpSimData->At(element->fPosition)==channelAddress);
341     if (fpSimData->At(element->fPosition)!=channelAddress) {
342       iResult=-ENODATA;
343       break;
344     }
345     int dataPos=element->fPosition+1;
346     int nofBunches=fpSimData->At(dataPos++);
347     int bunch=0;
348     for (; bunch<nofBunches; bunch++) {
349       int bunchLength=fpSimData->At(dataPos++);
350       int startTime=fpSimData->At(dataPos++);
351       int time=startTime;
352       for (; time<startTime+bunchLength; time++) {
353         iResult=encoder.AddSignal(fpSimData->At(dataPos++), time);
354       }
355       assert(time-1==fpSimData->At(dataPos));
356       dataPos++; // DO NOT PUT INTO ASSERT
357       assert(bunchLength==fpSimData->At(dataPos));
358       dataPos++; // DO NOT PUT INTO ASSERT
359     }
360
361     if (iResult>=0 && channelAddress>=0) {
362       assert(nofBunches==fpSimData->At(dataPos));
363       dataPos++; // DO NOT PUT INTO ASSERT
364       assert(channelAddress==fpSimData->At(dataPos));
365       dataPos++; // DO NOT PUT INTO ASSERT
366     }
367     encoder.SetChannel(channelAddress);
368   }
369
370   if (iResult>=0) {
371     iResult=(encoder.GetTotal40bitWords()*5)/4;
372   }
373
374   return iResult;
375 }
376
377 int AliHLTAltroGenerator::GetRandom(int min, int max)
378 {
379   // see header file for class documentation
380   if (max-min<2) return min;
381   bool setTheSeed=fpRand!=NULL;
382   if (fpRand==NULL) {
383     fpRand=new TRandom;
384   }
385   if (fpRand==NULL) return min;
386   if (!setTheSeed) {
387     TDatime dt;
388     fpRand->SetSeed(dt.Get());
389   }
390   return fpRand->Integer(max-min);
391 }
392
393 int AliHLTAltroGenerator::Reset()
394 {
395   // see header file for class documentation
396   fChannelPositions.clear();
397   fNof10BitWords=0;
398   Rewind();
399   return 0;
400 }
401
402 int AliHLTAltroGenerator::Rewind()
403 {
404   // see header file for class documentation
405   fCurrentPosition=-1;
406   fCurrentBunch=-1;
407   fCurrentTimeOffset=-1;
408   return 0;
409 }
410
411 bool AliHLTAltroGenerator::Next()
412 {
413   // see header file for class documentation
414   bool haveData=false;
415   if (!fpSimData) return false;
416   do {
417     if ((haveData=(fCurrentTimeOffset>=0 &&
418                    fCurrentBunch>0 &&
419                    ++fCurrentTimeOffset<GetBunchSize()))) {
420       break;
421     }
422
423     if ((haveData=(NextBunch()) && GetBunchSize()>0)) {
424       fCurrentTimeOffset=0;
425       break;
426     }
427   } while (NextChannel());
428   return haveData;
429 }
430
431 AliHLTUInt16_t AliHLTAltroGenerator::GetSignal()
432 {
433   // see header file for class documentation
434   if (!fpSimData || fCurrentTimeOffset<0) return 0;
435   assert(fCurrentPosition>=0 && fCurrentPosition<(int)fChannelPositions.size());
436   assert(fCurrentBunch>=0);
437   if (fDirection==kForwards) {
438     return fpSimData->At(fCurrentBunch+2+fCurrentTimeOffset);
439   } else if (fDirection==kBackwards){
440     return fpSimData->At(fCurrentBunch-(2+fCurrentTimeOffset));
441   }
442   return 0;
443 }
444
445 bool AliHLTAltroGenerator::NextChannel()
446 {
447   // see header file for class documentation
448   bool haveData=false;
449   if (!fpSimData || fChannelPositions.size()==0) return false;
450   fpSimData->GetArray();
451   if (fCurrentPosition==-1) {
452     if (fDirection==kForwards) fCurrentPosition=0;
453     else fCurrentPosition=fChannelPositions.size()-1;
454     haveData=true;
455   } else {
456     if (fDirection==kForwards && (haveData=(fCurrentPosition+1<(int)fChannelPositions.size()))) {
457       fCurrentPosition++;
458     } else if (fDirection==kBackwards && (haveData=(fCurrentPosition>0))) {
459       fCurrentPosition--;
460     }
461   }
462   
463   fCurrentBunch=-1;
464   fCurrentTimeOffset=-1;
465   return haveData;
466 }
467
468 AliHLTUInt16_t AliHLTAltroGenerator::GetHwAddress()
469 {
470   // see header file for class documentation
471   if (fCurrentPosition>=0 && fCurrentPosition<(int)fChannelPositions.size())
472     return fChannelPositions[fCurrentPosition].fChannel;
473   return ~((AliHLTUInt16_t)0);
474 }
475
476 int AliHLTAltroGenerator::GetBunchCount()
477 {
478   // see header file for class documentation
479   if (fpSimData && fCurrentPosition>=0 && fCurrentPosition<(int)fChannelPositions.size()) {
480     if (fDirection==kForwards)
481       return fpSimData->At(fChannelPositions[fCurrentPosition].fPosition+1);
482     else if (fDirection==kBackwards)
483       return fpSimData->At(fChannelPositions[fCurrentPosition].fEnd-1);
484   }
485   return ~((AliHLTUInt16_t)0);
486 }
487
488 bool AliHLTAltroGenerator::NextBunch()
489 {
490   // see header file for class documentation
491   bool haveData=false;
492   if (fpSimData && fCurrentPosition>=0 && fCurrentPosition<(int)fChannelPositions.size()) {
493     if (fDirection==kBackwards) {
494       if (fCurrentBunch<0) {
495         // bunch count in channel end - 1
496         if ((haveData=(fpSimData->At(fChannelPositions[fCurrentPosition].fEnd-1))>0)) {
497           // first bunch length at channel end - 2
498           fCurrentBunch=fChannelPositions[fCurrentPosition].fEnd-2;
499         }
500       } else if (fCurrentBunch>fChannelPositions[fCurrentPosition].fPosition+1) {
501         fCurrentBunch-=fpSimData->At(fCurrentBunch)+4;
502         haveData=fCurrentBunch>fChannelPositions[fCurrentPosition].fPosition+1;
503       }
504       // cross check
505       if (haveData) {
506         assert(fpSimData->At(fCurrentBunch)==fpSimData->At(fCurrentBunch-fpSimData->At(fCurrentBunch)-3));
507         haveData=fpSimData->At(fCurrentBunch)==fpSimData->At(fCurrentBunch-fpSimData->At(fCurrentBunch)-3);
508       }
509     } else if (fDirection==kForwards) {
510       if (fCurrentBunch<0) {
511         // bunch count in channel start + 1
512         if ((haveData=(fpSimData->At(fChannelPositions[fCurrentPosition].fPosition+1))>0)) {
513           // first bunch length at channel start + 2
514           fCurrentBunch=fChannelPositions[fCurrentPosition].fPosition+2;
515         }
516       } else if (fCurrentBunch<fChannelPositions[fCurrentPosition].fEnd-1) {
517         fCurrentBunch+=fpSimData->At(fCurrentBunch)+4;
518         haveData=fCurrentBunch<fChannelPositions[fCurrentPosition].fEnd-1;
519       }
520       // cross check
521       if (haveData) {
522         assert(fpSimData->At(fCurrentBunch)==fpSimData->At(fCurrentBunch+fpSimData->At(fCurrentBunch)+3));
523         haveData=fpSimData->At(fCurrentBunch)==fpSimData->At(fCurrentBunch+fpSimData->At(fCurrentBunch)+3);
524       }
525     }
526   }
527   fCurrentTimeOffset=-1;
528   return haveData;
529 }
530
531 AliHLTUInt16_t AliHLTAltroGenerator::GetBunchSize()
532 {
533   // see header file for class documentation
534   if (fCurrentBunch<0) return 0;
535   if (fCurrentBunch<fChannelPositions[fCurrentPosition].fPosition+2) return 0;
536   if (fCurrentBunch>fChannelPositions[fCurrentPosition].fEnd-2) return 0;
537   return fpSimData->At(fCurrentBunch);
538 }
539
540 AliHLTUInt16_t AliHLTAltroGenerator::GetStartTime()
541 {
542   // see header file for class documentation
543   if (!fpSimData || GetBunchSize()==0) return 0;
544   AliHLTUInt16_t startTime=0;
545   if (fDirection==kForwards) {
546     startTime=fpSimData->At(fCurrentBunch+1);
547   } else if (fDirection==kBackwards) {
548     startTime=fpSimData->At(fCurrentBunch-fpSimData->At(fCurrentBunch)-2);
549   }
550   if (fCurrentTimeOffset>=0) {
551     if (fDirection==kForwards) {
552       startTime+=fCurrentTimeOffset;
553     } else if (fDirection==kBackwards) {
554       startTime=GetEndTime();
555     }
556   }
557   return startTime;
558 }
559
560 AliHLTUInt16_t AliHLTAltroGenerator::GetEndTime()
561 {
562   // see header file for class documentation
563   if (!fpSimData || GetBunchSize()==0) return 0;
564   AliHLTUInt16_t endTime=0;
565   if (fDirection==kForwards) {
566     endTime=fpSimData->At(fCurrentBunch+fpSimData->At(fCurrentBunch)+2);
567   } else if (fDirection==kBackwards) {
568     endTime=fpSimData->At(fCurrentBunch-1);
569   }
570   if (fCurrentTimeOffset>=0) {
571     assert(fCurrentTimeOffset<fpSimData->At(fCurrentBunch));
572     if (fDirection==kForwards) {
573       endTime=GetStartTime();
574     } else if (fDirection==kBackwards) {
575       endTime-=fCurrentTimeOffset;
576     }
577   }
578   return endTime;
579 }
580
581 const Short_t* AliHLTAltroGenerator::GetSignals()
582 {
583   // see header file for class documentation
584   if (!fpSimData || GetBunchSize()==0) return NULL;
585   if (fDirection==kForwards) {
586     return fpSimData->GetArray()+fCurrentBunch+2;
587   } else if (fDirection==kBackwards) {
588     return fpSimData->GetArray()+(fCurrentBunch-fpSimData->At(fCurrentBunch)-1);
589   }
590   return NULL;
591 }
592
593 void AliHLTAltroGenerator::Print()
594 {
595   // see header file for class documentation
596   cout << *this << endl;
597 }
598
599 ostream &operator<<(ostream &stream, AliHLTAltroGenerator &generator)
600 {
601   // see header file for class documentation
602   int iResult=0;
603   Short_t channelAddress=-1;
604   for (vector<AliHLTAltroGenerator::AliChannelPosition>::iterator element=generator.fChannelPositions.begin();
605        element!=generator.fChannelPositions.end() && iResult>=0;
606        element++) {
607     if (!generator.fpSimData ||
608         generator.fpSimData->GetSize()<=element->fPosition ||
609         generator.fNof10BitWords==0) {
610       stream << "AliHLTAltroGenerator: no data available" << endl;;
611       break;
612     }
613     channelAddress=element->fChannel;
614     assert(generator.fpSimData->At(element->fPosition)==channelAddress);
615     if (generator.fpSimData->At(element->fPosition)!=channelAddress) {
616       stream << "AliHLTAltroGenerator: internal data mismatch" << endl;;
617       iResult=-ENODATA;
618       break;
619     }
620     int dataPos=element->fPosition+1;
621     int nofBunches=generator.fpSimData->At(dataPos++);
622     stream << "***************************************************************" << endl;
623     stream << "channel address: " << channelAddress << "    " << nofBunches << " bunch(es)" << endl;
624     int bunch=0;
625     for (; bunch<nofBunches; bunch++) {
626       int bunchLength=generator.fpSimData->At(dataPos++);
627       int startTime=generator.fpSimData->At(dataPos++);
628       int time=startTime;
629       stream << "   length " << bunchLength << " start time " << startTime << ":     ";
630       for (; time<startTime+bunchLength; time++) {
631         stream << " " << generator.fpSimData->At(dataPos++);
632       }
633       assert(time-1==generator.fpSimData->At(dataPos));
634       dataPos++; // DO NOT PUT INTO ASSERT
635       assert(bunchLength==generator.fpSimData->At(dataPos));
636       dataPos++; // DO NOT PUT INTO ASSERT
637       stream << "      -> end time " << time-1 << endl;
638     }
639
640     if (iResult>=0 && channelAddress>=0) {
641       assert(nofBunches==generator.fpSimData->At(dataPos));
642       dataPos++; // DO NOT PUT INTO ASSERT
643       assert(channelAddress==generator.fpSimData->At(dataPos));
644       dataPos++; // DO NOT PUT INTO ASSERT
645     }
646   }
647
648   return stream;
649 }