adding AliHLTAltroGenerator including unit test for simulation of Altro data and...
[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 "AliRawDataHeader.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   int iResult=0;
83
84   if (!fpSimData) fpSimData=new TArrayS;
85   if (!fpSimData) {
86     return -ENOMEM;
87   }
88
89   Reset();
90
91   int nofChannels=GetRandom(1, fMaxChannels);
92   if (nofChannels==0) nofChannels=1;
93
94   HLTDebug("number of channels: %d", nofChannels);
95   int channelAddress=-1;
96   int lastChannel=-1;
97   int dataPos=0;
98   int repetitions=0;
99   for (int channel=0; channel<nofChannels; channel++) {
100     channelAddress=GetRandom(0, fMaxChannels);
101     //HLTDebug("channel %d: address %d, %d bunch(es)", channel, channelAddress, nofBunches);
102     if (channelAddress==lastChannel) {
103       channel--;
104       if (repetitions++>5) break;
105       continue;
106     }
107     int nofBunches=GetRandom(1, fMaxBunches);
108     if (nofBunches==0) {
109       channel--;
110       if (repetitions++>5) break;
111       continue;
112     }
113     repetitions=0;
114     int totalBunches=0;
115     int bunchEndTime=fMaxTimebin;
116
117     HLTDebug("simulate channel %d: address %d", channel, channelAddress);
118
119     // save beginning of this channel for better navigation
120     AliChannelPosition position={channelAddress, dataPos, 0};
121
122     // add channel address and bunch count at the beginning
123     if (fpSimData->GetSize()<dataPos+2) fpSimData->Set(dataPos+2);
124     (*fpSimData)[dataPos++]=channelAddress;
125     dataPos++; // placeholder for number of bunches
126
127     int bunch=0;
128     for (bunch=0; bunch<nofBunches && bunchEndTime>0; bunch++) {
129       bunchEndTime=GetRandom(0, bunchEndTime-1);
130       int bunchLength=GetRandom(0, bunchEndTime<fMaxBunchLength?bunchEndTime:fMaxBunchLength);
131       if (bunchLength==0) continue;
132       totalBunches++;
133
134       HLTDebug("       bunch %d, length %d, end time %d ", bunch, bunchLength, bunchEndTime);
135
136       if (fpSimData->GetSize()<dataPos+bunchLength+4) fpSimData->Set(dataPos+bunchLength+4);
137       // write bunch length and time at both ends
138       (*fpSimData)[dataPos++]=bunchLength;
139       int time=bunchEndTime-bunchLength+1;
140       (*fpSimData)[dataPos++]=time;
141       for (; time<=bunchEndTime; time++) {      
142         int signal=GetRandom(0, fMaxSignal);
143         (*fpSimData)[dataPos++]=signal;
144       }
145       (*fpSimData)[dataPos++]=bunchEndTime;
146       (*fpSimData)[dataPos++]=bunchLength;
147       fNof10BitWords+=bunchLength+2;
148     }
149     if (totalBunches>0) {
150       (*fpSimData)[position.fPosition+1]=totalBunches;
151       if (fpSimData->GetSize()<dataPos+2) fpSimData->Set(dataPos+2);
152       (*fpSimData)[dataPos++]=totalBunches;
153       position.fEnd=dataPos;
154       (*fpSimData)[dataPos++]=channelAddress;
155       lastChannel=channelAddress;
156       fNof10BitWords=(fNof10BitWords+7)/4; fNof10BitWords*=4; // align to 4 and add 4
157       fChannelPositions.push_back(position);
158       assert((*fpSimData)[position.fPosition]==(*fpSimData)[position.fEnd]);
159       HLTDebug("       channel %d added: address %d, %d bunch(es)", channel, channelAddress, totalBunches);
160     } else {
161       dataPos-=2;
162       HLTDebug("       channel %d skipped: address %d, %d bunch(es)", channel, channelAddress, totalBunches);
163     }
164   }
165
166   assert(fNof10BitWords%4==0);
167   if (iResult<0) {
168     fpSimData->Set(0);
169     return iResult;
170   }
171   fpSimData->Set(dataPos);
172   return GetDataSize();
173 }
174
175 int AliHLTAltroGenerator::GetNof40BitAltroWords() const
176 {
177   // see header file for class documentation
178   assert(fNof10BitWords%4==0);
179   return fNof10BitWords/4;
180 }
181
182 int AliHLTAltroGenerator::GetDataSize()
183 {
184   // see header file for class documentation
185   int iResult=0;
186   iResult=(fNof10BitWords*5)/4;
187   if (fpTrailer) {
188     *(reinterpret_cast<AliHLTUInt32_t*>(fpTrailer))=GetNof40BitAltroWords();
189     iResult+=fTrailerSize;
190   }
191   if (fpCDH) iResult+=fCDHSize;
192   return iResult;
193 }
194
195 int AliHLTAltroGenerator::GetData(AliHLTUInt8_t* &pBuffer)
196 {
197   // see header file for class documentation
198   int iResult=GetDataSize();
199   if (iResult>0) {
200     if (!fpData) fpData=new TArrayC(iResult);
201     if (fpData) {
202       if (fpData->GetSize()<iResult) fpData->Set(iResult);
203       if ((iResult=GetData(reinterpret_cast<AliHLTUInt8_t*>(fpData->GetArray()), fpData->GetSize()))>=0) {
204         pBuffer=reinterpret_cast<AliHLTUInt8_t*>(fpData->GetArray());
205       }
206     } else {
207       iResult=-ENOMEM;
208     }
209   }
210   return iResult;
211 }
212
213 int AliHLTAltroGenerator::GetData(AliHLTUInt8_t* pBuffer, int size)
214 {
215   // see header file for class documentation
216   int iResult=0;
217   int dataPos=0;
218
219   if (size<GetDataSize()) return -ENOSPC;
220
221   // copy Common Data Header
222   if (fpCDH) {
223     fpCDH->fSize=GetDataSize();
224     memcpy(pBuffer+dataPos, fpCDH, fCDHSize);
225     dataPos+=fCDHSize;
226   }
227
228   // encode simulated data
229   if ((iResult=EncodeData(pBuffer+dataPos, size-dataPos))>=0) {
230     dataPos+=iResult;
231   }
232
233   // copy trailer
234   if (fpTrailer) {
235     memcpy(pBuffer+dataPos, fpTrailer, fTrailerSize);
236     AliHLTUInt32_t* pLast=reinterpret_cast<AliHLTUInt32_t*>(fpTrailer+fTrailerSize-sizeof(AliHLTUInt32_t));
237     *pLast=GetNof40BitAltroWords();
238     dataPos+=fTrailerSize;
239   }
240
241   if (iResult<0) return iResult;
242   assert(fpCDH==NULL || (int)fpCDH->fSize==dataPos);
243   return dataPos;
244 }
245
246 int AliHLTAltroGenerator::SetCDH(AliRawDataHeader* pCDH, int size)
247 {
248   // see header file for class documentation
249   int iResult=0;
250   if (pCDH && size>0) {
251     if (fpCDH) delete[] fpCDH;
252     fpCDH=new AliRawDataHeader;
253     if (fpCDH) {
254       memcpy(fpCDH, pCDH, size);
255       fCDHSize=size;
256     } else {
257       iResult=-ENOMEM;
258     }
259   } else {
260     iResult=-EINVAL;
261   }
262   return iResult;
263 }
264
265 int AliHLTAltroGenerator::SetRCUTrailer(AliHLTUInt8_t* pTrailer, int size)
266 {
267   // see header file for class documentation
268   int iResult=0;
269   if (pTrailer && size>=(int)sizeof(AliHLTUInt32_t)) {
270     AliHLTUInt32_t* pLast=reinterpret_cast<AliHLTUInt32_t*>(pTrailer+size-sizeof(AliHLTUInt32_t));
271     if (size!=sizeof(AliHLTUInt32_t)) {
272       // if more than one trailer words, the last one is the trailer length (# 32bit words)
273       if (*pLast!=size/sizeof(AliHLTUInt32_t)) {
274         HLTError("invalid trailer: trailer length (last 32bit word) does not match trailer size (bytes)");
275         return -EBADF;
276       }
277     }
278     if (fpTrailer) delete[] fpTrailer;
279     fpTrailer=new AliHLTUInt8_t[size];
280     if (fpTrailer) {
281       memcpy(fpTrailer, pTrailer, size);
282       fTrailerSize=size;
283     } else {
284       iResult=-ENOMEM;
285     }
286   } else {
287     iResult=-EINVAL;
288   }
289   return iResult;
290 }
291
292 int AliHLTAltroGenerator::GetChannels(vector<AliHLTUInt16_t> list)
293 {
294   // see header file for class documentation
295   int iResult=0;
296   list.clear();
297   for (vector<AliChannelPosition>::iterator element=fChannelPositions.begin();
298        element!=fChannelPositions.end();
299        element++) {
300     list.push_back(element->fChannel);
301   }
302   iResult=list.size();
303   return iResult;
304 }
305
306 int AliHLTAltroGenerator::SetSorting(AliHLTUInt16_t */*array*/, int /*arraySize*/)
307 {
308   // see header file for class documentation
309   int iResult=0;
310   HLTError("function not yet implemented");
311   return iResult;
312 }
313
314 int AliHLTAltroGenerator::EncodeData(AliHLTUInt8_t* pBuffer, int size)
315 {
316   // see header file for class documentation
317   int iResult=0;
318   if (!pBuffer) return -EINVAL;
319
320   AliHLTAltroEncoder encoder;
321   encoder.SetBuffer(pBuffer, size);
322
323   Short_t channelAddress=-1;
324   for (vector<AliChannelPosition>::iterator element=fChannelPositions.begin();
325        element!=fChannelPositions.end() && iResult>=0;
326        element++) {
327     if (!fpSimData ||
328         fpSimData->GetSize()<=element->fPosition ||
329         fNof10BitWords==0) {
330       iResult=-ENODATA;
331       break;
332     }
333     channelAddress=element->fChannel;
334     assert(fpSimData->At(element->fPosition)==channelAddress);
335     if (fpSimData->At(element->fPosition)!=channelAddress) {
336       iResult=-ENODATA;
337       break;
338     }
339     int dataPos=element->fPosition+1;
340     int nofBunches=fpSimData->At(dataPos++);
341     int bunch=0;
342     for (; bunch<nofBunches; bunch++) {
343       int bunchLength=fpSimData->At(dataPos++);
344       int startTime=fpSimData->At(dataPos++);
345       int time=startTime;
346       for (; time<startTime+bunchLength; time++) {
347         //iResult=encoder.AddSignal(fpSimData->At(dataPos++), time);
348         iResult=encoder.AddChannelSignal(fpSimData->At(dataPos++), time, channelAddress);
349       }
350       assert(time-1==fpSimData->At(dataPos));
351       dataPos++; // DO NOT PUT INTO ASSERT
352       assert(bunchLength==fpSimData->At(dataPos));
353       dataPos++; // DO NOT PUT INTO ASSERT
354     }
355
356     if (iResult>=0 && channelAddress>=0) {
357       assert(nofBunches==fpSimData->At(dataPos));
358       dataPos++; // DO NOT PUT INTO ASSERT
359       assert(channelAddress==fpSimData->At(dataPos));
360       dataPos++; // DO NOT PUT INTO ASSERT
361     }
362     //encoder.SetChannel(channelAddress);
363   }
364   if (iResult>=0 && channelAddress>=0) {
365     encoder.SetChannel(channelAddress);
366   }
367
368   if (iResult>=0) {
369     iResult=(encoder.GetTotal40bitWords()*5)/4;
370   }
371
372   return iResult;
373 }
374
375 int AliHLTAltroGenerator::GetRandom(int min, int max)
376 {
377   // see header file for class documentation
378   if (max-min<2) return min;
379   bool setTheSeed=fpRand!=NULL;
380   if (fpRand==NULL) {
381     fpRand=new TRandom;
382   }
383   if (fpRand==NULL) return min;
384   if (!setTheSeed) {
385     TDatime dt;
386     fpRand->SetSeed(dt.Get());
387   }
388   return fpRand->Integer(max-min);
389 }
390
391 int AliHLTAltroGenerator::Reset()
392 {
393   // see header file for class documentation
394   fChannelPositions.clear();
395   fNof10BitWords=0;
396   Rewind();
397   return 0;
398 }
399
400 int AliHLTAltroGenerator::Rewind()
401 {
402   // see header file for class documentation
403   fCurrentPosition=-1;
404   fCurrentBunch=-1;
405   fCurrentTimeOffset=-1;
406   return 0;
407 }
408
409 bool AliHLTAltroGenerator::Next()
410 {
411   // see header file for class documentation
412   bool haveData=false;
413   if (!fpSimData) return false;
414   do {
415     if (haveData=(fCurrentTimeOffset>=0 &&
416                   fCurrentBunch>0 &&
417                   ++fCurrentTimeOffset<GetBunchSize())) {
418       break;
419     }
420
421     if (haveData=(NextBunch() && GetBunchSize()>0)) {
422       fCurrentTimeOffset=0;
423       break;
424     }
425   } while (NextChannel());
426   return haveData;
427 }
428
429 AliHLTUInt16_t AliHLTAltroGenerator::GetSignal()
430 {
431   // see header file for class documentation
432   if (!fpSimData || fCurrentTimeOffset<0) return 0;
433   assert(fCurrentPosition>=0 && fCurrentPosition<(int)fChannelPositions.size());
434   assert(fCurrentBunch>=0);
435   if (fDirection==kForwards) {
436     return fpSimData->At(fCurrentBunch+2+fCurrentTimeOffset);
437   } else if (fDirection==kBackwards){
438     return fpSimData->At(fCurrentBunch-(2+fCurrentTimeOffset));
439   }
440   return 0;
441 }
442
443 bool AliHLTAltroGenerator::NextChannel()
444 {
445   // see header file for class documentation
446   bool haveData=false;
447   if (fpSimData && fChannelPositions.size()==0) return false;
448   fpSimData->GetArray();
449   if (fCurrentPosition==-1) {
450     if (fDirection==kForwards) fCurrentPosition=0;
451     else fCurrentPosition=fChannelPositions.size()-1;
452     haveData=true;
453   } else {
454     if (fDirection==kForwards && (haveData=(fCurrentPosition+1<(int)fChannelPositions.size()))) {
455       fCurrentPosition++;
456     } else if (fDirection==kBackwards && (haveData=(fCurrentPosition>0))) {
457       fCurrentPosition--;
458     }
459   }
460   
461   fCurrentBunch=-1;
462   fCurrentTimeOffset=-1;
463   return haveData;
464 }
465
466 AliHLTUInt16_t AliHLTAltroGenerator::GetHwAddress()
467 {
468   // see header file for class documentation
469   if (fCurrentPosition>=0 && fCurrentPosition<(int)fChannelPositions.size())
470     return fChannelPositions[fCurrentPosition].fChannel;
471   return ~((AliHLTUInt16_t)0);
472 }
473
474 int AliHLTAltroGenerator::GetBunchCount()
475 {
476   // see header file for class documentation
477   if (fpSimData && fCurrentPosition>=0 && fCurrentPosition<(int)fChannelPositions.size()) {
478     if (fDirection==kForwards)
479       return fpSimData->At(fChannelPositions[fCurrentPosition].fPosition+1);
480     else if (fDirection==kBackwards)
481       return fpSimData->At(fChannelPositions[fCurrentPosition].fEnd-1);
482   }
483   return ~((AliHLTUInt16_t)0);
484 }
485
486 bool AliHLTAltroGenerator::NextBunch()
487 {
488   // see header file for class documentation
489   bool haveData=false;
490   if (fpSimData && fCurrentPosition>=0 && fCurrentPosition<(int)fChannelPositions.size()) {
491     if (fDirection==kBackwards) {
492       if (fCurrentBunch<0) {
493         // bunch count in channel end - 1
494         if (haveData=(fpSimData->At(fChannelPositions[fCurrentPosition].fEnd-1))>0) {
495           // first bunch length at channel end - 2
496           fCurrentBunch=fChannelPositions[fCurrentPosition].fEnd-2;
497         }
498       } else if (fCurrentBunch>fChannelPositions[fCurrentPosition].fPosition+1) {
499         fCurrentBunch-=fpSimData->At(fCurrentBunch)+4;
500         haveData=fCurrentBunch>fChannelPositions[fCurrentPosition].fPosition+1;
501       }
502       // cross check
503       if (haveData) {
504         assert(fpSimData->At(fCurrentBunch)==fpSimData->At(fCurrentBunch-fpSimData->At(fCurrentBunch)-3));
505         haveData=fpSimData->At(fCurrentBunch)==fpSimData->At(fCurrentBunch-fpSimData->At(fCurrentBunch)-3);
506       }
507     } else if (fDirection==kForwards) {
508       if (fCurrentBunch<0) {
509         // bunch count in channel start + 1
510         if (haveData=(fpSimData->At(fChannelPositions[fCurrentPosition].fPosition+1))>0) {
511           // first bunch length at channel start + 2
512           fCurrentBunch=fChannelPositions[fCurrentPosition].fPosition+2;
513         }
514       } else if (fCurrentBunch<fChannelPositions[fCurrentPosition].fEnd-1) {
515         fCurrentBunch+=fpSimData->At(fCurrentBunch)+4;
516         haveData=fCurrentBunch<fChannelPositions[fCurrentPosition].fEnd-1;
517       }
518       // cross check
519       if (haveData) {
520         assert(fpSimData->At(fCurrentBunch)==fpSimData->At(fCurrentBunch+fpSimData->At(fCurrentBunch)+3));
521         haveData=fpSimData->At(fCurrentBunch)==fpSimData->At(fCurrentBunch+fpSimData->At(fCurrentBunch)+3);
522       }
523     }
524   }
525   fCurrentTimeOffset=-1;
526   return haveData;
527 }
528
529 AliHLTUInt16_t AliHLTAltroGenerator::GetBunchSize()
530 {
531   // see header file for class documentation
532   if (fCurrentBunch<0) return 0;
533   if (fCurrentBunch<fChannelPositions[fCurrentPosition].fPosition+2) return 0;
534   if (fCurrentBunch>fChannelPositions[fCurrentPosition].fEnd-2) return 0;
535   return fpSimData->At(fCurrentBunch);
536 }
537
538 AliHLTUInt16_t AliHLTAltroGenerator::GetStartTime()
539 {
540   // see header file for class documentation
541   if (!fpSimData || GetBunchSize()==0) return 0;
542   AliHLTUInt16_t startTime=0;
543   if (fDirection==kForwards) {
544     startTime=fpSimData->At(fCurrentBunch+1);
545   } else if (fDirection==kBackwards) {
546     startTime=fpSimData->At(fCurrentBunch-fpSimData->At(fCurrentBunch)-2);
547   }
548   if (fCurrentTimeOffset>=0) {
549     if (fDirection==kForwards) {
550       startTime+=fCurrentTimeOffset;
551     } else if (fDirection==kBackwards) {
552       startTime=GetEndTime();
553     }
554   }
555   return startTime;
556 }
557
558 AliHLTUInt16_t AliHLTAltroGenerator::GetEndTime()
559 {
560   // see header file for class documentation
561   if (!fpSimData || GetBunchSize()==0) return 0;
562   AliHLTUInt16_t endTime=0;
563   if (fDirection==kForwards) {
564     endTime=fpSimData->At(fCurrentBunch+fpSimData->At(fCurrentBunch)+2);
565   } else if (fDirection==kBackwards) {
566     endTime=fpSimData->At(fCurrentBunch-1);
567   }
568   if (fCurrentTimeOffset>=0) {
569     assert(fCurrentTimeOffset<fpSimData->At(fCurrentBunch));
570     if (fDirection==kForwards) {
571       endTime=GetStartTime();
572     } else if (fDirection==kBackwards) {
573       endTime-=fCurrentTimeOffset;
574     }
575   }
576   return endTime;
577 }
578
579 const Short_t* AliHLTAltroGenerator::GetSignals()
580 {
581   // see header file for class documentation
582   if (!fpSimData || GetBunchSize()==0) return NULL;
583   if (fDirection==kForwards) {
584     return fpSimData->GetArray()+fCurrentBunch+2;
585   } else if (fDirection==kBackwards) {
586     return fpSimData->GetArray()+(fCurrentBunch-fpSimData->At(fCurrentBunch)-1);
587   }
588   return NULL;
589 }
590
591 void AliHLTAltroGenerator::Print()
592 {
593   // see header file for class documentation
594   cout << *this << endl;
595 }
596
597 ostream &operator<<(ostream &stream, AliHLTAltroGenerator &generator)
598 {
599   // see header file for class documentation
600   int iResult=0;
601   Short_t channelAddress=-1;
602   for (vector<AliHLTAltroGenerator::AliChannelPosition>::iterator element=generator.fChannelPositions.begin();
603        element!=generator.fChannelPositions.end() && iResult>=0;
604        element++) {
605     if (!generator.fpSimData ||
606         generator.fpSimData->GetSize()<=element->fPosition ||
607         generator.fNof10BitWords==0) {
608       stream << "AliHLTAltroGenerator: no data available" << endl;;
609       break;
610     }
611     channelAddress=element->fChannel;
612     assert(generator.fpSimData->At(element->fPosition)==channelAddress);
613     if (generator.fpSimData->At(element->fPosition)!=channelAddress) {
614       stream << "AliHLTAltroGenerator: internal data mismatch" << endl;;
615       iResult=-ENODATA;
616       break;
617     }
618     int dataPos=element->fPosition+1;
619     int nofBunches=generator.fpSimData->At(dataPos++);
620     stream << "***************************************************************" << endl;
621     stream << "channel address: " << channelAddress << "    " << nofBunches << " bunch(es)" << endl;
622     int bunch=0;
623     for (; bunch<nofBunches; bunch++) {
624       int bunchLength=generator.fpSimData->At(dataPos++);
625       int startTime=generator.fpSimData->At(dataPos++);
626       int time=startTime;
627       stream << "   length " << bunchLength << " start time " << startTime << ":     ";
628       for (; time<startTime+bunchLength; time++) {
629         stream << " " << generator.fpSimData->At(dataPos++);
630       }
631       assert(time-1==generator.fpSimData->At(dataPos));
632       dataPos++; // DO NOT PUT INTO ASSERT
633       assert(bunchLength==generator.fpSimData->At(dataPos));
634       dataPos++; // DO NOT PUT INTO ASSERT
635       stream << "      -> end time " << time-1 << endl;
636     }
637
638     if (iResult>=0 && channelAddress>=0) {
639       assert(nofBunches==generator.fpSimData->At(dataPos));
640       dataPos++; // DO NOT PUT INTO ASSERT
641       assert(channelAddress==generator.fpSimData->At(dataPos));
642       dataPos++; // DO NOT PUT INTO ASSERT
643     }
644   }
645
646   return stream;
647 }