Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

/// @file CMV.h
/// @author Tuba Gündem, tuba.gundem@cern.ch
/// @brief Common mode values data format definition

/// The data is sent by the CRU as 256+16 bit words. The CMV data layout is as follows:
/// - 256-bit Header: [version:8][packetID:8][errorCode:8][magicWord:8][heartbeatOrbit:32][heartbeatBC:16][padding:176]
/// - 16-bit CMV value: [sign:1][I8F7:15] where bit 15 is the sign (1=positive, 0=negative) and the lower 15 bits are a fixed point I8F7 value (8 integer bits, 7 fractional bits)
/// Float conversion: sign ? (value & 0x7FFF) / 128.0 : -(value & 0x7FFF) / 128.0

#ifndef ALICEO2_DATAFORMATSTPC_CMV_H
#define ALICEO2_DATAFORMATSTPC_CMV_H

#include <cstdint>
#include <cmath>

namespace o2::tpc::cmv
{

static constexpr uint32_t NTimeBinsPerPacket = 3564; ///< number of time bins (covering 8 heartbeats)
static constexpr uint32_t NPacketsPerTFPerCRU = 4; ///< 4 packets per timeframe
static constexpr uint32_t NTimeBinsPerTF = NTimeBinsPerPacket * NPacketsPerTFPerCRU; ///< maximum number of timebins per timeframe (14256)

/// Data padding: NTimeBinsPerPacket * sizeof(Data) = 3564 * 2 = 7128 bytes
static constexpr uint32_t DataSizeBytes = NTimeBinsPerPacket * sizeof(uint16_t); ///< 7128 bytes
static constexpr uint32_t DataPaddingBytes = (32 - (DataSizeBytes % 32)) % 32; ///< 8 bytes

/// Header definition of the CMVs
struct Header {
static constexpr uint8_t MagicWord = 0xDC;
union {
uint64_t word0 = 0; ///< bits 0 - 63
struct {
uint8_t version : 8; ///< version
uint8_t packetID : 8; ///< packet id
uint8_t errorCode : 8; ///< errors
uint8_t magicWord : 8; ///< magic word
uint32_t heartbeatOrbit : 32; ///< first heart beat timing of the package
};
};
union {
uint64_t word1 = 0; ///< bits 64 - 127
struct {
uint16_t heartbeatBC : 16; ///< first BC id of the package
uint16_t unused1 : 16; ///< reserved
uint32_t unused2 : 32; ///< reserved
};
};
union {
uint64_t word3 = 0; ///< bits 128 - 191
struct {
uint64_t unused3 : 64; ///< reserved
};
};
union {
uint64_t word4 = 0; ///< bits 192 - 255
struct {
uint64_t unused4 : 64; ///< reserved
};
};
};

/// CMV single data container
struct Data {
uint16_t cmv{0}; ///< 16-bit signed fixed point value: bit 15 = sign (1=positive, 0=negative), bits 14-0 = I8F7 magnitude

uint16_t getCMV() const { return cmv; } ///< raw 16-bit integer representation
void setCMV(uint16_t value) { cmv = value; } ///< set raw 16-bit integer representation

// Decode to float: sign-magnitude with 7 fractional bits, range ±255.992
float getCMVFloat() const
{
const bool positive = (cmv >> 15) & 1; // bit 15: sign (1=positive, 0=negative)
const float magnitude = (cmv & 0x7FFF) / 128.f; // lower 15 bits, shift right by 7 (divide by 2^7)
return positive ? magnitude : -magnitude;
}

// Encode from float: clamps magnitude to 15 bits, range ±255.992
void setCMVFloat(float value)
{
const bool positive = (value >= 0.f);
const uint16_t magnitude = static_cast<uint16_t>(std::abs(value) * 128.f + 0.5f) & 0x7FFF;
cmv = (positive ? 0x8000 : 0x0000) | magnitude;
}
};

/// CMV full data container: one packet carries NTimeBinsPerPacket CMV values followed by padding
/// Layout: Header (32 bytes) + Data[NTimeBinsPerPacket] (7128 bytes) + padding (8 bytes) = 7168 bytes total (224 * 32 = 7168)
/// The padding bytes at the end of the data array are rubbish/unused and must not be interpreted as CMV values
struct Container {
Header header; ///< CMV data header
Data data[NTimeBinsPerPacket]; ///< data values
uint8_t padding[DataPaddingBytes]{}; ///< trailing padding to align data to 32-byte boundary

// Header and data accessors
const Header& getHeader() const { return header; }
Header& getHeader() { return header; }

const Data* getData() const { return data; }
Data* getData() { return data; }

// Per timebin CMV accessors
uint16_t getCMV(uint32_t timeBin) const { return data[timeBin].getCMV(); }
void setCMV(uint32_t timeBin, uint16_t value) { data[timeBin].setCMV(value); }

float getCMVFloat(uint32_t timeBin) const { return data[timeBin].getCMVFloat(); }
void setCMVFloat(uint32_t timeBin, float value) { data[timeBin].setCMVFloat(value); }
};

} // namespace o2::tpc::cmv

