Skip to content

Commit 33e2fd0

Browse files
committed
TPC: Processing of common mode values in O2
1 parent 095ccb4 commit 33e2fd0

File tree

13 files changed

+2006
-2
lines changed

13 files changed

+2006
-2
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// @file CMV.h
13+
/// @author Tuba Gündem, tuba.gundem@cern.ch
14+
/// @brief Common mode values data format definition
15+
16+
/// The data is sent by the CRU as 256+16 bit words. The CMV data layout is as follows:
17+
/// - 256-bit Header: [version:8][packetID:8][errorCode:8][magicWord:8][heartbeatOrbit:32][heartbeatBC:16][padding:176]
18+
/// - 16-bit CMV value: [CMV:16]
19+
20+
#ifndef ALICEO2_DATAFORMATSTPC_CMV_H
21+
#define ALICEO2_DATAFORMATSTPC_CMV_H
22+
23+
#include <bitset>
24+
25+
namespace o2::tpc::cmv
26+
{
27+
28+
static constexpr uint32_t NTimeBinsPerPacket = 3564; ///< number of time bins (covering 8 heartbeats)
29+
static constexpr uint32_t NPacketsPerTFPerCRU = 4; ///< 4 packets per timeframe
30+
static constexpr uint32_t NTimeBinsPerTF = NTimeBinsPerPacket * NPacketsPerTFPerCRU; ///< maximum number of timebins per timeframe (14256)
31+
static constexpr uint32_t SignificantBits = 2; ///< number of bits used for floating point precision
32+
static constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float
33+
34+
/// Header definition of the CMVs
35+
struct Header {
36+
static constexpr uint8_t MagicWord = 0xDC;
37+
union {
38+
uint32_t word0 = 0; ///< bits 0 - 31
39+
struct {
40+
uint8_t version : 8; ///< version
41+
uint8_t packetID : 8; ///< packet id
42+
uint8_t errorCode : 8; ///< errors
43+
uint8_t magicWord : 8; ///< magic word
44+
};
45+
};
46+
union {
47+
uint32_t word1 = 0; ///< bits 32 - 63
48+
struct {
49+
uint32_t heartbeatOrbit : 32; ///< first heart beat timing of the package
50+
};
51+
};
52+
union {
53+
uint16_t word2 = 0; ///< bits 64 - 79
54+
struct {
55+
uint16_t heartbeatBC : 16; ///< first BC id of the package
56+
};
57+
};
58+
union {
59+
uint16_t word3 = 0; ///< bits 80 - 95
60+
struct {
61+
uint16_t unused1 : 16; ///< reserved
62+
};
63+
};
64+
union {
65+
uint32_t word4 = 0; ///< bits 96 - 127
66+
struct {
67+
uint32_t unused2 : 32; ///< reserved
68+
};
69+
};
70+
union {
71+
uint64_t word5 = 0; ///< bits 128 - 191
72+
struct {
73+
uint64_t unused3 : 64; ///< reserved
74+
};
75+
};
76+
union {
77+
uint64_t word6 = 0; ///< bits 192 - 255
78+
struct {
79+
uint64_t unused4 : 64; ///< reserved
80+
};
81+
};
82+
};
83+
84+
/// CMV single data container
85+
struct Data {
86+
uint16_t CMV{0}; ///< 16bit ADC value
87+
88+
// Raw integer accessors
89+
uint16_t getCMV() const { return CMV; }
90+
void setCMV(uint16_t value) { CMV = value; }
91+
92+
// Float helpers using SignificantBits for fixed-point conversion
93+
float getCMVFloat() const { return static_cast<float>(CMV) * FloatConversion; }
94+
void setCMVFloat(float value)
95+
{
96+
// round to nearest representable fixed-point value
97+
setCMV(uint32_t((value + 0.5f * FloatConversion) / FloatConversion));
98+
}
99+
};
100+
101+
/// CMV full data container: one packet carries NTimeBinsPerPacket
102+
struct Container {
103+
Header header; ///< CMV data header
104+
Data data[NTimeBinsPerPacket]; ///< data values
105+
106+
// Header and data accessors
107+
const Header& getHeader() const { return header; }
108+
Header& getHeader() { return header; }
109+
110+
const Data* getData() const { return data; }
111+
Data* getData() { return data; }
112+
113+
// Per-time-bin CMV accessors
114+
uint16_t getCMV(uint32_t timeBin) const { return data[timeBin].getCMV(); }
115+
void setCMV(uint32_t timeBin, uint16_t value) { data[timeBin].setCMV(value); }
116+
117+
float getCMVFloat(uint32_t timeBin) const { return data[timeBin].getCMVFloat(); }
118+
void setCMVFloat(uint32_t timeBin, float value) { data[timeBin].setCMVFloat(value); }
119+
};
120+
121+
} // namespace o2::tpc::cmv
122+
123+
#endif

Detectors/TPC/base/include/TPCBase/RDHUtils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#define AliceO2_TPC_RDHUtils_H
1414

1515
#include "DetectorsRaw/RDHUtils.h"
16-
//#include "Headers/RAWDataHeader.h"
16+
// #include "Headers/RAWDataHeader.h"
1717

