In the reference section we will describe SpecTcl's event processing model in some detail. Here we are just going to see what is minimally needed to analyze a data set.
SpecTcl expects you to have supplied code that can take a raw event and
Decode it into a set of parameters.
Describe to the framework the number of bytes the event occupied.
Describe if the event was successfully processed or not
The glue-point for providing this code is a logical pipeline of
event processors. Event processors are instances
of a class that is derived from the CEventProcessor
class. The CEventProcessor base class provides
several virtual functions that you can override.
A minimal event processor looks like this
Example 7-1. Minimal SpecTcl Event Processor
Header file:
class MyEventProcessor : public CEventProcessor
{
public:
virtual Bool_t operator()(const Address_t pEvent,
CEvent& rEvent,
CAnalyzer& rAnalyzer,
CBufferDecoder& rDecoder);
};
Implementation file segment:
Bool_t
MyEventProcessor::operator()const Address_t pEvent,
CEvent& rEvent,
CAnalyzer& rAnalyzer,
CBufferDecoder& rDecoder)
{
TranslatorPointer<UShort_t> p(*(rDecoder.getBufferTranslator()), pEvent);
UShort_t nWords = *p++;
CTclAnalyzer& rAna((CTclAnalyzer&)rAnalyzer);
rAna.SetEventSize(nWords*sizeof(UShort_t)); // Set event size.
// Application specific unpacking code goes here...
…
// End of application specific unpacking code.
return kfTRUE;
}
Let's look at the callouts in the example above:

operator() function take as its
first parameter, an const Address_t, a generic
pointer. This generic pointer, named pEvent
in the example, points to the start of the event
operator() is being asked to analyze.

operator() member function is
expected to produce a set of parameters. If you are not using
the treeparameter package, this is done by reserving slots in
an array-like object called a CEvent.
Specific parameters will be stored in specific slots in that
array. The second parameter to operator()
is a reference to that array and has been called
rEvent.
Note that event processors may read previously unpacked
data from the rEvent object as well as,
or even instead of, processing the raw event.

NSCL data buffers include byte ordering information. Most data acquisition systems also have markers from which it is possible to deduce the byte ordering of the acquisition system. To do so is the job of an object called the buffer decoder.
A discussion of buffer decoders is beyond the scope of this
chapter. For now, all you need to know is that you can use
the buffer decoder passed to the event processor along with
a pointer to the raw event to construct a
TranslatorPointer object.
TranslatorPointer objects are pointer
like objects (objects that can, for all purposes be treated
like pointers), that know how to transform integer data in
the format of the buffer (acquisition system endian-ness) into
integer data of the format of the computer running the program.
Creating a
TranslatorPointer and using it
rather than a raw pointer, makes your event processor
independent of potential byte order differences between
the acquisition system and the system running SpecTcl.

The code shown informs SpecTcl of the event size by re-creating
the rAnalyzer parameter as a
CTclAnalyzer,
The actual type of the object that is managing the flow
of control for data analysis and then calling its
SetEventSize
method passing the number of bytes in
the event.


rEvent
will not increment any histograms.
![]() |
A common problem is to forget to return any value from
If you do this you may note that random events won't get processed. |
With this background we can see that the minimum set of things you need to do to get started with SpecTcl on a new data set is to:
Get a copy of the SpecTcl Skeleton.
Add a new event processor to the skeleton.
Build your customized SpecTcl
This section is necessarily somewhat NSCL dependent as we cannot know where and how SpecTcl is installed on systems at remote institutions. At the NSCL, SpecTcl is installed in /usr/opt/spectcl/version. Where version is the Spectcl version number (e.g. 3.2). In addition, /usr/opt/spectcl/current is a symbolic link that points to the version I recommend for use in new applications.
If these locations don't work for you, work with the people who installed SpecTcl at your institution to find out what should be substituted for /usr/opt/spectcl/current in the discussion below.
To get a copy of the SpecTcl Skeleton, execute the following commands:
In the Minimal SpecTcl Event Processor example, we showed what a minimal event processor looks like. We now need to add this to the skeleton SpecTcl that you have copied.
Adding the new event processor requires:
Creating the event processor header and implementation files.
Remove the registrations of the sample event processors from the SpecTcl event processing pipeline in MySpecTclApp.cpp
Adding code to the MySpecTclApp.cpp implementation file to creat an instance of your event processor and to add that instance to the event pipeline.
To promote re-use and modularity, I strongly suggest that you not add your event processor definitions to MySpecTclApp.cpp, but instead, create separate header and implementation files. Doing this will also help insulate you from changes to the MySpecTclApp.cpp file that may occur in future releases (I promise to try hard not to do this but if there is a compelling reason to, I will break that promise).
Using your favorite editor, create the header file for the event processor. For the sake of this example, we'll call the header MyEventProcessor.h.
Example 7-3. Example Header for minimal SpecTcl Event processor
// MyEventProcessor.h : #ifndef __MYEVENTPROCESSOR_H #define __MYEVENTPROCESSOR_H#include <EventProcessor.h>
class MyEventProcessor : public CEventProcessor
{ public: virtual Bool_t operator() (const Address_t, pEvent, CEvent& rEvent, CAnalyzer& rAnalyzer,
CBufferDecoder& rDecoder); }; #endif

