This page contains and step by step instructions to use the standalone gain calibration software that we use for the current engineering prototype and inherits from the AHCAL HBU Electronic Bringup guide page.

This software is used to perform the gain calibration and the pedestal offset calibration of the detector when running in laboratory mode with forced trigger conditions (standard LED runs).

This code is no longer used as baseline code for the Engineering Prototype since 2016 since all the code is being exported to the main Calice software repository (see next bullet point) but is very useful for small setups or groups that begin to handle the AHCAL setup since it only needs a root installation (it has been tested with several root 5 versions) and is easily exportable.

The standalone code is divided in three main parts: a text to root file converter, the pedestal offset extractor and the gain calibrator. Is important to notice that this code is intended mainly to study standard LED calibration runs taken in Lab mode (forced trigger).

The only requirement to run it is to have root installed (the code is tested in latest versions of root 5) with enabled fftw3 libraries (see root installation instructions). and standard compilers (required by root, in any case).

To download it from the repository:

svn co YourDestinationFolderName

Convert Root

The data is saved, by the Labview programs that run the electronic setup, in ASCII format. This part of the code converts these files to more manageable root files,


without performing any event building (so, no sorting by BXID). If you want to analyze physics data (cosmic muon runs, radioactive source runs, test beam runs), you need to run the code in the convert_root_eventbuilding folder.


Instructions to run the code

Here are some step-by-step instruction to run the program:

1) Go to convert_root folder.

cd YourDestinationFolderName/convert_root

This folder contains two other folders: srd/ and Debug/. The first one contains the source files and the second one contains the executables and the makefile to produce them.

The ASCII files produced by the Labview do not contain any header giving any explanation of what is the format of the data, so you need to know it before hand. The data is usually delivered in two different formats, depending if the data is taking using the HBU DAQ or the HDMI DAQ connexion. For the HBU DAQ Labview, the text file contains 12 columns:

BunchXID, CycleNr, ChipID2, ASICNr, EvtNr, chn, TDC, ADC, xPos, yPos, Hit_Bit, Gain_Bit

Bunch crossing ID: clock count

Cycle Number: readout cycle (the readout continues until all memory cells are filled, then the data needs to be processed and another readout cycle starts)

ChipID2: identity of the chips, is not hardcoded in the chip itself but set in the slow control files (don't change it!)

ASICNr: Position of the asic in the HBU (0-3). Not used.

EvtNr: Memory cell.

chn: channel (0-35)

TDC: relative time of the hit within a BunchXID (uncalibrated)

ADC: charge, in ADC counts, collected by the channel/memory cell in one hit (uncalibrated)

xPos, yPos: position of the chip/channel, not filled.

Hit_Bit: 1 if the channel signal was over the threshold, 0 if not. In laboratory mode (forced trigger) is always 1.

Gain_Bit: 1 if the ADC is delivered in High_gain mode or 0 if is in low gain.

The data format which depends of your labview. For single HBU setup, the usual is to use the USB DAQ version. Here you have the two standard formats.

//sscanf(line.c_str(), "%i %i %i %i %i %i %i %i %i", &CycleNr, &BunchXID, &ChipID2, &EvtNr, &chn, &TDC, &ADC, &Hit_Bit, &Gain_Bit); for HDMI DAQ
sscanf(line.c_str(), "%i %i %i %i %i %i %i %i %i %i %i %i", &BunchXID, &CycleNr, &ChipID2, &ASICNr, &EvtNr, &chn, &TDC, &ADC, &xPos, &yPos, &Hit_Bit, &Gain_Bit); for USB DAQ

2) Compile the code:

cd YourDestinationFolderName/convert_root/Debug
make clean
make convert_root

3) Finally, you can run the program. It only needs two arguments: folder where the text files and the folder where the rootfiles will be (you need to create it).

./convert_root [INPUT FOLDER] [OUTPUT FOLDER][Data format: USB or HDMI]

Output data format

