Commit f38dfb47 authored by Fabio Ravera's avatar Fabio Ravera
Browse files

Merge branch 'Dev_Marijus_BERmeasurement' into 'Dev'

BER measurement to determine data link quality

See merge request cms_tk_ph2/Ph2_ACF!211
parents 7bf73daf 50848883
......@@ -34,6 +34,47 @@ class DetectorContainer;
* \class DQMHistogramBase
* \brief Base class for monitoring histograms
*/
namespace user_detail
{
template <typename>
struct sfinae_true_DQMHistogramBase : std::true_type
{
};
template <typename T>
static auto test_SetZTitle(int ) -> sfinae_true_DQMHistogramBase<decltype(std::declval<T>().SetZTitle(""))>;
template <typename>
static auto test_SetZTitle(long) -> std::false_type;
} // namespace user_detail
// SFINAE: check if object T has SetZTitle
template <typename T>
struct has_SetZTitle : decltype(user_detail::test_SetZTitle<T>(0))
{
};
// Functor for SetZTitle - default case
template <typename T, bool hasSetZTitle = false>
struct CallSetZTitle
{
void operator()(T* thePlot, const char* theTitle)
{
return;
}
};
// Functor for SetZTitle - case when SetZTitle is defined
template <typename T>
struct CallSetZTitle<T, true>
{
void operator()(T* thePlot, const char* theTitle)
{
thePlot.SetZTitle(theTitle);
return;
}
};
class DQMHistogramBase
{
public:
......@@ -84,9 +125,13 @@ class DQMHistogramBase
const char* YTitle = nullptr,
const char* ZTitle = nullptr)
{
if(XTitle != nullptr) histContainer.fTheHistogram->SetXTitle(XTitle);
if(YTitle != nullptr) histContainer.fTheHistogram->SetYTitle(YTitle);
if(ZTitle != nullptr) histContainer.fTheHistogram->SetZTitle(ZTitle);
if(XTitle != nullptr) histContainer.fTheHistogram->GetXaxis()->SetTitle(XTitle);
if(YTitle != nullptr) histContainer.fTheHistogram->GetYaxis()->SetTitle(YTitle);
if(ZTitle != nullptr)
{
CallSetZTitle<Hist, has_SetDirectory<Hist>::value> setZTitleFunctor;
setZTitleFunctor(histContainer.fTheHistogram, ZTitle);
}
RootContainerFactory::bookChipHistograms(theOutputFile, theDetectorStructure, dataContainer, histContainer);
}
......@@ -148,12 +193,13 @@ class DQMHistogramBase
}
}
template <typename T>
template<typename T>
T findValueInSettings(const Ph2_System::SettingsMap& settingsMap, const std::string name, T defaultValue = T()) const
{
auto setting = settingsMap.find(name);
return (setting != std::end(settingsMap) ? boost::any_cast<T>(setting->second) : defaultValue);
}
};
#endif
......@@ -20,6 +20,7 @@
#include "RD53SCurveHistograms.h"
#include "RD53ThrEqualizationHistograms.h"
#include "RD53ThresholdHistograms.h"
#include "RD53DataTransmissionTestGraphs.h"
#include "SSAPhysicsHistograms.h"
#include "TFile.h"
......@@ -127,6 +128,8 @@ void DQMInterface::configure(std::string const& calibrationName, std::string con
fDQMHistogrammerVector.push_back(new PhysicsHistograms());
else if(calibrationName == "ssaphysics")
fDQMHistogrammerVector.push_back(new SSAPhysicsHistograms());
else if(calibrationName == "datatrtest")
fDQMHistogrammerVector.push_back(new DataTransmissionTestGraphs());
fOutputFile = new TFile("tmp.root", "RECREATE");
for(auto dqmHistogrammer: fDQMHistogrammerVector) dqmHistogrammer->book(fOutputFile, fDetectorStructure, pSettingsMap);
......
/*!
\file RD53DataTransmissionTestGraphs.cc
\brief Implementation of TAP scan graphs
\author Marijus AMBROZAS
\version 1.0
\date 26/04/21
Support: email to marijus.ambrozas@cern.ch
*/
#include "RD53DataTransmissionTestGraphs.h"
using namespace Ph2_HwDescription;
using namespace Ph2_HwInterface;
void DataTransmissionTestGraphs::book(TFile* theOutputFile, const DetectorContainer& theDetectorStructure, const Ph2_System::SettingsMap& settingsMap)
{
ContainerFactory::copyStructure(theDetectorStructure, DetectorData);
// #######################
// # Retrieve parameters #
// #######################
BERtarget = this->findValueInSettings<double>(settingsMap, "BERtarget");
given_time = this->findValueInSettings<double>(settingsMap, "byTime");
frames_or_time = this->findValueInSettings<double>(settingsMap, "framesORtime");
auto gTAP0scan = CanvasContainer<TGraphAsymmErrors>(11);
bookImplementer(theOutputFile, theDetectorStructure, TAP0scan, gTAP0scan, "TAP0", "Bit Error Rate");
auto hTAP0tgt = CanvasContainer<TH1F>("TAP0tgt", "TAP0 at target BER", 1024, 0-0.5, 1024-0.5);
bookImplementer(theOutputFile, theDetectorStructure, TAP0tgt, hTAP0tgt, "TAP0 at target BER", "Value");
}
bool DataTransmissionTestGraphs::fill(std::vector<char>& dataBuffer)
{
ChipContainerStream<EmptyContainer, std::array<std::tuple<uint16_t, double, double, double>, 11>> theTAP0scanStreamer("DataTransmissionTestTAP0scan");
ChipContainerStream<EmptyContainer, uint16_t> theTAP0tgtStreamer("DataTransmissionTestTAP0tgteshold");
if(theTAP0scanStreamer.attachBuffer(&dataBuffer))
{
theTAP0scanStreamer.decodeChipData(DetectorData);
DataTransmissionTestGraphs::fillTAP0scan(DetectorData);
DetectorData.cleanDataStored();
return true;
}
else if(theTAP0tgtStreamer.attachBuffer(&dataBuffer))
{
theTAP0tgtStreamer.decodeChipData(DetectorData);
DataTransmissionTestGraphs::fillTAP0tgt(DetectorData);
DetectorData.cleanDataStored();
return true;
}
return false;
}
void DataTransmissionTestGraphs::fillTAP0scan(const DetectorDataContainer& TAP0scanContainer)
{
for(const auto cBoard: TAP0scanContainer)
{
for(const auto cOpticalGroup: *cBoard)
for(const auto cHybrid: *cOpticalGroup)
for(const auto cChip: *cHybrid)
{
if(cChip->getSummaryContainer<std::array<std::tuple<uint16_t, double, double, double>, 11>>() == nullptr) continue;
auto *TAP0scanGraph = TAP0scan.at(cBoard->getIndex())->at(cOpticalGroup->getIndex())->at(cHybrid->getIndex())->at(cChip->getIndex())->getSummary<CanvasContainer<TGraphAsymmErrors>>().fTheHistogram;
for(auto i = 0u; i < 11u; i++) // set bin errors manually
{
TAP0scanGraph->SetPoint(i, ((double)(std::get<0>((cChip->getSummary<std::array<std::tuple<uint16_t, double, double, double>, 11>>())[i]))),
std::get<1>((cChip->getSummary<std::array<std::tuple<uint16_t, double, double, double>, 11>>())[i]));
TAP0scanGraph->SetPointError(i, 0.5, 0.5, std::get<2>((cChip->getSummary<std::array<std::tuple<uint16_t, double, double, double>, 11>>())[i]),
std::get<3>((cChip->getSummary<std::array<std::tuple<uint16_t, double, double, double>, 11>>())[i]));
}
TAP0scanGraph->SetMarkerStyle(8);
}
}
}
void DataTransmissionTestGraphs::fillTAP0tgt(const DetectorDataContainer& TAP0tgtContainer)
{
for(const auto cBoard: TAP0tgtContainer)
for(const auto cOpticalGroup: *cBoard)
for(const auto cHybrid: *cOpticalGroup)
for(const auto cChip: *cHybrid)
{
if(cChip->getSummaryContainer<uint16_t>() == nullptr) continue;
auto* TAP0tgtHist = TAP0tgt.at(cBoard->getIndex())->at(cOpticalGroup->getIndex())->at(cHybrid->getIndex())->at(cChip->getIndex())->getSummary<CanvasContainer<TH1F>>().fTheHistogram;
TAP0tgtHist->SetBinContent(TAP0tgtHist->GetBin(cChip->getSummary<uint16_t>()), 1);
TAP0tgtHist->SetBinError(TAP0tgtHist->GetBin(cChip->getSummary<uint16_t>()), 0);
}
}
void DataTransmissionTestGraphs::process()
{
draw<TGraphAsymmErrors>(TAP0scan, "APZ0");
draw<TH1F>(TAP0tgt, "P*");
}
/*!
\file RD53DataTransmissionTestGraphs.h
\brief Header file of TAP scan graphs
\author Marijus AMBROZAS
\version 1.0
\date 26/04/21
Support: email to marijus.ambrozas@cern.ch
*/
#ifndef RD53DataTransmissionTestGraphs_H
#define RD53DataTransmissionTestGraphs_H
#include "../System/SystemController.h"
#include "../Utils/ContainerFactory.h"
#include "../Utils/ContainerStream.h"
#include "../Utils/GenericDataArray.h"
#include "../Utils/RD53Shared.h"
#include "DQMHistogramBase.h"
#include <TH1F.h>
#include <TGraphAsymmErrors.h>
class DataTransmissionTestGraphs : public DQMHistogramBase
{
public:
void book(TFile* theOutputFile, const DetectorContainer& theDetectorStructure, const Ph2_System::SettingsMap& settingsMap) override;
void process() override;
bool fill(std::vector<char>& dataBuffer) override;
void reset() override{};
void fillTAP0scan(const DetectorDataContainer& TAP0scanContainer);
void fillTAP0tgt(const DetectorDataContainer& TAP0tgtContainer);
private:
DetectorDataContainer DetectorData;
DetectorDataContainer TAP0scan;
DetectorDataContainer TAP0tgt;
double BERtarget;
bool given_time;
double frames_or_time;
};
#endif
......@@ -19,6 +19,45 @@
#include <iostream>
namespace user_detail
{
template <typename>
struct sfinae_true_CanvasContainer : std::true_type
{
};
template <typename T>
static auto test_SetDirectory(int ) -> sfinae_true_CanvasContainer<decltype(std::declval<T>().SetDirectory())>;
template <typename>
static auto test_SetDirectory(long) -> std::false_type;
} // namespace user_detail
// SFINAE: check if object T has SetDirectory
template <typename T>
struct has_SetDirectory : decltype(user_detail::test_SetDirectory<T>(0))
{
};
// Functor for SetDirectory - default case
template <typename T, bool hasSetDirectory = false>
struct CallSetDirectory
{
void operator()(T* thePlot)
{
return;
}
};
// Functor for SetDirectory - case when SetDirectory is defined
template <typename T>
struct CallSetDirectory<T, true>
{
void operator()(T* thePlot)
{
thePlot.SetDirectory(0);
}
};
template <class Hist>
class CanvasContainer : public PlotContainer
{
......@@ -31,8 +70,9 @@ class CanvasContainer : public PlotContainer
template <class... Args>
CanvasContainer(Args... args)
{
fTheHistogram = new Hist(std::forward<Args>(args)...);
fTheHistogram->SetDirectory(0);
fTheHistogram = new Hist(args...);
CallSetDirectory<Hist, has_SetDirectory<Hist>::value> setDirectoryFunctor;
setDirectoryFunctor(fTheHistogram);
fCanvas = nullptr;
}
......@@ -76,7 +116,8 @@ class CanvasContainer : public PlotContainer
fTheHistogram = new Hist(*(static_cast<const CanvasContainer<Hist>*>(reference)->fTheHistogram));
fTheHistogram->SetName(name.data());
fTheHistogram->SetTitle(title.data());
fTheHistogram->SetDirectory(0);
CallSetDirectory<Hist, has_SetDirectory<Hist>::value> setDirectoryFunctor;
setDirectoryFunctor(fTheHistogram);
gDirectory->Append(fCanvas);
}
......
......@@ -19,6 +19,7 @@
#include "../tools/RD53ThrAdjustment.h"
#include "../tools/RD53ThrEqualization.h"
#include "../tools/RD53ThrMinimization.h"
#include "../tools/RD53DataTransmissionTest.h"
#include "../tools/Tool.h"
#include "MiddlewareController.h"
//#include "../tools/SSAPhysics.h"
......@@ -118,6 +119,8 @@ std::string MiddlewareController::interpretMessage(const std::string& buffer)
theSystemController_ = new CombinedCalibration<ClockDelay>;
else if(getVariableValue("Calibration", buffer) == "physics")
theSystemController_ = new Physics;
else if(getVariableValue("Calibration", buffer) == "datatrtest")
theSystemController_ = new CombinedCalibration<DataTransmissionTest>;
else
{
......
......@@ -245,7 +245,11 @@
<Setting name="chain2Test"> 0 </Setting>
<Setting name="byTime"> 1 </Setting>
<Setting name="framesORtime"> 10 </Setting>
<Setting name="TargetBER"> 1e-5 </Setting>
<Setting name="TargetBER"> 1e-5 </Setting>
<Setting name="RegNameDAC1"> VCAL_HIGH </Setting>
<Setting name="StartValueDAC1"> 250 </Setting>
<Setting name="StopValueDAC1"> 600 </Setting>
......@@ -254,7 +258,7 @@
<Setting name="StartValueDAC2"> 28 </Setting>
<Setting name="StopValueDAC2"> 50 </Setting>
<Setting name="StepDAC2"> 1 </Setting>
<Setting name="DoFast"> 0 </Setting>
<Setting name="DisplayHisto"> 0 </Setting>
<Setting name="UpdateChipCfg"> 1 </Setting>
......
......@@ -16,6 +16,7 @@
#include "../tools/RD53BERtest.h"
#include "../tools/RD53ClockDelay.h"
#include "../tools/RD53DataReadbackOptimization.h"
#include "../tools/RD53DataTransmissionTest.h"
#include "../tools/RD53Gain.h"
#include "../tools/RD53GainOptimization.h"
#include "../tools/RD53GenericDacDacScan.h"
......@@ -123,7 +124,7 @@ int main(int argc, char** argv)
cmd.defineOption("calib",
"Which calibration to run [latency pixelalive noise scurve gain threqu gainopt thrmin thradj "
"injdelay clkdelay datarbopt physics eudaq bertest voltagetuning, gendacdac]",
"injdelay clkdelay datarbopt datatrtest physics eudaq bertest voltagetuning, gendacdac]",
CommandLineProcessing::ArgvParser::OptionRequiresValue);
cmd.defineOptionAlternative("calib", "c");
......@@ -373,6 +374,20 @@ int main(int argc, char** argv)
dro.run();
dro.draw();
}
else if(whichCalib == "datatrtest")
{
// ##############################
// # Run Data Transmission Test #
// ##############################
LOG(INFO) << BOLDMAGENTA << "@@@ Performing Data Transmission Test @@@" << RESET;
std::string fileName("Run" + RD53Shared::fromInt2Str(runNumber) + "_DataTransmissionTest");
DataTransmissionTest dtt;
dtt.Inherit(&mySysCntr);
dtt.localConfigure(fileName, runNumber);
dtt.run();
dtt.draw();
}
else if(whichCalib == "pixelalive")
{
// ##################
......
......@@ -24,7 +24,7 @@ if(NOT DEFINED ENV{OTSDAQ_CMSOUTERTRACKER_DIR})
link_directories(${PROJECT_SOURCE_DIR/lib})
# Initial set of libraries
set(LIBS ${LIBS} Ph2_Description Ph2_Interface Ph2_Utils Ph2_System Ph2_Tools Ph2_DQMUtils NetworkUtils)
set(LIBS ${LIBS} Ph2_Description Ph2_Interface Ph2_Utils Ph2_System Ph2_Tools Ph2_DQMUtils Ph2_RootUtils NetworkUtils)
# Check for ZMQ installed
if(ZMQ_FOUND)
......
/*!
\file RD53DataTransmissionTest.cc
\brief TAP0 scan to measure the Bit Error Rate and determine data transmission quality
\author Marijus AMBROZAS
\version 1.0
\date 26/04/20
Support: email to marijus.ambrozas@cern.ch
*/
#include "RD53DataTransmissionTest.h"
using namespace Ph2_HwDescription;
using namespace Ph2_HwInterface;
void DataTransmissionTest::ConfigureCalibration()
{
// ##############################
// # Initialize sub-calibration #
// ##############################
BERtest::ConfigureCalibration();
// #######################
// # Retrieve parameters #
// #######################
BERtarget = this->findValueInSettings<double>("TargetBER");
given_time = this->findValueInSettings<double>("byTime");
frames_or_time = this->findValueInSettings<double>("framesORtime");
doDisplay = this->findValueInSettings<double>("DisplayHisto");
// ############################################################
// # Create directory for: raw data, config files, histograms #
// ############################################################
this->CreateResultDirectory(RD53Shared::RESULTDIR, false, false);
}
void DataTransmissionTest::Running()
{
theCurrentRun = this->fRunNumber;
LOG(INFO) << GREEN << "[DataTransmissionTest::Running] Starting run: " << BOLDYELLOW << theCurrentRun << RESET;
DataTransmissionTest::run();
DataTransmissionTest::sendData();
}
void DataTransmissionTest::sendData()
{
// Store (TAP0, BER, BERlowErr, BERupErr)
auto theStreamTAP0scan = prepareChipContainerStreamer<EmptyContainer, std::array<std::tuple<uint16_t, double, double, double>, 11>>("DataTransmissionTestTAP0scan");
// Store TAP0 value that has nearest BER to the target
auto theStreamTAP0tgt = prepareChipContainerStreamer<EmptyContainer, uint16_t>("DataTransmissionTestTAP0target");
if(fStreamerEnabled == true)
{
for(const auto cBoard: theTAP0scanContainer) theStreamTAP0scan.streamAndSendBoard(cBoard, fNetworkStreamer);
for(const auto cBoard: theTAP0tgtContainer) theStreamTAP0tgt.streamAndSendBoard(cBoard, fNetworkStreamer);
}
}
void DataTransmissionTest::Stop()
{
LOG(INFO) << GREEN << "[DataTransmissionTest::Stop] Stopping" << RESET;
Tool::Stop();
DataTransmissionTest::draw();
this->closeFileHandler();
RD53RunProgress::reset();
}
void DataTransmissionTest::localConfigure(const std::string fileRes_, int currentRun)
{
#ifdef __USE_ROOT__
histos = nullptr;
#endif
if(currentRun >= 0)
{
theCurrentRun = currentRun;
LOG(INFO) << GREEN << "[DataTransmissionTest::localConfigure] Starting run: " << BOLDYELLOW << theCurrentRun << RESET;
}
DataTransmissionTest::ConfigureCalibration();
DataTransmissionTest::initializeFiles(fileRes_, currentRun);
}
void DataTransmissionTest::initializeFiles(const std::string fileRes_, int currentRun)
{
fileRes = fileRes_;
#ifdef __USE_ROOT__
delete histos;
histos = new DataTransmissionTestGraphs;
#endif
}
void DataTransmissionTest::run()
{
ContainerFactory::copyAndInitChip<std::array<std::tuple<uint16_t, double, double, double>, 11>>(*fDetectorContainer, theTAP0scanContainer);
for(const auto cBoard: *fDetectorContainer) static_cast<RD53Interface*>(this->fReadoutChipInterface)->WriteBoardBroadcastChipReg(cBoard, "CML_CONFIG_SER_EN_TAP", 0x0);
DataTransmissionTest::binSearch(&theTAP0scanContainer);
DataTransmissionTest::analyze(theTAP0scanContainer, theTAP0tgtContainer);
DataTransmissionTest::chipErrorReport();
}
void DataTransmissionTest::draw(bool saveData)
{
#ifdef __USE_ROOT__
TApplication* myApp = nullptr;
if(doDisplay == true) myApp = new TApplication("myApp", nullptr, nullptr);
this->InitResultFile(fileRes);
LOG(INFO) << BOLDBLUE << "\t--> DataTransmissionTest saving histograms..." << RESET;
histos->book(fResultFile, *fDetectorContainer, fSettingsMap);
DataTransmissionTest::fillHisto();
histos->process();
this->WriteRootFile();
if(doDisplay == true) myApp->Run(true);
this->CloseResultFile();
#endif
}
void DataTransmissionTest::analyze(const DetectorDataContainer& theTAP0scanContainer, DetectorDataContainer& theTAP0tgtContainer)
{
ContainerFactory::copyAndInitChip<uint16_t>(*fDetectorContainer, theTAP0tgtContainer);
for(const auto cBoard: theTAP0scanContainer)
for(const auto cOpticalGroup: *cBoard)
for(const auto cHybrid: *cOpticalGroup)
for(const auto cChip: *cHybrid)
{
double nearestBERup = 2; // to store the nearest BER value to the target
double nearestBERlo = 2; // to store the second nearest BER value to the target
double nearestTAP0up = 0; // to store the TAP0 value that gives the nearest BER
double nearestTAP0lo = 0; // to store the TAP0 value that gives the second nearest BER
// Find the TAP0 values nearest to the target BER
for(auto i = 0u; i < 11u; i++)
{
auto currentTAP0 = std::get<0>((cChip->getSummary<std::array<std::tuple<uint16_t, double, double, double>, 11>>())[i]);
auto currentBER = std::get<1>((cChip->getSummary<std::array<std::tuple<uint16_t, double, double, double>, 11>>())[i]);
if(currentBER > 0 && currentBER >= BERtarget &&
(fabs(currentBER - BERtarget) < fabs(nearestBERup-BERtarget) || (fabs(currentBER - BERtarget) == fabs(nearestBERup-BERtarget) && currentTAP0 > nearestTAP0up)))
{
nearestBERup = currentBER;
nearestTAP0up = (double)currentTAP0;
}
else if(currentBER > 0 && currentBER < BERtarget &&
(fabs(currentBER - BERtarget) < fabs(nearestBERlo-BERtarget) || (fabs(currentBER - BERtarget) == fabs(nearestBERlo-BERtarget) && currentTAP0 < nearestTAP0lo)))
{
nearestBERlo = currentBER;
nearestTAP0lo = (double)currentTAP0;
}
}
// Saving the very nearest value
auto nearestTAP0 = fabs(BERtarget-nearestBERup) < fabs(BERtarget-nearestBERlo) ? nearestTAP0up : nearestTAP0lo;
LOG(INFO) << BOLDMAGENTA << ">>> TAP0 value at target (BER=" << std::setprecision(4) << std::scientific << BOLDYELLOW << BERtarget << BOLDMAGENTA
<< std::fixed << std::setprecision(0) << ") for [board/opticalGroup/hybrid/chip = " << BOLDYELLOW << cBoard->getId() << "/" << cOpticalGroup->getId() << "/"
<< cHybrid->getId() << "/" << +cChip->getId() << BOLDMAGENTA << "] is " << BOLDYELLOW << nearestTAP0 << BOLDMAGENTA << " <<<" << RESET;
// Fill the container with TAP0 value at target BER
theTAP0tgtContainer.at(cBoard->getIndex())->at(cOpticalGroup->getIndex())->at(cHybrid->getIndex())->at(cChip->getIndex())->getSummary<uint16_t>() = (uint16_t)nearestTAP0;
// Fitting the last two points with exp(a-bx)
if(nearestBERup <= 1 && nearestBERlo < BERtarget)
{
double a = log(nearestBERup) + log(nearestBERup/nearestBERlo) * nearestTAP0up / (nearestTAP0lo-nearestTAP0up);
double b = log(nearestBERup/nearestBERlo) / (nearestTAP0lo-nearestTAP0up);
LOG(INFO) << BOLDBLUE << "Exponential fit function between the two points around target (" << BOLDYELLOW
<< nearestTAP0up << BOLDBLUE << ", " << BOLDYELLOW << nearestTAP0lo << BOLDBLUE << "): BER = EXP(a-b*TAP0)" << RESET;
LOG(INFO) << BOLDBLUE << std::scientific << std::setprecision(5) << "a = " << BOLDYELLOW << a << RESET;
LOG(INFO) << BOLDBLUE << "b = " << BOLDYELLOW << b << RESET;
if (fabs(nearestTAP0lo-nearestTAP0up)>1)
LOG(INFO) << BOLDYELLOW << "You might want to consider adjusting the BER target as the distance between nearest two points is >2 (likely due to fluctuations)" << RESET;
}
else
LOG(ERROR) << BOLDRED << "Could not find two closest points around the BER target that are proper to fit. BER target should be adjusted." << RESET;
}
}
void DataTransmissionTest::fillHisto()
{
#ifdef __USE_ROOT__
histos->fillTAP0scan(theTAP0scanContainer);
histos->fillTAP0tgt(theTAP0tgtContainer);
#endif
}
void DataTransmissionTest::binSearch(DetectorDataContainer* theTAP0scanContainer)
{
uint16_t currentTAP0 = 1023; // start from highest TAP0
uint16_t step = 512; // next point will be 512 units away
uint8_t timesRepeated = 0; // to be used for dealing with errors
for(auto i = 0u; i < 11u; i++)
{
// Setting new TAP0 value
LOG(INFO) << BOLDMAGENTA << ">>> " << BOLDYELLOW << "CML_TAP0_BIAS" << BOLDMAGENTA << " value = " << BOLDYELLOW << currentTAP0 << BOLDMAGENTA << " <<<" << RESET;
for(const auto cBoard: *fDetectorContainer