#endif
3 changes: 2 additions & 1 deletion Detectors/TPC/base/include/TPCBase/RDHUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#define AliceO2_TPC_RDHUtils_H

#include "DetectorsRaw/RDHUtils.h"
//#include "Headers/RAWDataHeader.h"
// #include "Headers/RAWDataHeader.h"

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

/// compose feeid from cru, endpoint and link
Expand Down
4 changes: 3 additions & 1 deletion Detectors/TPC/calibration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ o2_add_library(TPCCalibration
src/DigitAdd.cxx
src/CorrectdEdxDistortions.cxx
src/PressureTemperatureHelper.cxx
src/CMVContainer.cxx
PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBaseRecSim
O2::TPCReconstruction ROOT::Minuit
Microsoft.GSL::GSL
Expand Down Expand Up @@ -115,7 +116,8 @@ o2_target_root_dictionary(TPCCalibration
include/TPCCalibration/TPCMShapeCorrection.h
include/TPCCalibration/DigitAdd.h
include/TPCCalibration/CorrectdEdxDistortions.h
include/TPCCalibration/PressureTemperatureHelper.h)
include/TPCCalibration/PressureTemperatureHelper.h
include/TPCCalibration/CMVContainer.h)

o2_add_test_root_macro(macro/comparePedestalsAndNoise.C
PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim
Expand Down
93 changes: 93 additions & 0 deletions Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

/// @file CMVContainer.h
/// @author Tuba Gündem, tuba.gundem@cern.ch
/// @brief Structs for storing CMVs to the CCDB

#ifndef ALICEO2_TPC_CMVCONTAINER_H_
#define ALICEO2_TPC_CMVCONTAINER_H_

#include <string>
#include <memory>
#include <vector>
#include <cstdint>

#include "TTree.h"
#include "TPCBase/CRU.h"
#include "DataFormatsTPC/CMV.h"

namespace o2::tpc
{

struct CMVPerTF; // forward declaration

/// Delta+zigzag+varint compressed CMV data for one TF across all CRUs
/// Produced by CMVPerTF::compress(), restored with decompress()
/// Each TTree entry corresponds to one CMVPerTFCompressed object (one TF)
struct CMVPerTFCompressed {
uint32_t firstOrbit{0}; ///< First orbit of this TF (copied from CMVPerTF)
uint16_t firstBC{0}; ///< First bunch crossing of this TF (copied from CMVPerTF)

/// Delta+zigzag+varint encoded CMV values
/// Layout: CRU-major, time-minor; delta is reset to zero at each CRU boundary
std::vector<uint8_t> mCompressedData;

/// Restore a CMVPerTF from this compressed object into *cmv (must not be null)
void decompress(CMVPerTF* cmv) const;

private:
static uint16_t signedToCmv(int32_t val); ///< Signed integer -> sign-magnitude uint16_t
static int32_t zigzagDecode(uint32_t value); ///< Zigzag decode
static uint32_t decodeVarint(const uint8_t*& data, const uint8_t* end); ///< Varint decode

public:
ClassDefNV(CMVPerTFCompressed, 1)
};

/// CMV data for one TF across all CRUs
/// Raw 16-bit CMV values are stored in a flat C array indexed as [cru * NTimeBinsPerTF + timeBin]
/// CRU::MaxCRU and cmv::NTimeBinsPerTF are compile-time constants, so no dynamic allocation is needed
/// Each TTree entry corresponds to one CMVPerTF object (one TF)
struct CMVPerTF {
uint32_t firstOrbit{0}; ///< First orbit of this TF, from heartbeatOrbit of the first CMV packet
uint16_t firstBC{0}; ///< First bunch crossing of this TF, from heartbeatBC of the first CMV packet

// Raw 16-bit CMV values, flat array indexed as [cru * NTimeBinsPerTF + timeBin]
uint16_t mDataPerTF[CRU::MaxCRU * cmv::NTimeBinsPerTF]{};

/// Return the raw 16-bit CMV value for a given CRU and timebin within this TF
uint16_t getCMV(const int cru, const int timeBin) const;

/// Return the float CMV value for a given CRU and timebin within this TF
float getCMVFloat(const int cru, const int timeBin) const;

/// Compress this object into a CMVPerTFCompressed using delta+zigzag+varint encoding
CMVPerTFCompressed compress() const;

/// Serialise into a TTree; each Fill() call appends one entry (one TF)
std::unique_ptr<TTree> toTTree() const;

/// Write the TTree to a ROOT file
static void writeToFile(const std::string& filename, const std::unique_ptr<TTree>& tree);

private:
static int32_t cmvToSigned(uint16_t raw); ///< Sign-magnitude uint16_t → signed integer
static uint32_t zigzagEncode(int32_t value); ///< Zigzag encode
static void encodeVarintInto(uint32_t value, std::vector<uint8_t>& out); ///< Varint encode

public:
ClassDefNV(CMVPerTF, 1)
};

} // namespace o2::tpc

#endif // ALICEO2_TPC_CMVCONTAINER_H_
126 changes: 126 additions & 0 deletions Detectors/TPC/calibration/macro/drawCMV.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#if !defined(__CLING__) || defined(__ROOTCLING__)
#include <string>
#include <string_view>
#include <fmt/format.h>

#include "TFile.h"
#include "TParameter.h"
#include "TTree.h"
#include "TH2F.h"
#include "TCanvas.h"

#include "TPCCalibration/CMVContainer.h"
#include "TPCBase/Utils.h"
#endif

using namespace o2::tpc;

/// Draw CMV (Common Mode Values) vs timebin from a CCDB TTree file
/// \param filename input ROOT file containing the ccdb_object TTree
/// \param outDir output directory for saved plots; nothing is saved if empty
/// \return array of canvases
TObjArray* drawCMV(std::string_view filename, std::string_view outDir)
{
TObjArray* arrCanvases = new TObjArray;
arrCanvases->SetName("CMV");

// open file
TFile f(filename.data(), "READ");
if (f.IsZombie()) {
fmt::print("ERROR: cannot open '{}'\n", filename);
return arrCanvases;
}
fmt::print("Opened file: {}\n", filename);

// get TTree
TTree* tree = nullptr;
f.GetObject("ccdb_object", tree);
if (!tree) {
fmt::print("ERROR: TTree 'ccdb_object' not found\n");
return arrCanvases;
}
fmt::print("Tree 'ccdb_object' found, entries: {}\n", tree->GetEntries());

// read metadata
long firstTF = -1, lastTF = -1;
if (auto* userInfo = tree->GetUserInfo()) {
for (int i = 0; i < userInfo->GetSize(); ++i) {
if (auto* p = dynamic_cast<TParameter<long>*>(userInfo->At(i))) {
if (std::string(p->GetName()) == "firstTF")
firstTF = p->GetVal();
if (std::string(p->GetName()) == "lastTF")
lastTF = p->GetVal();
}
}
}
fmt::print("firstTF: {}, lastTF: {}\n", firstTF, lastTF);

const int nEntries = tree->GetEntries();
if (nEntries == 0) {
fmt::print("ERROR: no entries in tree\n");
return arrCanvases;
}

constexpr int nCRUs = CRU::MaxCRU;
constexpr int nTimeBins = cmv::NTimeBinsPerTF;

TH2F* h2d = new TH2F("hCMVvsTimeBin", ";Timebin (200 ns);Common Mode Values (ADC)",
nTimeBins / 16, 0, nTimeBins,
110, -100, 10);
h2d->SetStats(0);

// branch setup
o2::tpc::CMVPerTFCompressed* tfEntry = nullptr;
tree->SetBranchAddress("CMVPerTFCompressed", &tfEntry);

// allocate once outside the loop to avoid repeated zero-initialisation of the large array
auto* tf = new CMVPerTF();

long firstOrbit = -1;

for (int i = 0; i < nEntries; ++i) {
tree->GetEntry(i);
tfEntry->decompress(tf);

if (i == 0) {
firstOrbit = tf->firstOrbit;
}

for (int cru = 0; cru < nCRUs; ++cru) {
for (int tb = 0; tb < nTimeBins; ++tb) {
h2d->Fill(tb, tf->getCMVFloat(cru, tb));
}
}
}

delete tf;
tree->ResetBranchAddresses();
delete tfEntry;

fmt::print("firstOrbit: {}\n", firstOrbit);

// draw
auto* c = new TCanvas("cCMVvsTimeBin", "");
c->SetLogz();
h2d->Draw("colz");

arrCanvases->Add(c);

if (outDir.size()) {
utils::saveCanvases(*arrCanvases, outDir, "png,pdf", "CMVCanvases.root");
}

f.Close();
return arrCanvases;
}
Loading
Loading