The output is a root file with TTree that contains an entry for each line of data (an entry for each channel that has been stored in the data file). In LED runs with forced trigger in high gain mode, the Hit_bit and Gain_Bit will be always 1 and all 16 memory cells are filled for all chips even though the memory cell 0 (16 if we use USB labview) is filled with zeros.

        int CycleNr = 0;
        int BunchXID = 0;
        int ChipID2 = 0;
        int ASICNr = 0;
        int EvtNr = 0;
        int chn = 0;
        int TDC = 0;
        int ADC = 0;
        int xPos = 0;
        int yPos = 0;
        int Hit_Bit = 0;
        int Gain_Bit = 0;

        TFile* output_tree_file = new TFile(output_file.c_str(),"RECREATE");
        TTree *myTree = new TTree ("tree", "tree");
        myTree->Branch ("CycleNr",&CycleNr,"CycleNr/I");
        myTree->Branch ("BunchXID",&BunchXID,"BunchXID/I");
        myTree->Branch ("ChipID2",&ChipID2,"ChipID2/I");
        myTree->Branch ("ASICNr",&ASICNr,"ASICNr/I");
        myTree->Branch ("EvtNr",&EvtNr,"EvtNr/I");
        myTree->Branch ("chn",&chn,"chn/I");
        myTree->Branch ("TDC",&TDC,"TDC/I");
        myTree->Branch ("ADC",&ADC,"ADC/I");
        myTree->Branch ("xPos",&xPos,"xPos/I");
        myTree->Branch ("yPos",&yPos,"yPos/I");
        myTree->Branch ("Hit_Bit",&Hit_Bit,"Hit_Bit/I");
        myTree->Branch ("Gain_Bit",&Gain_Bit,"Gain_Bit/I");

Pedestal Offset Memcell

This software is used to do the pedestal calibration for LED runs forced trigger mode.

The input for this program is a LED scan with 0 voltage. These kind of runs is what we call Pedestal Runs. For every channel, a specific distribution is saved: a gaussian-like distribution centered in 200-300 ADC ticks created mainly by dark rate and electronic noise. Here is an example:

It takes a pedestal data file (rootfile) as input and provide a .tsv file as output containing the Mean/RMS pedestal with all memory cells (from 1 to 16) as also the relative shift of memory cells compared to the 2nd memory, starting from 1, not from 0. For the data obtained with the USB DAQ, the memory cells order is inverted, so the offset is calculated with respect memory cell 15 (the previous to the last one).

Here is an example of the output file :


The pedestal position is defined as the mean position of this distribution and is calculated as the mean value of a gaussian fitted to the data. The pedestal position may be different for every channel/chip and also (this is very important) for every memory cell. The spread of the pedestal position (noise) among different memory cells in one specific channel affects to the data making wider the ADC distributions when we study all memory cell together (for example when performing gain calibration).

Instructions to run the code

cd YourDestinationFolderName/pedestal_memcell

First you will need to tell the program what is your input file (root file) and what is the output folder (where a text file with all pedestal offsets is written). These lines are in the YourDestinationFolderName/pedestal_memcell/src/pedestal_memcell.cpp file.

string PATH ="YourDestinationFolderName/../RootFiles_LED/fileLED000.root";
string PATH_OUT = "YourDestinationFolderName/../RootFiles_LED/output_pedestal/";

Then, compile the program:

cd YourDestinationFolderName/pedestal_memcell/Debug
make clean
make pedestal_memcell

and runt it:

./pedestal_memcell InputFilePath OutputDirectoryPath Flag (HG/LG(0) or HG/TDC(1))

The main output file (pedestal_offsets_in.tsv) looks like:

#pedestal positions & memory cell dependent offsets (tpedOffsetcellX = tpedOffsetcell2 - tpedOffsetcellX) from file "/home/airqui/AFS/HBU_setup_TokyoApril2016/Rootfiles/Vscan_first_LED0000_2016-04-04.root"
#format: the ordering of memory cells is inverted for DAQ HBU
#chip chn pedposall pedwidthall pedOffsetcell1 pedOffsetcell2 pedOffsetcell3 ... pedOffsetcell16"

