Hooking the Event Processor Into SpecTclSetting Up SpecTclModifying the SpecTcl Skeleton FilesWriting a class derived from CEventProcessor

Writing a class derived from CEventProcessor

As with Readout, we will put our additional class code into separate files. This helps make our code re-usable and easily maintained. For example, if our event package is used in a larger experiment, we can just plug it in as an additional event processor.

In order to support this when analyzing events, we need to look at the entire event and ignore packets that are not intended for us. A high level flowchart of the unpacking function is shown below in the figure Unpacking Flowchart.

Note how we ignore packet types we don't recognize. This allows us to be inserted into the analysis pipeline without any knowledge of what the other elements of the pipeline are doing. This unpacker needs to have 32 elements of the CEvent array dedicated to it.

Unpacking flowchart
 

In this implementation, we will use a pair of constants, one for base parameter index of the parameters we unpack from the ADC and another for the TDC. The code for the class definition is in, MyEventProcessor.h shown below:

ifndef __MYEVENTPROCESSOR_H
#define __MYEVENTPROCESSOR_H
/*!
   Event unpacker for an experiment. The event structure is that
   of a documented packet with up to 16 subpackets.   The
   subpackets contain a phillips tdc and adc channel, and are
   also tagged with length and id.
*/

class MyEventProcessor : public CEventProcessor
{
public:
  virtual   Bool_t operator() (const Address_t pEvent, 
			       CEvent& rEvent, 
			       CAnalyzer& rAnalyzer, 
			       CBufferDecoder& rDecoder);
protected:
   void  UnpackPacket(TranslatorPointer<UShort_t> p, 
                      Cevent& rEvent);
 };


#endif

This header defines a class, MyEventProcessor, and the member function operator() that is called for each physics event. operator() must return a Bool_t (boolean) value. This return value should be kfTRUE if the unpacker succeeded, and kfFALSE if not. If the value returned is kfFALSE, SpecTcl will abandon processing the event. The unpacked parameters will not be histogrammed and no other event processors will be invoked. The function UnpackPacket is a helper function as we will see later.

The parameters passed to operator() are as follows:

Parameters passed to MyEventProcessor::operator()

Name Type Usage
pEvent Address_t Pointer to the raw event.
rEvent CEvent An array like object where parameters get stored
rAnalyzer CAnalyzer A reference to the analyzer.
rDecoder CBufferDecoder A reference to the buffer decoder.

Note that:

The main part of our work in the implementation is to copy selected data words from pEvent into specific elements of rEvent.

The implementation of MyEventProcessor::operator() is shown in the example below:

Bool_t
MyEventProcessor::operator()(const Address_t pEvent, 
			     CEvent& rEvent, 
			     CAnalyzer& rAnalyzer, 
			     CBufferDecoder& rDecoder)            [1]
{
  TranslatorPointer<UShort_t> p(*(rDecoder.getBuferTranslator()), [2]
                                                        pEvent);                     
  CtclAnalyzer&               rAna((CTclAnalyzer&)rAnalyzer);

  UShort_t     nWords = *p++;	// Word count.
  rAna.SetEventSize(nWords*sizeof(UShort_t));                     [3]
  nWords--;			// Already, we're past the event size.

  while(nWords) {
    UShort_t nPacketWords = *p;
    UShort_t nId    =  p[1];                                      [4]
    if(nId == nOurId) {		// This is our packet!!.
      UnpackPacket(p, rEvent);                                    [5]
    } 			
    p      += nPacketWords;                                       [6]
    nWords -= nPacketWords;
    }
  }
  
  return kfTRUE;                                                  [7]
} 