Good programming practice requires include gaurds in all header files.

CEventProcessor
base class. You must include this header in order for
the compiler to know how to extend the base class with your
event processor class.

MyEventProcessor
class will be derived from the base class
CEventProcessor.
All event processors must be derived from that base class.

operator()
member function of the
CEventProcessor
base class to define our own functionality.
In this case, to produce our own set of parameters.
Following our Minimal SpecTcl Event Processor example, use your favorite editor to create the following implementation file:
Example 7-4. Implementation file for a minimal event processor
Bool_t
MyEventProcessor::operator()const Address_t pEvent,
CEvent& rEvent,
CAnalyzer& rAnalyzer,
CBufferDecoder& rDecoder)
{
TranslatorPointer<UShort_t> p(*(rDecoder.getBufferTranslator()), pEvent);
UShort_t nWords = *p++;
CTclAnalyzer& rAna((CTclAnalyzer&)rAnalyzer);
rAna.SetEventSize(nWords*sizeof(UShort_t)); // Set event size.
// Application specific unpacking code goes here...
// End of application specific unpacking code.
return kfTRUE;
}

p and
pull parameters out of that event into slots in the
rEvent array. In
SpecTcl at Run-Time
we will describe how to link those slots to SpecTcl parameters,
and define histograms on those parameters.
Now that you have created your event processor, you must get SpecTcl to use it. Doing this requires making an instance of the class (think of classes as data types, and instances as variables), and add that instance to the event pipeline.
To do that we must edit the file MySpecTclApp.cpp that was part of the SpecTcl skeleton files we copied in. First locate the top where headers are being included and add:
#include "MyEventProcessor.h"
Note that the header filname is in quotations not angle brackets.
Angle brackets only let the library directories be searched for
headers while quotations also search the current working directory.
Some developers prefer to isolate their header files in a separate include directory. If you do that You should use angle brackets and add that directory to the set of directories searched for library headers. In the next section we will point out how to do that.
Now that MySpecTclApp.cpp has been informed of the existence of our event processor we need to remove the registration of the sample event processors from MySpecTclApp.cpp and instantiate/register our event processor.
Locate the function CMySpecTclApp::CreateAnalysisPipeline.
Delete the body of that function and instead make it read:
RegisterEventProcessor(*(new MyEventProcessor), "Mine");
The RegisterEventProcessor member function
takes two parameters. The first is a reference to an intance of
an event processor (more precisely an instance of a class derived from
CEventProcessor). The second parameter
is optional and allows
you to associate a name with this event processor. Event processors
will be executed in the order in which they are registered, forming
a logical pipeline in which the results (rEvent) of
previous event processors are available to subsequent ones.
The construction *(new MyEventProcessor)
constructs a new instance of a MyEventProcessor
and passes a reference to it to RegisterEventProcessor.
While ordinarily, we'd have to worry about ensuring that the
event processor was destroyed after it was used, in this case, it is
likely the event processor will
live
for the lifetime of each run of SpecTcl so that's not a crucial
issue.
Building a customized SpecTcl means that we will have to
Edit the Makefile provided with the skeleton to make it compile your event processor.
Add any C++ compiler and linker flags needed to the Makefile
Run make to create your customized SpecTcl
Fix any errors and re-run make as needed until you get a clean build.
The SpecTcl makefile is organized so that in most cases you will not need to know much about Make to edit it. It does this by using make include files to get most of the muscle needed to compile and link programs to SpecTcl, and Makefile macro definitions to learn what has to be made. Let's have a look at the top part of the skeleton Makefile:
Example 7-5. The top part of the SpecTcl Skeleton Makefile
INSTDIR=/usr/opt/spectcl/3.2# Skeleton makefile for 3.1 include $(INSTDIR)/etc/SpecTcl_Makefile.include
# If you have any switches that need to be added to the default c++ compilation # rules, add them to the definition below: USERCXXFLAGS=
# If you have any switches you need to add to the default c compilation rules, # add them to the defintion below: USERCCFLAGS=$(USERCXXFLAGS) # If you have any switches you need to add to the link add them below: USERLDFLAGS=
# # Append your objects to the definitions below: # OBJECTS=MySpecTclApp.o
…


This separation has been accomplished by extracting the SpecTcl version specific information needed by make into include files. This line includes the top level of that include file hierarchy.

The compilation rules defined by these include files specify the compilation flags needed to locate the SpecTcl include files. The USERCXXFLAGS definition allows you to add to that set of flags.
Suppose, for example, you decided to put all of your header files in an include subdirectory to the source directory. You could define:
USERCXXFLAGS=Iinclude
to ensure that directory will be searched for headers.
The variable USERCCFLAGS allows you to define flags for C compilations. Usually these are the same as those required for C++ compilations, and the default skeleton Makefile therefore defines that variable to be the value of USERCXXFLAGS.

Suppose you have a library in ~/mylib named libmylib.a that is needed by your event processor. You could write:
USERLDFLAGS=-L~/mylib -lmylib
to incorporate that library in the link.

OBJECTS=MySpecTclApp.o MyEventProcessor.o
Once the makefile has been edited:
$ make
will build your tailored SpecTcl. The Makefile will produce
an executable image named SpecTcl in the same
directory as the Makefile.