193     0       230.633 6.03161 -5.33352        0       2.31612 5.63341 -1.19706        4.58673 -2.11284        2.01089 -4.77171        2.81487 -2.6499 4.12378 -1.35646        4.23672 -1.6403 231.128
193     1       233.762 5.81631 3.14922 0       3.71844 -9.13052        -0.212337       -1.65904        -5.77079        -1.0121 -0.200168       4.23524 -5.34121        -3.79015        -3.16202        -0.44851
        1.0539  232.799

A second file, Plots_comp.root, is saved. It contains the pedestal distributions for all channels, chips and memory cells.

Depending of your input data format, the memory cells are ordered in one way or the other. In LED forced trigger scans, the first memory cell is always zero (known bug of Spiroc 2b). If we using the USB Labview, the first memory cell filled with data is the last one sent to the PC, so is the last one in the output file shown above (you can see that last written memory cell has an offset of ~230). Using the HDMI Labview, the memory cell output is well sorted. Therefore, to calculate the offset, we have to chose as a reference one memory cell that always works, in this case memory cell 2nd (15th in USB labview) over 16th which is always filled.

Pedestal offset correction

cd YourDestinationFolderName/gain_ledcalib_ver2

The output file of the pedestal calculation, pedestal_offsets_in.tsv, is used to perform pedestal offset correction. This is very important, especially for the gain calibration measurements since we want to see the single pixel spectrum and measure the distance between pixels (in ADC). If the pedestal offset correction is not performed, the single pixel spectra peaks are wider since every memory cell noise average is a bit shifted with respect to the others. This shift is not always in the same direction.

Gain Led Calibration

This software is used to do the Gain calibration of the AHCAL. It takes rootfiles of the standard LED runs as input and the pedestal file (.tsv) in order to produce ADC Spectrums for each channels. Then it performs a MultiGaussian Fit on the Spectra.

For doing this we need to, previously, take several LED runs in forced trigger mode for several LED Voltages. The optimal voltage is, a priori, unknown and varies for every channel. One reasonable range is [3700-5000 mV] but some old boards require higher luminic power.

I explain here, briefly, the flow of the main program (gain_ledcalib.cpp):

Program flow

