Skip to content
Open
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
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
64 changes: 64 additions & 0 deletions otsdaq-mu2e-tracker/Ui/BisectionSearch.hh
Original file line number Diff line number Diff line change
@@ -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 <math.h>
#include <string>
#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<typename T, typename U, typename functor>
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<T>((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
1 change: 1 addition & 0 deletions otsdaq-mu2e-tracker/Ui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
159 changes: 159 additions & 0 deletions otsdaq-mu2e-tracker/Ui/DtcInterface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<roc_data_t> writeable = {
static_cast<uint16_t>(channel.Channel()), // channel number
dac, // programmed value
static_cast<uint16_t>(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<PreampThreshold> DtcInterface::QueryThresholds(const DTC_Link_ID& Link){
// write parameters into roc to initiate routine
vector<roc_data_t> 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<PreampThreshold> 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<PreampChannel>& channels,
std::vector<double>& 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
//-----------------------------------------------------------------------------
Expand Down
30 changes: 30 additions & 0 deletions otsdaq-mu2e-tracker/Ui/DtcInterface.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<PreampThreshold>
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<PreampChannel>& channels,
std::vector<double>& 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
Expand Down
34 changes: 34 additions & 0 deletions otsdaq-mu2e-tracker/Ui/PreampChannel.cc
Original file line number Diff line number Diff line change
@@ -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;
}
26 changes: 26 additions & 0 deletions otsdaq-mu2e-tracker/Ui/PreampChannel.hh
Original file line number Diff line number Diff line change
@@ -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 <string>
#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
Loading
Loading