4.2. Modifications to skeleton.cpp

This section will provide a guide to the modifications you will need to make to a skeleton.cpp and files it depends on to be able to use it with the RingDaq readout framework.

The primary modifications are needed in readevt and the set of headers and stem from the fact that spectrodaq.h no longer exists as RingDaq by definition does not use spectrodaq, and the fact that therefore DAQWordBufferPtr objects also no longer exist. DAQWordBufferPtr objects are replaced by ordinary pointers to ordinary memory.

Since a DAQWordBufferPtr does a good job of imitating an ordinary pointer, these modifications should have minimal impact on your actual code.

I'm going to show two fairly empty readevt functions and what you have to do to them. The first is the 'standard' version, where data are taken into DAQWordBufferPtr objects directly, while the second uses the DAQWordBufferPtr::CopyIn function and a local buffer to improve the SPDAQ performance.

Let's look at the relevant pieces of the first case. Many of the standard comments have been removed for the sake of brevity, as is the standard body.

Example 4-3. A readout classic readevt


...
#include <spectrodaq.h>  (1)
...
#include <daqinterface.h> (2)
...
WORD
#ifdef __unix__
readevt (DAQWordBufferPtr& bufpt)  (3)
#else 
readevt (WORD* bufpt)
#endif
{
#ifdef __unix__
    DAQWordBufferPtr _sbufpt = bufpt; (4)
#else
    WORD *_sbufpt = bufpt;
#endif
    LOGICAL reject;

    reject   = FALSE;
    {
    // code here that invokes putbufw a bunch of times explicitly or
    // implicitly.
    ...
    }
    IF(reject) return 0;
#ifdef __unix__
    return bufpt.GetIndex() - _sbufpt.GetIndex();  (5)
#else
    return (bufpt - _sbufpt);
#endif
}


                
(1)
This line includes the spectrodaq.h header file. Among other things this defines DAQWordBufferPtr
(2)
The daqinterface.h provides a mini-api to the readout classic library. This also does not exist in RingDaq.
(3)
In __unix__ operating systems, readevt is passed a reference to a DAQWordBufferPtr. Data then get stored via that object.
(4)
In order to be able to compute and return the event size, the 'pointer' object is saved.
(5)
The DAQWordBufferPtr::GetIndex method returns the offset of the 'pointer' in the underlying buffer. This line therefore determines how many words have been read by readevt

In fact the non __unix__ version of readevt is actually almost correct for the RingDaq. Here's how this code fragment would be modified:

Example 4-4. skeleton.cpp modified for RingDaq


... (1)
WORD
readevt (WORD* bufpt)   (2)
{

    WORD *_sbufpt = bufpt;

    LOGICAL reject;

    reject   = FALSE;
    {    
     // readout code that uses putbufw etc.
     ...
    }

    IF(reject) return 0;

    return (bufpt - _sbufpt);  (3)
}

                
(1)
Both the spectrodaq.h and the daqinterface.h headers are no longer included.
(2)
The parameter signature of readevt has been modified to take a WORD* rather than a DAQWordPtr& paramter. putbufl and its compatriots are pre-processor macros that only require that the bufpt variable be in scope and have pointer-like semantics.
(3)
Ordinary pointer subtraction can now be used to compute the number of words that were put in the buffer.

Now lets look at the case where readevt reads data into a local buffer and then uses DAQWordBuferPtr::CopyIn to transfer it to the spectrodaq buffer. Typically readevt functions have the following form:

Example 4-5. Classic readout using CopyIn


                    
static WORD localBuffer[8192];   (1)                    
...
WORD
#ifdef __unix__
readevt (DAQWordBufferPtr& bufpt)
#else 
readevt (WORD* bufpt)
#endif
{
#ifdef __unix__
    DAQWordBufferPtr _sbufpt = bufpt;
#else
    WORD *_sbufpt = bufpt;
#endif
    LOGICAL reject;

    reject   = FALSE;
    {
        WORD* localbufpt = localbuffer;  (2)
    // code here that invokes localputbufw a bunch of times explicitly or
    // implicitly. putting data into localbuffer.
    ...
        int nWords = localbufpt - localBuffer;  (3)
        bufpt.CopyIn(localBuffer, 0, nWords);   (4)
        bufpt += nWords;                        (5)
    }

    
    IF(reject) return 0;
#ifdef __unix__
    return bufpt.GetIndex() - _sbufpt.GetIndex(); 
#else
    return (bufpt - _sbufpt);
#endif
}
                

Key features of this scheme are:

(1)
readevt functions that follow this pattern declare a local buffer into which data are first read.
(2)
A pointer is then created to that local buffer and replacements for putbufw usually named something like localputbufw are used to put data into this local buffer.
(3)
Once readout has been completed, ordinary pointer arithmetic is used to determine how many words have been loaded into the buffer.
(4)
The data read are then transfered to the spetrodaq buffer using the DAQWordBufferPtr::CopyIn method.
(5)
Finally the DAQWordBufferPtr object is advanced so that the size calculation done by the framework code is done correctly (or in some cases nWords is simply returned at that point).

With RingDaq, since readevt is recdeiving an ordinary pointer parameter, there are no efficiency gains to be had by using a local buffer. In order to avoid having to recast all the readout code in terms of e.g. putbufw the following trick can be used:

Example 4-6. Converting Classic readout with local buffers to RingDaq


(1)
#include <stdint.h>     (2)
...
readevt (uint16_t bufpt)
    WORD *_sbufpt = bufpt;
    LOGICAL reject;

    reject   = FALSE;
    {
        WORD* localbufpt = bufpt;    (3)
    // code here that invokes localputbufw a bunch of times explicitly or
    // implicitly. putting data into localbuffer.
    ...
        bufpt = localbuffer;   (4)
    }
    
    
    
    IF(reject) return 0;
    return (bufpt - _sbufpt);
}
                
(2)
This header defines integers of known bit widths such as uint16_t an unsigned integer that is guaranteed to occupy exactly 16 bits of storage.
(1)
As discussed before the example, we don't need the local buffer any more.
(3)
The localbufpt is just initialized to point to the buffer passed in to readevt this allows macros like localputbufw to function properly without source code modification.
(4)
Setting bufpt to the value of localbufpt is all that is then needed to make the size computation performed by the framework code function correctly.