int VarToFill = ADC;
if (VarToFill > 0 && Gain_Bit == 1 ){
   if (takePedestal) {
      histos[thisRun->vCalib][ChipID2][chn]->Fill(VarToFill+b.getOffset(ChipID2, chn, EvtNr));
  } else

Where the offset correction is done memory cell by memory cell in this part:

TSpectrum * spectrum = new TSpectrum(5,1);//searching for peaks        spectrum->Search((*it)->histoAll,5,"",0.15);
Int_t nPeaks = spectrum->GetNPeaks();
cout << "nPeaks CheckSpectrum = " << nPeaks << endl;//Check if the initial spectrum is okay
bool SpectrumIsOK = CheckSpectrum(spectrum);

The TSpectrum class from root is able to study any spectrum and find the number of candidates to be peaks in the data (pseudo-peaks).

The CheckSpectrum function basically checks that we have a minimum number of pseudo-peaks needed for the gain calibration and that the pseudo-peaks are separated for similar distances. If this is the case, then a full fit to the data of a multigaussian is performed using the output information of the checks as input information to start the fit.

Another check that is done is the called "Mean Condition" which basically ask for ADC spectrums no saturated (mean < 1000-3000 ADCs depending of the ttiles/SiPMs/boards...)

The Fit is in the form :

mg += par[2*i+3]*TMath::Gaus(xx,gain*i+p0,par[2*i+4]);

par[2*i+3] is the normalisation of the peak i
gain is the distance between peaks -> knowledge input there (as we know that it should be like that)
p0 is the 1st peak position (pedestal)
par[2*i+4] is the width of the peak at position i

It also performs a FFT fit in order to get a comparison with another method.

Instructions to run the code

First we need to change few lines in src/gain_ledcalib.cpp to point to the right output folder and to right pedestal file. Even if you don't apply pedestal offset correction (takePedestal==true), you need a pedestal file to run the code. Lines to modify:

string PATH_PEDCALIB = "/home/airqui/AFS/HBU_setup_TokyoApril2016/output_pedestal_test0/pedestal_offsets_in.tsv";
string PATH_OUT = "/home/airqui/AFS/HBU_setup_TokyoApril2016/output_thresholdscan/";

The input file names need to have the LED voltage value in the name in order to read it and use it to create the output files. This is done in the GainRunFolderParser.h, in this line:

t = file.substr(file.find("mV")-4,4);

since the program is expecting a file name like *LEDXXXXmV*.root where XXXX is the LED voltage in mV.

Then, compile the program:

cd YourDestinationFolderName/gain_ledcalib_ver2/Debug
make clean
make gain_ledcalib

and run it:

./gain_ledcalib [INPUT ROOT FILE] y/n

The last argument is telling us whether we are running in HG/LG mode. In this mode, the ADC for the HighGain in the input root file is saved in the TDC branch.

Output files

The output consist in several files:

  1. TestXXXX.root --> contains results related to the quality of the fits

  2. HighSignal4700.root --> contains all the ADC histograms (for all channels and chips)

  3. Plot_compXXXX.root --> contains the fitted histograms and the histograms that satisfy some minimum requirements .

  4. gainfits_XXXX.tsv --> text file with the gain values. Five rows: Chip, channel, gain (multigaussian), gain (FFT), errorgain (FFT)

Where XXXX is the LED voltage in mV.

Example of results

Here we show an example of gain fit of the same channel and chip for several LED voltages (3800, 3900 and 4100 mV). We see how the increase of light intensity saturates the spectra. Notice that the x-scale of the last plot is different.

alt gain3800 alt gain3900 alt gain4100

HoldScan & ADC Distribution properties

Other tools are available in the package, for example:

Convert Root EventBuilding

When analyzing physics data is important to convert the text files into root file performing, at the same time, the Event Building. Due to the characteristics of the Spiroc readouts, the output is not classified by events (noise or real hit events) but by readout cycle. Let's, for the moment, consider that we only have one ASIC and that we are running in autotrigger mode:


Acquisition continuous until all memory cells are filled. Then BUSY signal is set and the detector goes blind for up to ~50 ms due to conversion and slow readout. This makes unpredictable the time between readout cycles.

Now, let's consider the more realistic case: we run several chips with the same DAQ interface boards (specifically with same DIF). In this case, the data packets come out-of-order making a bit more complicated the sorting of the events by bunch crossing id.

This is done by the convert_root_eventbuilding code.

To run it:

Compile the code:

cd YourDestinationFolderName/convert_root_eventbuilding/Debug
make clean
make all

Run the program. It only needs two arguments: folder where the text files and the folder where the rootfiles will be (you need to create it).

./convert_root [INPUT FOLDER] [OUTPUT FOLDER][Data format: USB or HDMI]

Output data format

 int nHits = 0;
 int iEvt = 0; //global event number , after sorting

  const static unsigned int MAXCELLS  = 10000; /*should be big enough for all detectors!*/

  int BunchXID[MAXCELLS];
  int CycleNr[MAXCELLS];
  int ChipID[MAXCELLS];
  int EvtNr[MAXCELLS];
  int Channel[MAXCELLS];
  int HitBit[MAXCELLS];
  int GainBit[MAXCELLS];

  TFile* output_tree_file = new TFile(output_file.c_str(),"RECREATE");

  TTree *myTree = new TTree ("myTree", "myTree");

  TString _prefix ="ahcal_";

  myTree->Branch( string(_prefix + "nHits").c_str(), &nHits,  string(_prefix+"nHits/I").c_str());
  myTree->Branch( string(_prefix+"iEvt").c_str(), &iEvt, string(_prefix+"iEvt/I").c_str());

  myTree->Branch( string(_prefix + "BunchXID").c_str(), &BunchXID, string(_prefix+"BunchXID["+_prefix+"nHits]/I").c_str() );
  myTree->Branch( string(_prefix + "CycleNr").c_str(), &CycleNr, string(_prefix+"CycleNr["+_prefix+"nHits]/I").c_str() );
  myTree->Branch( string(_prefix + "ChipID").c_str(), &ChipID, string(_prefix+"ChipID["+_prefix+"nHits]/I").c_str() );
  myTree->Branch( string(_prefix + "EvtNr").c_str(), &EvtNr, string(_prefix+"EvtNr["+_prefix+"nHits]/I").c_str() );
  myTree->Branch( string(_prefix + "Channel").c_str(), &Channel, string(_prefix+"Channel["+_prefix+"nHits]/I").c_str() );
  myTree->Branch( string(_prefix + "TDC").c_str(), &TDC, string(_prefix+"TDC["+_prefix+"nHits]/I").c_str() );
  myTree->Branch( string(_prefix + "ADC").c_str(), &ADC, string(_prefix+"ADC["+_prefix+"nHits]/I").c_str() );
  myTree->Branch( string(_prefix + "HitBit").c_str(), &HitBit, string(_prefix+"HitBit["+_prefix+"nHits]/I").c_str() );
  myTree->Branch( string(_prefix + "GainBit").c_str(), &GainBit, string(_prefix+"GainBit["+_prefix+"nHits]/I").c_str() );

The final tree contains one entry for every event (noise or real event). Most of the objects are vectors with a maximum size of nHits == total number of channels in our setup. Remember that our ASICs, when one single channel is over the threshold all channels are readout. To know if a channel was triggered or not (by autotrigger) you need to check the value of the Hitbit (==1 if autotriggered, ==0 if other channel was triggered).

Example: New readout scheme for scintillator tile with surface mounted SiPM (U. Tokyo)

We have checked this event builder using data taken in a special setup in University of Tokyo. In this setup, a new tile readout scheme, “four-corners readout”, is being studied.

Four corners

In difference with the standard configuration, in this setuo the SiPMs are located at vertices of scintillator tiles and detect scintillation light from four adjacent tiles. Tile hit is defined by taking a coincidence on four SiPMs belonging to the tile. SiPM charge is split to hit tiles if shared by adjacent hit tiles. For more information, please see these slides from W. Ootani in the Kyushu Calice Meeting.

The analysed data come from a setup that consist in a matrix of 3x3 tiles resting on the HBU. The central tile corners are on top of the following SiPMs (chip,channel) coordinates:

(193,0) (194,5) (195,35) (196,30)

The setup has been running for ~24h taking cosmic data (without any validation signal). A toy analysis, in the folder convert_root_eventbuilding/examples, shows the ADC spectra for one of these channels in different cases:

Cosmics Cosmics log

Temperature extraction

Temperature profile can be extracted from the TXT files by sed command

sed '/^# Date/!d; N;/# Temp/!d; s/\r\n/ /g'

, which extracts only temperature data and joins them with time information in one line. The result looks like following line

# Date / Time : 2016<94>N4<8C><8E>8<93><FA> 16:37:31 # Temperatures : 131.0E+0 27.4E+0 131.0E+0 26.0E+0 131.0E+0 26.0E+0

In Japan, unicode characters can be filtered out by following command:

sed 's/\x94N/-/;s/\x8c\x8e/-/;s/\x93\xfa /_/;s/# Date \/ Time : \(.*\) # Temperatures :/\1/'

This converts this line to nice format, which can be easily parsed for plotting:

2016-4-8_15:51:40 131.0E+0 27.7E+0 131.0E+0 26.3E+0 131.0E+0 26.3E+0

Simple gnu script for plotting the temperature profile:

#!/usr/bin/gnuplot -persist set xdata time set format x "%m-%d\n%H:%M" timedate set timefmt "%Y-%m-%d_%H:%M:%S" set title "Temperature during taking cosmics" set xlabel "time"; set ylabel "Temperature [Deg C]" set grid plot 'temp_all_ascii.txt' u 1:3 w lp title "T sensor 2" ps 0.5, '' u 1:5 w lp title "T sensor 4" ps 0.5, '' u 1:7 w lp title "T sensor 6" ps 0.5 


AHCALHBU2ElectronicBringupGuide/Analysis (last edited 2017-03-15 10:48:46 by AnnaRosmanitz)