Events and packets

This system deals exclusively with data in the so called "Phenix Raw Data Format" (PRDF). Internally, the data which make up one "Event" consist of packets, each of which contains the data from one (or more) pieces of hardware. The event structure wraps like an envelope around the collection of packets inside, and adds a header with envelope information about the event itself. The event structure encloses the packets which make up the event. The packets are the entities which contain the actual event data. An event typically has a large number of packets, although an "empty structure" with no packets at all is considered a legal event.

Each of those packets maps on to a piece of a given detector, such as a group of channels, or whatever connects to a given piece of hardware (typically a FEM board or a DCM). The packets can be considered independent sub-entities within the whole event, and make the event data more modular. In particular,

In a fictitious example, let's say that we want to get at the ADC value of a particular module of the lead glass detector. Let's assume that the 2x3 rows of upper-left Supermodules (144 channels) are connected to a FEM which generates a packet with id 8002. The way the Supermodules were connected is that our module in the 2x3 area is the 57th channel (counting from 0 here). So in order to get at the value of that channel, we have to know the packet id (8002) from a database, and the offset of our channel (57) within these 144 channels read out through this FEM. If this cabling arrangement is never changed, we will always find this channel as channel number 57 within packet 8002. This reflects the way the detector have been connected to the readout electronics.

The way to get at channel number 57 once you get hold of the packet, however, is handled entirely by the Event handling system. All information needed to decode the data within the packet, the way to get at the value of channel 57, all the internals, are stored within the packet header, so the event handling system can deal with this on its own. You (the analysis software) only need to supply the packet id and offset value, the rest is invisible to your analysis code.

This is a very important feature of the system. It will shield your analysis software from the internals of the data storage, so it will allow changes in the data acquisition without ever affecting the analysis software. The analysis software will continue to get the value of our channel 57 from packet 8002, no matter how the data are stored inside the packet. Without recompilation, and in most cases without relinking, it will pick up new code to unpack the data even if the compression algorithm used was not known when the analysis code was compiled. Let's assume that in 1999, the way to encode the data is the so-called "pass-through" mode, that is, essentially no compression or even zero-suppression is performed. For this particular way of "encoding" the data, there is a particular algorithm to decode the data, which will be identified by a particular entry in the packet header. The software looks at this value, decides which decoding algorithm to use, and will deliver the value of channel 57 to the analysis software. At some point we will switch to "mode 1" encoding, some zero-suppression algorithm to compress the data. Again, the appropriate algorithm will be identified in the header, and another algorithm will be selected to decode the data and deliver the value of channel 57 to us. In the future, we might find a way to compress the data even more, and yet another decoding algorithm will be available to decode the data from that particular encoding scheme. At no point the particular encoding algorithm is exposed to the analysis software, which will not depend on it and will continue to function in the same way indefinitely.

Of course, in the process of debugging the data acquisition and analysis software, the programmer will want to know the details of how the data are stored. While the system will shield the analysis production software from those internals and prevent it from depending on the implementation details, it will also help the code developer to access the internal data in various ways for debugging purposes. There is a rich set of debugging-style functions, which display the internal data in various ways, display "envelope information" about the data structures (by that we typically mean the contents of the headers), copy the data to some user array to look at it in more detail, and so on.

Before going any further, let's look at an example which decodes the packet 8002 as described before. It is important to distinguish between the actual event or packet data, which are a stream of bits and bytes, and the Event and Packet objects, which you can think of as the maintainer or manager of the event or packet data. Most of the time we will deal with the objects only, and will ask them questions and ask them to perform services for us.

#include <iostream.h>
#include "Event.h"
#include "packet.h"
void channel57( Event *evt)
{
   // the Event object creates a Packet object
   // and returns a pointer to the Packet object to us. 
   Packet *p = evt->getPacket(8002);
   // we ask it now to get channel 57 for us
   int channel57 = p->iValue(57);
   cout << "Channel 57 is " << channel57 << endl;
   delete p;   // we are done with p and delete it again.
}

We don't worry for now about where the Event object came from initially and how we got such a thing. We assume that we get it as a parameter to our "channel57" function for now. We then ask the Event object to return us a pointer to a Packet object which manages the data of the packet with id 8002. The event objects locates the data of the packet, creates an object to manage them and returns the pointer. We then ask this Packet object to please return to us the value of channel 57 through its "iValue" function.

This iValue member function of the Packet class is just one way to get at the decoded data. rValue will return the value as a float value, and then there are several ways to get all channels in one call. For now we will continue with the "one channel at a time" iValue function.

By the way, we delete the Packet object p because it was created in our routine and we are done with it. Different from the Event object, which is maintained outside of our routine, this object is known only inside, and we have to dispose of it before we return.

You may wonder what happens if there is no packet 8002 in that event. Well, in the example above our routine would crash, because the Event object would returns 0. We have to test for a non-zero value to make sure that we actually got such a Packet object delivered.

#include <iostream.h>
#include "Event.h"
#include "packet.h"
int channel57( Event *evt)
{
   // the Event object creates a Packet object
   // and returns a pointer to the Packet object to us. 
   Packet *p = evt->getPacket(8002);
   if (p) 
      {
        // yes, we got the packet, and we ask it now to get channel 57 for us
        int channel57 = p->iValue(57);
        cout << "Channel 57 is " << channel57 << endl;
        delete p;
        return 0;
      }
   return -1;
}
Here we test if p is not 0, and get and print out the value of channel 57. We delete p again, and return 0 from our function to indicate that everything went ok. If we don't get the Packet object, we return -1 to signal an error to the calling routine.

Alphabetic index Hierarchy of classes



This page was generated with the help of DOC++.