3
\$\begingroup\$

Preliminaries

I have CAEN Digitizer N6720A and use WaveDump software for the data acquisition and recording. As a result of data recording this software produces a binary file in which the sequence of events is present. Each event has the following structure:

  1. Header of an event
  2. Event's data points

So the entire binary file looks like this:

<header><point><point><point>...
<header><point><point><point>...

Header and some auxiliary constants

namespace caen
{
    struct Header
    {
        uint32_t EVENT_SIZE;//header + data 
        uint32_t BOARD_ID;
        uint32_t PATTERN;//meaningful only for VME boards
        uint32_t CHANNEL;//channel number
        uint32_t EVENT_COUNTER;
        uint32_t TRIGGER_TIME_TAG;
    };

    typedef uint16_t ADC_SIZE;

    const double    SAMPLE_TIME = 4.0;//ns
    const unsigned  N_BITS      = 12;//
    const double    FSR         = 2.0;//V
    const double    LSB         = FSR / ( (1 << N_BITS) - 1 );//V
}

In order to read, parse and analyse data from such binaries I have written a small C++ library which I describe below (except analysis part).

Parser

There are two main classes which are taking part in reading and parsing binaries. Those are Event and Parser.

Event class

This class represents a single event. It stores the header and the data points of the event (and some additional info). Usually one needs only one instance of this class to exist.

Event.h

#ifndef EVENT_H
#define EVENT_H

#include <fstream>
#include <vector>
#include <cstddef>

#include "ADC.h"//Header and board specific constants


namespace caen
{
    class Parser;

    struct Point
    {
        //This structure represents one data point in a waveform
        double time;//real time (in nanosecs)
        double voltage;//real voltage (in volts)

        Point( double time, double voltage ) : time( time ), voltage( voltage ) { }
    };


    class Event
    {
        //This class represents a single event
        public :
            enum POLARITY { NEGATIVE, POSITIVE };


        protected :
            std::fstream::pos_type position;//position (in bytes) of this event in the entire binary file
            Header header; 
            std::vector<Point> points; 
            POLARITY polarity;


        public :
            typedef typename std::vector<Point>::const_iterator const_point_iterator;
            const_point_iterator cbegin() const { return points.cbegin(); }
            const_point_iterator cend() const { return points.cend(); }

            Event() :
                position( 0 ),
                polarity( NEGATIVE )
            { }

            void Clear()
            {
                position = 0;
                points.clear();
            }

            void SetPolarity( POLARITY pol ) { polarity = pol; }

            size_t GetSize() const { return points.size(); }
            double GetLength() const { return (points.back()).time; }
            POLARITY GetPolarity() const { return polarity; }

            void Print() const;

            friend class Parser;
    };
}
#endif

Parser class

This class does all the work in reading and parsing procedure. It has member ReadEvent which returns true if an event read successfully and false otherwise.

Parser::ReadEvent member

bool Parser::ReadEvent( Event& event )
{
    //clear vector of points and set event's position to 0
    event.Clear();

    std::ifstream file;
    file.exceptions( std::ios::failbit | std::ios::badbit );
    try
    {
        file.open( pathToFile, std::ios::binary );
        //start reading from the first position after the last event read
        file.seekg( currentPosition );
        //read event information first: board ID, trigger time tag, ...
        file.read( reinterpret_cast<char*>( &event.header ), sizeof( event.header ) ); 
        //dataSize is the size (in bytes) of a waveform = total size of event - size of header 
        uint32_t dataSize = (event.header.EVENT_SIZE - sizeof(event.header));
        //must contain whole number of points. 
        //ADC_SIZE is a typedef defined in <Device>.h
        if( !(dataSize % sizeof(ADC_SIZE)) )
        {
            //read wavefoem
            for( uint32_t i = 0; i < (dataSize / sizeof(ADC_SIZE)); i++ )
            {
                ADC_SIZE adcValue;
                file.read( reinterpret_cast<char*>( &adcValue ), sizeof( adcValue ) );
                event.points.push_back( { RealTime( i ), RealVoltage( adcValue ) } );
            }
            //update input position indicator
            //next event will be being read from this point 
            event.position = currentPosition;
            currentPosition = file.tellg();
            //Increment number of successfully read events
            nEvents++;
        }
        else//number of points is not a whole number
        {
            PrintWarning( "Data block seems to be corrupted. Number of points is not a whole number. Failure occurred reading an event at:" );
            PrintCurrentPosition();

            file.close();
            return false;
        }
        file.close();
    }
    catch( const std::ifstream::failure& e )
    {
        //make sure that before reaching the EOF
        //no data was read
        if( file.eof() and !file.gcount() )
        {
            PrintInfo( "EOF has been reached. All data read successfully." );
        }
        else
        {
            PrintWarning( "Data block seems to be corrupted. Couldn't open/read/close binary. Failure occurred reading an event at:" );
            PrintCurrentPosition();
        }
        return false;
    }
    return true;
}

The main idea behind the above member is to read the binary event by event. It is accomplished by having the Parser::currentPosition member data which stores the position of the next potential event. This approach allows a user to analyse event on-the-fly (see Usage section) while not keeping file open.

Usage

Usage is straightforward. One must instantiate Event and Parser objects and then call the Parser::ReadEvent member function to read an event:

#include "Parser.h"


int main()
{
    caen::Parser p( "path/to/file" );
    caen::Event e;
    //I usually need this software to record a lot
    //of waveforms for further analysis, for example
    //to have integral spectra
    while( p.ReadEvent( e ) )
    {
        //do something ...
    }

    return 0;
}

As always, any suggestions, critics and ideas are appreciated.

\$\endgroup\$
  • \$\begingroup\$ A definition of Event.Header would be very helpful. \$\endgroup\$ – vnp Jun 23 at 18:43
  • \$\begingroup\$ @vnp, I have added info about the header. \$\endgroup\$ – LRDPRDX Jun 23 at 18:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.