Refer to the numbers (like [1]) to match up comments below with corresponding code.

  1. Declares the function implementation. This declaration essentially matches that in the header file.
  2. Data in the buffer are stored in the binary representation of the system that does the readout. It's possible that you will analyze this data on a system with a different internal representation than the acquisition system. TranslatorPointers are pointer like objects that are capable of transparently translating from the event data representation to the data representation of the host system. I strongly suggest that you always access data in a data buffer through translating pointers to improve the portability of your software.
  3. The first word of the event is a self inclusive word count. At least one of the event processors in the processing pipeline must inform the analyzer of the event size. The analyzer uses this information to locate the start of the next event in the data buffer.
  4. Each event is broken up into packets. The first word of the packet is its length, the second word is an identifier that describes the type of data in the packet. Here we're getting the id of a packet and checking to see if is the one we know how to decode.
  5. If there's a match, we call the helper function UnpackPacket (described later) to unpack the event. UnpackPacket exists to help us manage complexity. This function is easy to understand, it looks at event packets, and decodes the ones it knows. UnpackPacket will be similarly easy to undestand as it has a very clearly defined, simple function. Program first for understandability. If you have performance problems, be sure you know where they are before you break the understandability in exchange for faster code.
  6. Whether the packet is recognized or not, this code points to the next packet and adjusts the number of remaining words. This business of silently ignoring data that is not meant for us is what allows our unpacker to be used in a pipeline of unpackers to process events that are composed of data from several apparatuses. Each unpacker knows how to handle its own data and ignores data destined for the others.
  7. Returning kfTRUE allows the data acquisition system to accept the event.

The implementation of UnpackPacket is shown below.

*!
   Unpack an event packet we recognize.  In a larger system
   there might be separate functions, or even separate classes
   for each packet recognized.  
    pEvent points to the packet. 
    The body of the packet contains 4 word sub packets that contain:

    +------------------------------+
    |            4                 |
    +------------------------------+
    | Channel number (0-15)        |                                    
    +------------------------------+                  [1]
    |     TDC Value                |
    +----------------------------- +
    |     ADC Value                |   
    +------------------------------+

*/
void
MyEventProcessor::UnpackPacket(TranslatorPointer<UShort_t> pEvent,
			      CEvent& rEvent)
{
  UInt_t nPacketSize = *pEvent;
  ++pEvent; ++pEvent;		// Skip header of packet.
  nPacketSize -= 2;		// Remaining number of words.

  while(nPacketSize) {                                [2]
    UInt_t nSubPacketSize = *pEvent;
    ++pEvent;
    Int_t  nChannel       = *pEvent;                  [3]
    ++pEvent;
    rEvent[nTDCBase + nChannel] = *pEvent;
    ++pEvent;                                                              ?
    rEvent[nADDBase + nChannel] = *pEvent;
    ++pEvent;
    nPacketSize -= nSubPacketSize;                     [4]
    
  }
  
}

Once more, the numbers in the comments below refer to the numbers like: [1] in the sample code.

  1. The comment header for the function clearly describes the format of the data packet. This can be invaluable when you look back on this code later on in your analysis. Note that the comments in this and other comment blocks in these examples are in doxygen format. Doxygen is a documentation tool that will automatically build either web or LaTeX based documentation of software from comment blocks within the software itself. a sample of doxygen web output is at: http://docs.nscl.msu.edu/daq/ProductionReadout/Doxygen/
  2. This loop iterates over all the words in the body, and operates in turn on each sub-packet of the packet. Notice that even though we know sub-packets are all 4 words, we use the sub-packet word count, rather than the hard-coded value of 4. This minimizes the changes required if additional words get added to the packet.
  3. The first data word of the sub packet is a channel number. The channel word is extracted since it determines, along with the appropriate base indices, which slots of the parameter array the data will be unpacked to.
  4. The code here takes the data words from the buffer and stores them in the appropriate slots in the event array.
  5. Finally this bit of book-keeping keeps track of the number of words remaining to be processed.

Report documentation errors to Ron Fox (fox@nscl.msu.edu)or NSCL's Bugzilla page

Hooking the Event Processor Into SpecTclSetting Up SpecTclModifying the SpecTcl Skeleton FilesWriting a class derived from CEventProcessor