#include <algorithm> #include <fstream> #include <sstream> // Local #include "TbKernel/TbConstants.h" #include "TbKernel/TbCondFile.h" #include "TbKernel/ITbDataSvc.h" #include "TbPixelSvc.h" DECLARE_SERVICE_FACTORY(TbPixelSvc) //============================================================================ // Constructor //============================================================================ TbPixelSvc::TbPixelSvc(const std::string& name, ISvcLocator* svc) : base_class(name, svc), m_geomSvc(nullptr), m_msg(nullptr) { declareProperty("WriteTrimDACs", m_writeTrimDACs = false); } //============================================================================ // Destructor //============================================================================ TbPixelSvc::~TbPixelSvc() { // Delete the message service. if (m_msg) delete m_msg; } //============================================================================ // Initialisation //============================================================================ StatusCode TbPixelSvc::initialize() { StatusCode sc = Service::initialize(); if (!sc.isSuccess()) return sc; // Get the number of chips. const unsigned int nDevices = geomSvc()->nDevices(); m_pixelConfiguration.resize(nDevices); m_protectPhase = std::vector<bool>(nDevices, 0); for (unsigned int i = 0; i < nDevices; ++i) { m_pixelConfiguration[i].resize(Tb::NPixels, PixelConfig()); } // Retrieve the names of the configuration files to be parsed. ITbDataSvc* dataSvc = Gaudi::svcLocator()->service<ITbDataSvc>("TbDataSvc"); std::vector<std::string> files = dataSvc->getPixelConfig(); files.push_back(dataSvc->getTimingConfig()); for (auto& f : files) { if (f == "") continue; msg() << MSG::INFO << "Importing pixel configuration from " << f << endmsg; // Import conditions from configuration file. if (!readConditions(f)) { msg() << MSG::ERROR << "Cannot import pixel configuration" << endmsg; return StatusCode::FAILURE; } } return StatusCode::SUCCESS; } //============================================================================ // Add a constant offset in time to a single super column //============================================================================ void TbPixelSvc::addOffset(const int offset, const unsigned int device, const unsigned int dcol) { for (unsigned int pix = 0; pix < 512; ++pix) { const unsigned int address = pix + (0xFE00 & (dcol << 9)); m_pixelConfiguration[device][address].tOffset += offset; } } //============================================================================ // Export trim DACs and masked pixel flags to a text file //============================================================================ void TbPixelSvc::writeTrimDAC(const unsigned int device) { std::ofstream trimdac; trimdac.open(("plane_" + std::to_string(device) + "_trimdac.txt").c_str()); trimdac << "#col row trim mask tp_ena " << std::endl; for (unsigned int col = 0; col < Tb::NCols; ++col) { for (unsigned int row = 0; row < Tb::NRows; ++row) { const unsigned int pixel = address(col, row); trimdac << std::setw(3) << col << std::setw(4) << row << std::setw(4) << (int)(m_pixelConfiguration[device][pixel].trim) << std::setw(4) << m_pixelConfiguration[device][pixel].trimDac_isMasked << std::setw(4) << m_pixelConfiguration[device][pixel].tp_ena << std::endl; } } trimdac.close(); } //============================================================================ // Set trim DACs for the entire pixel matrix of a chip //============================================================================ void TbPixelSvc::setTrim(const unsigned int device, const char* data) { for (unsigned int col = 0; col < Tb::NCols; ++col) { for (unsigned int row = 0; row < Tb::NRows; ++row) { const unsigned int pixel = address(col, row); const uint8_t word = *(data + col + Tb::NRows * row); m_pixelConfiguration[device][pixel].trim = 0xF & (word >> 1); m_pixelConfiguration[device][pixel].tp_ena = 0x1 & (word >> 5); m_pixelConfiguration[device][pixel].trimDac_isMasked = 0x1 & word; } } if (m_writeTrimDACs) writeTrimDAC(device); } void TbPixelSvc::resetClockPhase(const unsigned int device) { for (unsigned int pixel = 0; pixel < Tb::NPixels; ++pixel) { m_pixelConfiguration[device][pixel].tOffset = 0; } } //============================================================================ // Calculate the clock phase correction. //============================================================================ void TbPixelSvc::setPhase(const unsigned int device, const unsigned int pll_config = 1024, const int amplitude = 1) { if (m_protectPhase[device]) return; const unsigned int phase = pow(2, (pll_config & 0x1C0) >> 6); if (phase == 1) return; for (unsigned int pixel = 0; pixel < Tb::NPixels; ++pixel) { // Decode the pixel address, first the double column. const unsigned int dcol = (0xFE00 & pixel) >> 9; // Get the pixel within the super pixel. const unsigned int pix = (0x0007 & pixel); // Calculate the column number. const unsigned int col = 2 * dcol + pix / 4; // col = col % 128; int dt = 0; if (phase == 16) { dt = (-((16 - ((col / 2) % 16)) % 16)) << 8; } else if (phase == 4) { dt = (-4 * ((4 - ((col / 2) % 4)) % 4)) << 8; } else if (phase == 2) { dt = (((col % 4) < 2) ? 8 : 0) << 8; } m_pixelConfiguration[device][pixel].tOffset += amplitude * dt; } m_protectPhase[device] = true; } //============================================================================ // Import pixel conditions from configuration file //============================================================================ bool TbPixelSvc::readConditions(const std::string& filename) { TbCondFile f(filename); if (!f.is_open()) { msg() << MSG::WARNING << "Cannot open " << filename << endmsg; return false; } std::string mode = ""; std::string chip = ""; int deviceIndex = -1; unsigned int col(0), row(0); std::vector<unsigned int> badPixels; while (!f.eof()) { std::string line = ""; if (!f.getLine(line)) continue; if (line.find("Charge") != std::string::npos) { f.split(line, ' ', mode, chip); deviceIndex = geomSvc()->deviceIndex(chip); if (deviceIndex == 999) { msg() << MSG::ERROR << "Device " << chip << " is not in the alignment file" << endmsg; msg() << MSG::ERROR << "Stop parsing " << filename << endmsg; break; } } else if (line.find("Mask") != std::string::npos || line.find("Offset") != std::string::npos || line.find("Timing") != std::string::npos || line.find("GlobalCharge") != std::string::npos || line.find("PLL") != std::string::npos) { mode = line; } else if (mode == "Charge") { float a(0.), b(0.), c(0.), t(0.); f.split(line, ' ', col, row, a, b, c, t); const unsigned int pixel = address(col, row); if( isnan(a) || isinf(a) || isnan(b) || isnan(c) || isnan(t) || isinf(b) || isinf(c) || isinf(t)){ badPixels.push_back(pixel); } m_pixelConfiguration[deviceIndex][pixel].p0 = a; m_pixelConfiguration[deviceIndex][pixel].p1 = b; m_pixelConfiguration[deviceIndex][pixel].c = c; m_pixelConfiguration[deviceIndex][pixel].t = t; } else if (mode == "Mask") { f.split(line, ' ', chip, col, row); deviceIndex = geomSvc()->deviceIndex(chip); if (deviceIndex == 999) { msg() << MSG::ERROR << "Device " << chip << " is not in the alignment file" << endmsg; continue; } const unsigned int pixel = address(col, row); msg() << MSG::INFO << "Masking pixel " << format("0x%04x", pixel) << format(" (column %3d", col) << format(", row %3d)", row) << " on device " << chip << endmsg; m_pixelConfiguration[deviceIndex][pixel].isMasked = true; } else if (mode == "GlobalCharge") { double a(0.), b(0.), c(0.), t(0.); f.split(line, ' ', chip, a, b, c, t); // if( isnan(a) || isnan(b) || isnan(c) || isnan(d) ) deviceIndex = geomSvc()->deviceIndex(chip); if (deviceIndex == 999) { msg() << MSG::ERROR << "Device " << chip << " is not in the alignment file" << endmsg; continue; } for (auto& pixel : m_pixelConfiguration[deviceIndex]) { pixel.p0 = a; pixel.p1 = b; pixel.c = c; pixel.t = t; } msg() << MSG::INFO << "Applying global charge calibration on device " << chip << "(a = " << a << ", b = " << b << ", c = " << c << ", t = " << t << ")" << endmsg; } else if (mode == "Offset") { int offset = 0; f.split(line, ' ', chip, col, offset); deviceIndex = geomSvc()->deviceIndex(chip); if (deviceIndex == 999) { msg() << MSG::ERROR << "Device " << chip << " is not in the alignment file" << endmsg; continue; } msg() << MSG::INFO << "Adding time offset " << offset * 25 << " ns " << "to double column " << col << " on chip " << chip << endmsg; addOffset(offset * Tb::ToA, deviceIndex, col); } else if (mode == "Timing") { double dt = 0.; f.split(line, ' ', chip, dt); deviceIndex = geomSvc()->deviceIndex(chip); if (deviceIndex == 999) { msg() << MSG::ERROR << "Device " << chip << " is not in the alignment file" << endmsg; continue; } const int offset = static_cast<int>(std::round(dt * Tb::nanosecond)); msg() << MSG::INFO << "Adding time offset " << dt << " ns (" << offset << " time units) to chip " << chip << endmsg; const unsigned int nDCols = Tb::NCols / 2; for (unsigned int col = 0; col < nDCols; ++col) { addOffset(offset, deviceIndex, col); } } else if (mode == "PLL") { int amplitude = 0.; unsigned int pll_mode; f.split(line, ' ', chip, pll_mode, amplitude); deviceIndex = geomSvc()->deviceIndex(chip); if (deviceIndex == 999) { msg() << MSG::ERROR << "Device " << chip << " is not in the alignment file" << endmsg; continue; } msg() << MSG::INFO << "Overwriting PLL CONFIG for chip " << chip << endmsg; m_protectPhase[deviceIndex] = false; resetClockPhase(deviceIndex); setPhase(deviceIndex, pll_mode, amplitude); } } for( auto& pixel : badPixels ){ auto pos = posFromAddress(pixel); float avgt(0), avgc(0), avgp0(0), avgp1(0); unsigned int nPixels=0; if( pos.second != 255 ){ unsigned int neighbour = address( pos.first, pos.second+1 ); if( std::find( badPixels.begin(), badPixels.end(), neighbour) == badPixels.end() ){ avgt += m_pixelConfiguration[deviceIndex][neighbour].t; avgc += m_pixelConfiguration[deviceIndex][neighbour].c; avgp0 += m_pixelConfiguration[deviceIndex][neighbour].p0; avgp1 += m_pixelConfiguration[deviceIndex][neighbour].p1; nPixels++; } } if( pos.second != 0 ){ unsigned int neighbour = address( pos.first, pos.second-1 ); if( std::find( badPixels.begin(), badPixels.end(), neighbour) == badPixels.end() ){ avgt += m_pixelConfiguration[deviceIndex][neighbour].t; avgc += m_pixelConfiguration[deviceIndex][neighbour].c; avgp0 += m_pixelConfiguration[deviceIndex][neighbour].p0; avgp1 += m_pixelConfiguration[deviceIndex][neighbour].p1; nPixels++; } } if(nPixels==0){ m_pixelConfiguration[deviceIndex][pixel].isMasked = true; // printConfig( deviceIndex, pixel ) ; continue; } m_pixelConfiguration[deviceIndex][pixel].t = avgt / (float)nPixels; m_pixelConfiguration[deviceIndex][pixel].p0 = avgp0 / (float)nPixels; m_pixelConfiguration[deviceIndex][pixel].p1 = avgp1 / (float)nPixels; m_pixelConfiguration[deviceIndex][pixel].c = avgc / (float)nPixels; //printConfig( deviceIndex, pixel ) ; } f.close(); return true; }