1818
namespace o2
1919
{
@@ -28,6 +28,7 @@ static constexpr FEEIDType UserLogicLinkID = 15; ///< virtual link ID for ZS dat
2828
static constexpr FEEIDType IDCLinkID = 20; ///< Identifier for integrated digital currents
2929
static constexpr FEEIDType ILBZSLinkID = 21; ///< Identifier for improved link-based ZS
3030
static constexpr FEEIDType DLBZSLinkID = 22; ///< Identifier for dense link-based ZS
31+
static constexpr FEEIDType CMVLinkID = 23; ///< Identifier for common mode values
3132
static constexpr FEEIDType SACLinkID = 25; ///< Identifier for sampled analog currents
3233

3334
/// compose feeid from cru, endpoint and link
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// @file CMVContainer.h
13+
/// @author Tuba Gündem, tuba.gundem@cern.ch
14+
/// @brief Struct for storing CMVs to the CCDB
15+
16+
#ifndef ALICEO2_TPC_CMVCONTAINER_H_
17+
#define ALICEO2_TPC_CMVCONTAINER_H_
18+
19+
#include <vector>
20+
#include <string>
21+
#include <memory>
22+
#include <stdexcept>
23+
24+
#include <fmt/format.h>
25+
26+
#include "TTree.h"
27+
#include "TFile.h"
28+
29+
#include "DataFormatsTPC/CMV.h"
30+
31+
namespace o2::tpc
32+
{
33+
34+
/// CMVContainer: accumulator for one aggregation interval
35+
struct CMVContainer {
36+
37+
uint32_t nTFs{0}; ///< number of TFs accumulated
38+
uint32_t nCRUs{0}; ///< number of contributing CRUs
39+
long firstTF{0}; ///< first TF counter in this aggregation interval
40+
41+
std::vector<float> cmvValues{}; ///< CMV float values
42+
std::vector<uint32_t> cru{}; ///< CRU indices
43+
std::vector<uint32_t> timebin{}; ///< absolute timebins within the TF
44+
std::vector<uint32_t> tf{}; ///< TF counters
45+
46+
/// Pre-allocate storage for the expected number of entries: expectedTFs × expectedCRUs × NTimeBinsPerTF
47+
void reserve(uint32_t expectedTFs, uint32_t expectedCRUs)
48+
{
49+
const std::size_t n = static_cast<std::size_t>(expectedTFs) * expectedCRUs * o2::tpc::cmv::NTimeBinsPerTF;
50+
cmvValues.reserve(n);
51+
cru.reserve(n);
52+
timebin.reserve(n);
53+
tf.reserve(n);
54+
}
55+
56+
/// Append one (cmv, cru, timebin, tf) tuple
57+
void addEntry(float cmvVal, uint32_t cruID, uint32_t tb, uint32_t tfCounter)
58+
{
59+
cmvValues.push_back(cmvVal);
60+
cru.push_back(cruID);
61+
timebin.push_back(tb);
62+
tf.push_back(tfCounter);
63+
}
64+
65+
/// Append one full CRU packet (NTimeBinsPerPacket consecutive timebins)
66+
/// \param packet pointer to NTimeBinsPerPacket floats
67+
/// \param cruID CRU index
68+
/// \param tbOffset absolute timebin of the first sample in this packet
69+
/// \param tfCounter TF counter
70+
void addPacket(const float* packet, uint32_t cruID, uint32_t tbOffset, uint32_t tfCounter)
71+
{
72+
for (uint32_t tb = 0; tb < o2::tpc::cmv::NTimeBinsPerPacket; ++tb) {
73+
addEntry(packet[tb], cruID, tbOffset + tb, tfCounter);
74+
}
75+
}
76+
77+
std::size_t size() const { return cmvValues.size(); }
78+
bool empty() const { return cmvValues.empty(); }
79+
80+
/// Clear all data and reset counters
81+
void clear()
82+
{
83+
cmvValues.clear();
84+
cru.clear();
85+
timebin.clear();
86+
tf.clear();
87+
nTFs = 0;
88+
nCRUs = 0;
89+
firstTF = 0;
90+
}
91+
92+
std::string summary() const
93+
{
94+
return fmt::format("CMVContainer: {} entries, {} TFs, {} CRUs, firstTF={}",
95+
size(), nTFs, nCRUs, firstTF);
96+
}
97+
98+
/// Build an in-memory TTree with one branch per field and one entry per tuple
99+
std::unique_ptr<TTree> toTTree() const
100+
{
101+
const std::size_t n = size();
102+
if (n == 0) {
103+
throw std::runtime_error("CMVContainer::toTTree() called on empty container");
104+
}
105+
106+
auto tree = std::make_unique<TTree>("CMVTree", "TPC common mode values");
107+
tree->SetAutoSave(0);
108+
109+
// Point branches directly at the vector data — single Fill() call writes all rows
110+
float* pCmv = const_cast<float*>(cmvValues.data());
111+
uint32_t* pCru = const_cast<uint32_t*>(cru.data());
112+
uint32_t* pTimebin = const_cast<uint32_t*>(timebin.data());
113+
uint32_t* pTf = const_cast<uint32_t*>(tf.data());
114+
115+
tree->Branch("cmv", pCmv, fmt::format("cmv[{}]/F", n).c_str());
116+
tree->Branch("cru", pCru, fmt::format("cru[{}]/i", n).c_str());
117+
tree->Branch("timebin", pTimebin, fmt::format("timebin[{}]/i", n).c_str());
118+
tree->Branch("tf", pTf, fmt::format("tf[{}]/i", n).c_str());
119+
120+
tree->Fill();
121+
return tree;
122+
}
123+
124+
/// Write the container as a TTree inside a TFile on disk
125+
/// \param filename path to the output ROOT file
126+
void writeToFile(const std::string& filename) const
127+
{
128+
TFile f(filename.c_str(), "RECREATE");
129+
if (f.IsZombie()) {
130+
throw std::runtime_error(fmt::format("CMVContainer::writeToFile: cannot open '{}'", filename));
131+
}
132+
auto tree = toTTree();
133+
tree->Write();
134+
f.Close();
135+
}
136+
137+
/// Restore a CMVContainer from a TTree previously written by toTTree()
138+
static CMVContainer fromTTree(TTree* tree)
139+
{
140+
if (!tree) {
141+
throw std::runtime_error("CMVContainer::fromTTree: null TTree pointer");
142+
}
143+
144+
CMVContainer c;
145+
const Long64_t nEntries = tree->GetEntries();
146+
if (nEntries <= 0) {
147+
return c;
148+
}
149+
150+
// Read the array branches back into vectors in one GetEntry() call
151+
std::vector<float> bCmv(nEntries);
152+
std::vector<uint32_t> bCru(nEntries), bTimebin(nEntries), bTf(nEntries);
153+
154+
tree->SetBranchAddress("cmv", bCmv.data());
155+
tree->SetBranchAddress("cru", bCru.data());
156+
tree->SetBranchAddress("timebin", bTimebin.data());
157+
tree->SetBranchAddress("tf", bTf.data());
158+
159+
tree->GetEntry(0);
160+
161+
c.cmvValues = std::move(bCmv);
162+
c.cru = std::move(bCru);
163+
c.timebin = std::move(bTimebin);
164+
c.tf = std::move(bTf);
165+
166+
return c;
167+
}
168+
169+
ClassDefNV(CMVContainer, 1)
170+
};
171+
172+
} // namespace o2::tpc
173+
174+
#endif // ALICEO2_TPC_CMVCONTAINER_H_

Detectors/TPC/workflow/CMakeLists.txt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ o2_add_library(TPCWorkflow
2525
src/KryptonRawFilterSpec.cxx
2626
src/OccupancyFilterSpec.cxx
2727
src/SACProcessorSpec.cxx
28+
src/CMVToVectorSpec.cxx
2829
src/IDCToVectorSpec.cxx
2930
src/CalibdEdxSpec.cxx
3031
src/CalibratordEdxSpec.cxx
@@ -288,4 +289,24 @@ o2_add_executable(pressure-temperature
288289
SOURCES src/tpc-pressure-temperature.cxx
289290
PUBLIC_LINK_LIBRARIES O2::TPCWorkflow)
290291

291-
add_subdirectory(readers)
292+
o2_add_executable(cmv-to-vector
293+
COMPONENT_NAME tpc
294+
SOURCES src/tpc-cmv-to-vector.cxx
295+
PUBLIC_LINK_LIBRARIES O2::TPCWorkflow)
296+
297+
o2_add_executable(cmv-flp
298+
COMPONENT_NAME tpc
299+
SOURCES src/tpc-flp-cmv.cxx
300+
PUBLIC_LINK_LIBRARIES O2::TPCWorkflow)
301+
302+
o2_add_executable(cmv-distribute
303+
COMPONENT_NAME tpc
304+
SOURCES src/tpc-distribute-cmv.cxx
305+
PUBLIC_LINK_LIBRARIES O2::TPCWorkflow)
306+
307+
o2_add_executable(cmv-factorize
308+
COMPONENT_NAME tpc
309+
SOURCES src/tpc-factorize-cmv.cxx
310+
PUBLIC_LINK_LIBRARIES O2::TPCWorkflow)
311+
312+
add_subdirectory(readers)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// @file CMVToVectorSpec.h
13+
/// @author Tuba Gündem, tuba.gundem@cern.ch
14+
/// @brief Processor to convert CMVs to a vector in a CRU
15+
16+
#ifndef TPC_CMVToVectorSpec_H_
17+
#define TPC_CMVToVectorSpec_H_
18+
19+
#include "Framework/DataProcessorSpec.h"
20+
#include <string_view>
21+
22+
namespace o2::tpc
23+
{
24+
25+
/// create a processor spec
26+
/// convert CMV raw values to a vector in a CRU
27+
o2::framework::DataProcessorSpec getCMVToVectorSpec(const std::string inputSpec, std::vector<uint32_t> const& crus);
28+
29+
} // end namespace o2::tpc
30+
31+
#endif // TPC_CMVToVectorSpec_H_

0 commit comments

Comments
 (0)