diff --git a/CMakeLists.txt b/CMakeLists.txt index 507a164..316b6be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,13 +74,13 @@ find_package(otsdaq-mu2e 1.02.00 REQUIRED EXPORT) find_package(mu2e-pcie-utils 3.02.00 REQUIRED EXPORT) # message("---------- otsdaq-mu2e-tracker: PRINT CMAKE VARIABLES " ) -# +# # get_cmake_property(_variableNames VARIABLES) # list (SORT _variableNames) # foreach (_variableName ${_variableNames}) # message(STATUS "${_variableName}=${${_variableName}}") # endforeach() - + # if (DEFINED ENV{MIDASSYS}) # message(-- P.Murat: otsdaq-mu2e-tracker: MIDASSYS=.$ENV{MIDASSYS}. ) # find_package(Midas REQUIRED) diff --git a/otsdaq-mu2e-tracker/Ui/BisectionSearch.hh b/otsdaq-mu2e-tracker/Ui/BisectionSearch.hh new file mode 100644 index 0000000..3660f24 --- /dev/null +++ b/otsdaq-mu2e-tracker/Ui/BisectionSearch.hh @@ -0,0 +1,64 @@ +// Ed Callaghan +// Bisection search over a discrete search space, templated to avoid sorting out functor types +// July 2024 + +#ifndef BisectionSearch_hh +#define BisectionSearch_hh + +#include +#include +#include "cetlib_except/exception.h" + +// search for a threshold-crossing of a monotonically increasing function +// of one discrete variable, using the classic bisection method +// +// T is type of discrete search space +// U is type of target space +// functor is type of whatever is callable, e.g. a class with operator() +template +T bisection_search(functor f, U target, U tolerance, T left, T right){ + // first, some short-circuits: + // a crossing may or may not be found, but search windows on discrete spaces + // may collapse to a single point, at which time the search must terminate + if (left == right){ + return left; + } + // sanity check that the bounds are ordered by increasing function value + else if (!(left < right)){ + std::string msg = "unsorted bisection search bounds"; + throw cet::exception("bisection_search") << msg << std::endl; + } + // effectively the same as the first case: an incompressible search window + else if (right - left == 1){ + return left; + } + + // evalaute function in the middle of the search window + T middle = static_cast((left + right) / 2); + U current = f(middle); + U error = fabs(current - target); + + // branch for next iteration + T rv = 0; + // if within tolerance, exit + if (error < tolerance){ + rv = middle; + } + // if we are left-of/under threshold, search the right half + else if (current < target){ + rv = bisection_search(f, target, tolerance, middle, right); + } + // if we are right-of/above threshold, search the left half + else if (target < current){ + rv = bisection_search(f, target, tolerance, left, middle); + } + // sould never reach here + else{ + std::string msg = "reached impossible state"; + throw cet::exception("bisection_search") << msg << std::endl; + } + + return rv; +} + +#endif diff --git a/otsdaq-mu2e-tracker/Ui/CMakeLists.txt b/otsdaq-mu2e-tracker/Ui/CMakeLists.txt index 3d0e1f6..d86c9c6 100644 --- a/otsdaq-mu2e-tracker/Ui/CMakeLists.txt +++ b/otsdaq-mu2e-tracker/Ui/CMakeLists.txt @@ -5,6 +5,7 @@ cet_make_library( SOURCE DtcInterface.cc DtcInterface_print.cc DtcInterface_ControlRoc.cc CfoInterface.cc + PreampChannel.cc PreampThreshold.cc LIBRARIES PUBLIC otsdaq-mu2e-tracker::ParseAlignment artdaq::DAQdata diff --git a/otsdaq-mu2e-tracker/Ui/DtcInterface.cc b/otsdaq-mu2e-tracker/Ui/DtcInterface.cc index 575b58b..156c097 100644 --- a/otsdaq-mu2e-tracker/Ui/DtcInterface.cc +++ b/otsdaq-mu2e-tracker/Ui/DtcInterface.cc @@ -1086,6 +1086,165 @@ void DtcInterface::FindAlignments(bool print, int LinkMask) } } + + void DtcInterface::ProgramThreshold(const DTC_Link_ID& Link, + const PreampChannel& channel, + const roc_data_t dac){ + // write parameters into roc to initiate routine + vector writeable = { + static_cast(channel.Channel()), // channel number + dac, // programmed value + static_cast(channel.Side()), // hv/cal side + }; + + // register 267: set threshold + bool increment_address = false; // read via fifo + fDtc->WriteROCBlock(Link, 267, writeable, false, increment_address, 100); + std::this_thread::sleep_for(std::chrono::microseconds(gSleepTimeROCWrite)); + + // TODO there is a confirmation readback + // then, wait till reg 128 returns non-zero + uint16_t u; + while ((u = fDtc->ReadROCRegister(Link, 128, 100)) != 0x8000){ + // idle + } + + // read back payload and validate against input parameters + auto returned = this->ReadROCBlockEnsured(Link, 267); + if (returned.size() != writeable.size()){ + string msg = "block read of bad length: expected " + + to_string(writeable.size()) + + ", got " + + to_string(returned.size()); + throw cet::exception("DtcInterface::ProgramThreshold") << msg; + } + if (channel.Side() == PreampChannel::Parity::hv){ + writeable[0] += 96; + } + for (size_t i = 0 ; i < returned.size() ; i++){ + if (returned[i] != writeable[i]){ + string msg = "back readback value at index " + + to_string(i) + + ", expected " + + to_string(writeable[i]) + + ", got " + + to_string(returned[i]); + throw cet::exception("DtcInterface::ProgramThreshold") << msg; + } + } + } + + vector DtcInterface::QueryThresholds(const DTC_Link_ID& Link){ + // write parameters into roc to initiate routine + vector writeable = { + 0xFFFF, // bitmask for channels 0 - 15 + 0xFFFF, // bitmask for channels 16 - 31 + 0xFFFF, // bitmask for channels 32 - 47 + 0xFFFF, // bitmask for channels 48 - 63 + 0xFFFF, // bitmask for channels 64 - 79 + 0xFFFF, // bitmask for channels 80 - 95 + }; + + // register 270: measure thresholds + bool increment_address = false; // read via fifo + fDtc->WriteROCBlock(Link, 270, writeable, false, increment_address, 100); + std::this_thread::sleep_for(std::chrono::microseconds(gSleepTimeROCWrite)); + + // then, wait till reg 128 returns non-zero + uint16_t u; + while ((u = fDtc->ReadROCRegister(Link, 128, 100)) != 0x8000){ + // idle + } + + // read back payload + auto returned = this->ReadROCBlockEnsured(Link, 270); + + // TODO magic numbers = bad + if (returned.size() != 288){ + string msg = "incomplete threshold vector: size = " + + to_string(returned.size()); + throw cet::exception("DtcInterface::QueryThresholds") << msg << endl; + } + + // TODO magic numbers = bad + vector rv; + for (size_t i = 0 ; i < 96 ; i++){ + size_t h_idx = 0 + i; + size_t c_idx = 96 + i; + size_t t_idx = 192 + i; + + double c_threshold = PreampThreshold::ComputeAnalogValue(returned[c_idx]); + double h_threshold = PreampThreshold::ComputeAnalogValue(returned[h_idx]); + double t_threshold = PreampThreshold::ComputeAnalogValue(returned[t_idx]); + + rv.emplace_back(c_threshold, h_threshold, t_threshold); + + /* FIXME rm debug block + if (returned[c_idx] != 65535 || returned[h_idx] != 65535 || returned[t_idx] != 65535){ + cout << i << ": " + << returned[c_idx] << " " + << returned[h_idx] << " " + << returned[t_idx] << endl; + } + */ + } + + return rv; + } + + double DtcInterface::ProgramAndQueryThreshold(const DTC_Link_ID& Link, + const PreampChannel& channel, + const roc_data_t dac){ + this->ProgramThreshold(Link, channel, dac); + auto queried = this->QueryThresholds(Link); + auto thresholds = queried.at(channel.Channel()); + auto rv = thresholds.GetThreshold(channel.Side()); + return rv; + } + + bool DtcInterface::SetThreshold(const DTC_Link_ID& Link, + const PreampChannel& channel, + const double threshold, + const double tolerance){ + roc_data_t lower = 0; + roc_data_t upper = 1023; + auto f = [this,Link,channel] (roc_data_t dac) -> double { + auto rv = this->ProgramAndQueryThreshold(Link, channel, dac); + cout << dac << ": " << rv << endl; + return rv; + }; + auto dac = bisection_search(f, -threshold, tolerance, lower, upper); + auto measured = this->ProgramAndQueryThreshold(Link, channel, dac); + + // return whether or not the search was successful + auto rv = false; + if (fabs(measured - threshold) < tolerance){ + rv = true; + } + + return rv; + } + + bool DtcInterface::SetThresholds(const DTC_Link_ID& Link, + const std::vector& channels, + std::vector& thresholds, + const double tolerance){ + if (channels.size() != thresholds.size()){ + string msg = "Mismatched channel/threshold count when setting thresholds"; + msg += to_string(channels.size()) + " channels vs " + + to_string(thresholds.size()) + " thresholds"; + throw cet::exception("DtcInterface::SetThresholds") << msg << endl; + } + + // if any channel fails to set, return false + bool rv = true; + for (size_t i = 0 ; i < channels.size() ; i++){ + rv &= this->SetThreshold(Link, channels[i], thresholds[i], tolerance); + } + + return rv; + } + //----------------------------------------------------------------------------- // configure itself to use a CFO //----------------------------------------------------------------------------- diff --git a/otsdaq-mu2e-tracker/Ui/DtcInterface.hh b/otsdaq-mu2e-tracker/Ui/DtcInterface.hh index 02c9e39..42c28df 100644 --- a/otsdaq-mu2e-tracker/Ui/DtcInterface.hh +++ b/otsdaq-mu2e-tracker/Ui/DtcInterface.hh @@ -17,6 +17,10 @@ #include "otsdaq-mu2e-tracker/ParseAlignment/Alignment.hh" #include "otsdaq-mu2e-tracker/ParseAlignment/PrintLegacyTable.hh" +#include "otsdaq-mu2e-tracker/Ui/BisectionSearch.hh" +#include "otsdaq-mu2e-tracker/Ui/PreampChannel.hh" +#include "otsdaq-mu2e-tracker/Ui/PreampThreshold.hh" +#include "otsdaq-mu2e-tracker/Ui/TrkSpiData.hh" #include "otsdaq-mu2e-tracker/Ui/ControlRocTypes.hh" namespace trkdaq @@ -127,6 +131,32 @@ class DtcInterface int LinkEnabled(int Link) { return (fLinkMask >> 4 * Link) & 0xf; } + Alignment FindAlignment(DTCLib::DTC_Link_ID Link); + void FindAlignments(bool print=false, int LinkMask=0); + + // TODO revisit these signatures --- what should be vectorized, what not... + void ProgramThreshold(const DTCLib::DTC_Link_ID& Link, + const PreampChannel& channel, + const DTCLib::roc_data_t dac); + std::vector + QueryThresholds(const DTCLib::DTC_Link_ID& Link); + double ProgramAndQueryThreshold(const DTCLib::DTC_Link_ID& Link, + const PreampChannel& channel, + const DTCLib::roc_data_t dac); + bool SetThreshold(const DTCLib::DTC_Link_ID& Link, + const PreampChannel& channel, + const double threshold, + const double tolerance); + bool SetThresholds(const DTCLib::DTC_Link_ID& Link, + const std::vector& channels, + std::vector& thresholds, + const double tolerance); + +//----------------------------------------------------------------------------- +// ROC functions +// if LinkMask=0, use fLinkMask +//----------------------------------------------------------------------------- + void ResetRoc (int LinkMask = 0, int SetNewMask = 0); // SampleEdgeMode=0: force rising edge // 1: force falling edge // 2: auto diff --git a/otsdaq-mu2e-tracker/Ui/PreampChannel.cc b/otsdaq-mu2e-tracker/Ui/PreampChannel.cc new file mode 100644 index 0000000..41242ec --- /dev/null +++ b/otsdaq-mu2e-tracker/Ui/PreampChannel.cc @@ -0,0 +1,34 @@ +// Ed Callaghan +// Encapsulate channel-number and parity of preamp nomenclatur into singly-loopable objects +// July 2024 + +#include "otsdaq-mu2e-tracker/Ui/PreampChannel.hh" + +PreampChannel::PreampChannel(unsigned int channel, PreampChannel::Parity side): + channel(channel), side(side){ + if (this->side == PreampChannel::Parity::total){ + std::string msg = "Cannot instantiate a preamp on the \"total\" side"; + throw cet::exception("PreampChannel::PreampChannel") << msg << std::endl; + } +} + +unsigned int PreampChannel::Channel(){ + auto rv = this->channel; + return rv; +} + +PreampChannel::Parity PreampChannel::Side(){ + auto rv = this->side; + return side; +} + +bool PreampChannel::IsHV(){ + bool rv; + if (this->Side() == Parity::hv){ + rv = true; + } + else{ + rv = false; + } + return rv; +} diff --git a/otsdaq-mu2e-tracker/Ui/PreampChannel.hh b/otsdaq-mu2e-tracker/Ui/PreampChannel.hh new file mode 100644 index 0000000..a90bddf --- /dev/null +++ b/otsdaq-mu2e-tracker/Ui/PreampChannel.hh @@ -0,0 +1,26 @@ +// Ed Callaghan +// Encapsulate channel-number and Parity of preamp nomenclatur into singly-loopable objects +// July 2024 + +#ifndef PreampChannel_hh +#define PreampChannel_hh + +#include +#include "cetlib_except/exception.h" + +class PreampChannel{ + public: + enum Parity {cal=0, hv=1, total=2}; + PreampChannel(unsigned int channel, Parity side); + + unsigned int Channel(); + Parity Side(); + bool IsHV(); + protected: + unsigned int channel; + Parity side; + private: + /**/ +}; + +#endif diff --git a/otsdaq-mu2e-tracker/Ui/PreampThreshold.cc b/otsdaq-mu2e-tracker/Ui/PreampThreshold.cc new file mode 100644 index 0000000..5091c74 --- /dev/null +++ b/otsdaq-mu2e-tracker/Ui/PreampThreshold.cc @@ -0,0 +1,53 @@ +// Ed Callaghan +// Encapsulate threshold triplets +// July 2024 + +#include "otsdaq-mu2e-tracker/Ui/PreampThreshold.hh" + +PreampThreshold::PreampThreshold(double c_threshold, + double h_threshold, + double t_threshold): + c_threshold(c_threshold), + h_threshold(h_threshold), + t_threshold(t_threshold){ + /**/ +} + +double PreampThreshold::GetThreshold(const PreampChannel::Parity& parity) const{ + double rv = 0; + if (parity == PreampChannel::Parity::cal){ + rv = this->GetCalThreshold(); + } + else if (parity == PreampChannel::Parity::hv){ + rv = this->GetCalThreshold(); + } + else if (parity == PreampChannel::Parity::total){ + rv = this->GetTotalThreshold(); + } + else{ + std::string msg = "Impossible preamp parity: " + parity; + throw cet::exception("PreampChannel::PreampChannel") << msg << std::endl; + } + return rv; +} + +double PreampThreshold::GetCalThreshold() const{ + auto rv = this->c_threshold; + return rv; +} + +double PreampThreshold::GetHVThreshold() const{ + auto rv = this->h_threshold; + return rv; +} + +double PreampThreshold::GetTotalThreshold() const{ + auto rv = this->t_threshold; + return rv; +} + +double PreampThreshold::ComputeAnalogValue(const DTCLib::roc_data_t adc){ + // in mV + auto rv = -100.0 + adc * (200.0 / 1024); + return rv; +} diff --git a/otsdaq-mu2e-tracker/Ui/PreampThreshold.hh b/otsdaq-mu2e-tracker/Ui/PreampThreshold.hh new file mode 100644 index 0000000..2d7a5d1 --- /dev/null +++ b/otsdaq-mu2e-tracker/Ui/PreampThreshold.hh @@ -0,0 +1,31 @@ +// Ed Callaghan +// Encapsulate threshold triplets +// July 2024 + +#ifndef PreampThreshold_hh +#define PreampThreshold_hh + +#include +#include "cetlib_except/exception.h" +#include "dtcInterfaceLib/DTC.h" +#include "otsdaq-mu2e-tracker/Ui/PreampChannel.hh" + +class PreampThreshold{ + public: + PreampThreshold(double c_threshold, double h_threshold, double t_threshold); + + double GetThreshold(const PreampChannel::Parity& parity) const; + double GetCalThreshold() const; + double GetHVThreshold() const; + double GetTotalThreshold() const; + + static double ComputeAnalogValue(const DTCLib::roc_data_t adc); + protected: + double c_threshold; // cal side + double h_threshold; // hv side + double t_threshold; // total + private: + /**/ +}; + +#endif