Release of 5.1.0 (#237)

* Setting pattern from memory (#218)

* ToString accepts c-style arrays

* fixed patwait time bug in validation

* Introduced pattern class

* compile for servers too

* Python binding for Pattern

* added scanParameters in Python

* slsReceiver: avoid potential memory leak around Implementation::generalData

* additional constructors for scanPrameters in python

* bugfix: avoid potentital memory leak in receiver if called outside constructor context

* added scanParameters in Python

* additional constructors for scanPrameters in python

* M3defaultpattern (#227)

* default pattern for m3 and moench including Python bindings

* M3settings (#228)

* some changes to compile on RH7 and in the server to load the default chip status register at startup

* Updated mythen3DeectorServer_developer executable with correct initialization at startup

Co-authored-by: Erik Frojdh <erik.frojdh@gmail.com>
Co-authored-by: Anna Bergamaschi <anna.bergamaschi@psi.ch>

* Pattern.h as a public header files (#229)

* fixed buffer overflow but caused by using global instead of local enum

* replacing out of range trimbits with edge values

* replacing dac values that are out of range after interpolation

* updated pybind11 to 2.6.2

* Mythen3 improved synchronization (#231)

Disabling scans for multi module Mythen3, since there is no feedback of the detectors being ready
startDetector first starts the slaves then the master
acquire firs calls startDetector for the slaves then acquire on the master
getMaster to read back from hardware which one is master

* New server for JF to go with the new FW (#232)

* Modified Jungfrau speed settings for HW1.0 - FW fix version 1.1.1, compilation date 210218

* Corrected bug. DBIT clk phase is implemented in both HW version 1.0 and 2.0. Previous version did not update the DBIT phase shift on the configuration of a speed.

* fix for m3 scan with single module

* m3 fw version

* m3 server

* bugfix for bottom when setting quad

* new strategy for finding zmq based on cppzmq



Co-authored-by: Dhanya Thattil <dhanya.thattil@psi.ch>
Co-authored-by: Dhanya Thattil <33750417+thattil@users.noreply.github.com>
Co-authored-by: Alejandro Homs Puron <ahoms@esrf.fr>
Co-authored-by: Anna Bergamaschi <anna.bergamaschi@psi.ch>
Co-authored-by: Xiaoqiang Wang <xiaoqiangwang@gmail.com>
Co-authored-by: lopez_c <carlos.lopez-cuenca@psi.ch>
This commit is contained in:
Erik Fröjdh
2021-03-22 14:43:11 +01:00
committed by GitHub
parent 64de9f3ad0
commit 2f2fe4dd47
168 changed files with 3614 additions and 9057 deletions

View File

@ -4,6 +4,7 @@ set(SOURCES
src/Detector.cpp
src/CmdProxy.cpp
src/CmdParser.cpp
src/Pattern.cpp
)
add_library(slsDetectorObject OBJECT
@ -30,6 +31,7 @@ set(PUBLICHEADERS
include/sls/detectorData.h
include/sls/Detector.h
include/sls/Result.h
include/sls/Pattern.h
)
#Shared library

View File

@ -1,4 +1,5 @@
#pragma once
#include "sls/Pattern.h"
#include "sls/Result.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_defs.h"
@ -109,7 +110,7 @@ class Detector {
/** list of possible settings for this detector */
std::vector<defs::detectorSettings> getSettingsList() const;
/** [Jungfrau][Gotthard][Gotthard2] */
/** [Jungfrau][Gotthard][Gotthard2][Mythen3] */
Result<defs::detectorSettings> getSettings(Positions pos = {}) const;
/** [Jungfrau] DYNAMICGAIN, DYNAMICHG0, FIXGAIN1, FIXGAIN2,
@ -117,12 +118,34 @@ class Detector {
* LOWGAIN, MEDIUMGAIN, VERYHIGHGAIN \n [Gotthard2] DYNAMICGAIN,
* FIXGAIN1, FIXGAIN2 \n [Moench] G1_HIGHGAIN, G1_LOWGAIN,
* G2_HIGHCAP_HIGHGAIN, G2_HIGHCAP_LOWGAIN, G2_LOWCAP_HIGHGAIN,
* G2_LOWCAP_LOWGAIN, G4_HIGHGAIN, G4_LOWGAIN \n [Eiger] Use threshold
* command. Settings loaded from file found in
* settingspath
* G2_LOWCAP_LOWGAIN, G4_HIGHGAIN, G4_LOWGAIN \n [Mythen3] STANDARD, FAST,
* HIGHGAIN. Also changes vrshaper and vrpreamp \n [Eiger] Use threshold
* command. Settings loaded from file found in settingspath
*/
void setSettings(defs::detectorSettings value, Positions pos = {});
/** [Eiger] */
Result<int> getThresholdEnergy(Positions pos = {}) const;
/** Mythen3] threshold energy for the three counters */
Result<std::array<int, 3>> getAllThresholdEnergy(Positions pos = {}) const;
/** [Eiger][Mythen3] It loads trim files from settingspath */
void setThresholdEnergy(int threshold_ev,
defs::detectorSettings settings = defs::STANDARD,
bool trimbits = true, Positions pos = {});
/** [Mythen3] It loads trim files from settingspath */
void setThresholdEnergy(std::array<int, 3> threshold_ev,
defs::detectorSettings settings = defs::STANDARD,
bool trimbits = true, Positions pos = {});
/** [Eiger][Mythen3] */
Result<std::string> getSettingsPath(Positions pos = {}) const;
/** [Eiger][Mythen3] Directory where settings files are loaded from/to */
void setSettingsPath(const std::string &value, Positions pos = {});
/** [Eiger][Mythen3] If no extension specified, serial number of each module
* is attached. */
void loadTrimbits(const std::string &fname, Positions pos = {});
@ -133,6 +156,13 @@ class Detector {
/**[Eiger][Mythen3] */
void setAllTrimbits(int value, Positions pos = {});
/**[Eiger][Mythen3] Returns energies in eV where the module is trimmed */
Result<std::vector<int>> getTrimEnergies(Positions pos = {}) const;
/** [Eiger][Mythen3] List of trim energies, where corresponding default trim
* files exist in corresponding trim folders */
void setTrimEnergies(std::vector<int> energies, Positions pos = {});
/**[Eiger][Jungfrau] */
bool getGapPixelsinCallback() const;
@ -939,20 +969,6 @@ class Detector {
/** [Eiger] in 32 bit mode */
void setSubDeadTime(ns value, Positions pos = {});
/** [Eiger] */
Result<int> getThresholdEnergy(Positions pos = {}) const;
/** [Eiger] It loads trim files from settingspath */
void setThresholdEnergy(int threshold_ev,
defs::detectorSettings settings = defs::STANDARD,
bool trimbits = true, Positions pos = {});
/** [Eiger] */
Result<std::string> getSettingsPath(Positions pos = {}) const;
/** [Eiger] Directory where settings files are loaded from/to */
void setSettingsPath(const std::string &value, Positions pos = {});
/** [Eiger] */
Result<bool> getOverFlowMode(Positions pos = {}) const;
@ -965,13 +981,6 @@ class Detector {
/** [Eiger] for client call back (gui) purposes to flip bottom image */
void setBottom(bool value, Positions pos = {});
/**[Eiger] Returns energies in eV where the module is trimmed */
Result<std::vector<int>> getTrimEnergies(Positions pos = {}) const;
/** [Eiger] List of trim energies, where corresponding default trim files
* exist in corresponding trim folders */
void setTrimEnergies(std::vector<int> energies, Positions pos = {});
/** [Eiger] deadtime in ns, 0 = disabled */
Result<ns> getRateCorrection(Positions pos = {}) const;
@ -1296,6 +1305,9 @@ class Detector {
/** [Mythen3] gate delay for all gates in auto or trigger timing mode
* (internal gating). Gate index: 0-2, -1 for all */
Result<std::array<ns, 3>> getGateDelayForAllGates(Positions pos = {}) const;
Result<bool> getMaster(Positions pos = {}) const;
///@{
/** @name CTB / Moench Specific */
@ -1457,10 +1469,17 @@ class Detector {
* (instead of executing line by line)*/
void setPattern(const std::string &fname, Positions pos = {});
/** [CTB][Moench][Mythen3] Loads pattern parameters structure directly to
* server */
void setPattern(const Pattern &pat, Positions pos = {});
/** [CTB][Moench][Mythen3] [Ctb][Moench][Mythen3] Saves pattern to file
* (ascii). \n [Ctb][Moench] Also executes pattern.*/
void savePattern(const std::string &fname);
/** [Mythen3][Moench] Loads and runs default pattern */
void loadDefaultPattern(Positions pos = {});
/** [CTB][Moench] */
Result<uint64_t> getPatternIOControl(Positions pos = {}) const;

View File

@ -0,0 +1,41 @@
#pragma once
#include "sls/sls_detector_defs.h"
#ifdef __cplusplus
#include <memory>
namespace sls {
#endif
// Common C/C++ structure to handle pattern data
typedef struct __attribute__((packed)) {
uint64_t word[MAX_PATTERN_LENGTH];
uint64_t ioctrl;
uint32_t limits[2];
// loop0 start, loop0 stop .. loop2 start, loop2 stop
uint32_t loop[6];
uint32_t nloop[3];
uint32_t wait[3];
uint64_t waittime[3];
} patternParameters;
#ifdef __cplusplus
class Pattern {
patternParameters *pat = new patternParameters{};
public:
Pattern();
~Pattern();
Pattern(const Pattern &other);
bool operator==(const Pattern &other) const;
bool operator!=(const Pattern &other) const;
patternParameters *data();
patternParameters *data() const;
size_t size() const noexcept { return sizeof(patternParameters); }
void validate() const;
void load(const std::string &fname);
void save(const std::string &fname);
std::string str() const;
};
} // namespace sls
#endif

View File

@ -54,7 +54,7 @@ bool CmdProxy::ReplaceIfDepreciated(std::string &command) {
if (d_it != depreciated_functions.end()) {
LOG(logWARNING)
<< command
<< " is depreciated and will be removed. Please migrate to: "
<< " is deprecated and will be removed. Please migrate to: "
<< d_it->second;
// insert old command into arguments (for dacs)
if (d_it->second == "dac") {
@ -356,6 +356,119 @@ std::string CmdProxy::DetectorSize(int action) {
return os.str();
}
std::string CmdProxy::Threshold(int action) {
std::ostringstream os;
os << cmd << ' ';
if (action == defs::HELP_ACTION) {
os << "[eV] [(optinal settings)"
"\n\t[Eiger][Mythen3] Threshold in eV. It loads trim files from "
"settingspath.";
if (cmd == "thresholdnotb") {
os << "Trimbits are not loaded.";
}
os << "\n\nthreshold [eV1] [eV2] [eV3] [(optional settings)]"
"\n\t[Mythen3] Threshold in eV for each counter. It loads trim "
"files from "
"settingspath.";
if (cmd == "thresholdnotb") {
os << "Trimbits are not loaded.";
}
os << '\n';
} else if (action == defs::GET_ACTION) {
if (cmd == "thresholdnotb") {
throw sls::RuntimeError("cannot get");
}
if (!args.empty()) {
WrongNumberOfParameters(0);
}
defs::detectorType type = det->getDetectorType().squash();
if (type == defs::EIGER) {
auto t = det->getThresholdEnergy(std::vector<int>{det_id});
os << OutString(t) << '\n';
} else if (type == defs::MYTHEN3) {
auto t = det->getAllThresholdEnergy(std::vector<int>{det_id});
os << OutString(t) << '\n';
} else {
throw RuntimeError("Not implemented for this detector\n");
}
} else if (action == defs::PUT_ACTION) {
defs::detectorType type = det->getDetectorType().squash();
if (type == defs::EIGER && args.size() != 1 && args.size() != 2) {
WrongNumberOfParameters(1);
}
if (type == defs::MYTHEN3 && (args.size() < 1 || args.size() > 4)) {
WrongNumberOfParameters(1);
}
bool trimbits = (cmd == "thresholdnotb") ? false : true;
std::array<int, 3> energy = {StringTo<int>(args[0]), 0, 0};
energy[1] = energy[0];
energy[2] = energy[0];
defs::detectorSettings sett = defs::STANDARD;
// check if argument has settings or get it
if (args.size() == 2 || args.size() == 4) {
sett = StringTo<defs::detectorSettings>(args[args.size() - 1]);
} else {
sett = det->getSettings(std::vector<int>{det_id})
.tsquash("Inconsistent settings between detectors");
}
// get other threshold values
if (args.size() > 2) {
energy[1] = StringTo<int>(args[1]);
energy[2] = StringTo<int>(args[2]);
}
switch (type) {
case defs::EIGER:
det->setThresholdEnergy(energy[0], sett, trimbits,
std::vector<int>{det_id});
break;
case defs::MYTHEN3:
det->setThresholdEnergy(energy, sett, trimbits,
std::vector<int>{det_id});
break;
default:
throw RuntimeError("Not implemented for this detector\n");
}
os << ToString(args) << '\n';
} else {
throw sls::RuntimeError("Unknown action");
}
return os.str();
}
std::string CmdProxy::TrimEnergies(int action) {
std::ostringstream os;
os << cmd << ' ';
if (action == defs::HELP_ACTION) {
os << "[trim_ev1] [trim_Ev2 (optional)] [trim_ev3 (optional)] "
"...\n\t[Eiger][Mythen3] Number of trim energies and list of "
"trim "
"energies, where corresponding default trim files exist in "
"corresponding trim folders."
<< '\n';
} else if (action == defs::GET_ACTION) {
if (!args.empty()) {
WrongNumberOfParameters(0);
}
auto t = det->getTrimEnergies(std::vector<int>{det_id});
os << OutString(t) << '\n';
} else if (action == defs::PUT_ACTION) {
std::vector<int> t(args.size());
if (!args.empty()) {
for (size_t i = 0; i < t.size(); ++i) {
t[i] = StringTo<int>(args[i]);
}
}
det->setTrimEnergies(t, std::vector<int>{det_id});
os << sls::ToString(args) << '\n';
} else {
throw sls::RuntimeError("Unknown action");
}
return os.str();
}
std::string CmdProxy::GapPixels(int action) {
std::ostringstream os;
os << cmd << ' ';
@ -1264,103 +1377,6 @@ std::string CmdProxy::ZMQHWM(int action) {
/* Eiger Specific */
std::string CmdProxy::Threshold(int action) {
std::ostringstream os;
os << cmd << ' ';
if (action == defs::HELP_ACTION) {
os << "[eV] [(optinal settings) standard, lowgain, veryhighgain, "
"verylowgain]"
"\n\t[Eiger] Threshold in eV. It loads trim files from "
"settingspath."
<< '\n';
} else if (action == defs::GET_ACTION) {
if (!args.empty()) {
WrongNumberOfParameters(0);
}
auto t = det->getThresholdEnergy(std::vector<int>{det_id});
os << OutString(t) << '\n';
} else if (action == defs::PUT_ACTION) {
if (args.size() == 1) {
auto t = det->getSettings(std::vector<int>{det_id})
.tsquash("Inconsistent settings between detectors");
det->setThresholdEnergy(StringTo<int>(args[0]), t, true,
std::vector<int>{det_id});
} else if (args.size() == 2) {
det->setThresholdEnergy(
StringTo<int>(args[0]),
sls::StringTo<slsDetectorDefs::detectorSettings>(args[1]), true,
{det_id});
} else {
WrongNumberOfParameters(1);
}
os << ToString(args) << '\n';
} else {
throw sls::RuntimeError("Unknown action");
}
return os.str();
}
std::string CmdProxy::ThresholdNoTb(int action) {
std::ostringstream os;
os << cmd << ' ';
if (action == defs::HELP_ACTION) {
os << "[eV] [(optional settings) standard, lowgain, veryhighgain, "
"verylowgain]"
"\n\t[Eiger] Threshold in eV set without setting trimbits"
<< '\n';
} else if (action == defs::GET_ACTION) {
throw sls::RuntimeError("cannot get");
} else if (action == defs::PUT_ACTION) {
if (args.size() == 1) {
auto t = det->getSettings(std::vector<int>{det_id})
.tsquash("Inconsistent settings between detectors");
det->setThresholdEnergy(StringTo<int>(args[0]), t, false,
std::vector<int>{det_id});
} else if (args.size() == 2) {
det->setThresholdEnergy(
StringTo<int>(args[0]),
sls::StringTo<slsDetectorDefs::detectorSettings>(args[1]),
false, std::vector<int>{det_id});
} else {
WrongNumberOfParameters(1);
}
os << ToString(args) << '\n';
} else {
throw sls::RuntimeError("Unknown action");
}
return os.str();
}
std::string CmdProxy::TrimEnergies(int action) {
std::ostringstream os;
os << cmd << ' ';
if (action == defs::HELP_ACTION) {
os << "[trim_ev1] [trim_Ev2 (optional)] [trim_ev3 (optional)] "
"...\n\t[Eiger] Number of trim energies and list of trim "
"energies, where corresponding default trim files exist in "
"corresponding trim folders."
<< '\n';
} else if (action == defs::GET_ACTION) {
if (!args.empty()) {
WrongNumberOfParameters(0);
}
auto t = det->getTrimEnergies(std::vector<int>{det_id});
os << OutString(t) << '\n';
} else if (action == defs::PUT_ACTION) {
std::vector<int> t(args.size());
if (!args.empty()) {
for (size_t i = 0; i < t.size(); ++i) {
t[i] = StringTo<int>(args[i]);
}
}
det->setTrimEnergies(t, std::vector<int>{det_id});
os << sls::ToString(args) << '\n';
} else {
throw sls::RuntimeError("Unknown action");
}
return os.str();
}
std::string CmdProxy::RateCorrection(int action) {
std::ostringstream os;
os << cmd << ' ';

View File

@ -596,6 +596,8 @@ class CmdProxy {
{"detectornumber", "serialnumber"},
{"thisversion", "clientversion"},
{"detsizechan", "detsize"},
{"trimdir", "settingspath"},
{"settingsdir", "settingspath"},
/* acquisition parameters */
{"cycles", "triggers"},
@ -724,8 +726,6 @@ class CmdProxy {
{"rx_datastream", "rx_zmqstream"},
/* Eiger Specific */
{"trimdir", "settingspath"},
{"settingsdir", "settingspath"},
{"resmat", "partialreset"},
/* Jungfrau Specific */
@ -774,8 +774,12 @@ class CmdProxy {
{"detsize", &CmdProxy::DetectorSize},
{"settingslist", &CmdProxy::settingslist},
{"settings", &CmdProxy::settings},
{"threshold", &CmdProxy::Threshold},
{"thresholdnotb", &CmdProxy::Threshold},
{"settingspath", &CmdProxy::settingspath},
{"trimbits", &CmdProxy::trimbits},
{"trimval", &CmdProxy::trimval},
{"trimen", &CmdProxy::TrimEnergies},
{"gappixels", &CmdProxy::GapPixels},
/* acquisition parameters */
@ -911,12 +915,8 @@ class CmdProxy {
/* Eiger Specific */
{"subexptime", &CmdProxy::subexptime},
{"subdeadtime", &CmdProxy::subdeadtime},
{"threshold", &CmdProxy::Threshold},
{"thresholdnotb", &CmdProxy::ThresholdNoTb},
{"settingspath", &CmdProxy::settingspath},
{"overflow", &CmdProxy::overflow},
{"flippeddatax", &CmdProxy::flippeddatax},
{"trimen", &CmdProxy::TrimEnergies},
{"ratecorr", &CmdProxy::RateCorrection},
{"readnlines", &CmdProxy::readnlines},
{"interruptsubframe", &CmdProxy::interruptsubframe},
@ -1014,6 +1014,7 @@ class CmdProxy {
/* Pattern */
{"pattern", &CmdProxy::Pattern},
{"savepattern", &CmdProxy::savepattern},
{"defaultpattern", &CmdProxy::defaultpattern},
{"patioctrl", &CmdProxy::patioctrl},
{"patword", &CmdProxy::PatternWord},
{"patlimits", &CmdProxy::PatternLoopAddresses},
@ -1081,6 +1082,8 @@ class CmdProxy {
std::string PackageVersion(int action);
std::string ClientVersion(int action);
std::string DetectorSize(int action);
std::string Threshold(int action);
std::string TrimEnergies(int action);
std::string GapPixels(int action);
/* acquisition parameters */
std::string Acquire(int action);
@ -1112,9 +1115,6 @@ class CmdProxy {
/* ZMQ Streaming Parameters (Receiver<->Client) */
std::string ZMQHWM(int action);
/* Eiger Specific */
std::string Threshold(int action);
std::string ThresholdNoTb(int action);
std::string TrimEnergies(int action);
std::string RateCorrection(int action);
std::string Activate(int action);
std::string PulsePixel(int action);
@ -1211,9 +1211,14 @@ class CmdProxy {
"\n\t[Gotthard2] - [dynamicgain | fixgain1 | fixgain2]"
"\n\t[Moench] - [g1_hg | g1_lg | g2_hc_hg | g2_hc_lg | "
"g2_lc_hg | g2_lc_lg | g4_hg | g4_lg]"
"\n\t[Eiger] Use threshold or thresholdnotb. \n\t[Eiger] "
"settings loaded from file found in settingspath. \n\t[Gotthard] Also "
"loads default dacs on to the detector.");
"\n\t[Mythen3] - [standard | fast | highgain] Also changes vrshaper "
"and vrpreamp. \n\t[Eiger] Use threshold or thresholdnotb. \n\t[Eiger] "
"threshold and settings loaded from file found in settingspath. "
"\n\t[Gotthard] Also loads default dacs on to the detector.");
STRING_COMMAND(settingspath, getSettingsPath, setSettingsPath,
"[path]\n\t[Eiger][Mythen3] Directory where settings files "
"are loaded from/to.");
EXECUTE_SET_COMMAND_1ARG(
trimbits, loadTrimbits,
@ -1775,10 +1780,6 @@ class CmdProxy {
"of EIGER subframes in 32 bit mode. Subperiod = subexptime + "
"subdeadtime.");
STRING_COMMAND(
settingspath, getSettingsPath, setSettingsPath,
"[path]\n\t[Eiger] Directory where settings files are loaded from/to.");
INTEGER_COMMAND_VEC_ID(
overflow, getOverFlowMode, setOverFlowMode, StringTo<int>,
"[0, 1]\n\t[Eiger] Enable or disable show overflow flag in "
@ -2063,6 +2064,11 @@ class CmdProxy {
"[fname]\n\t[Ctb][Moench][Mythen3] Saves pattern to file (ascii). "
"\n\t[Ctb][Moench] Also executes pattern.");
EXECUTE_SET_COMMAND(
defaultpattern, loadDefaultPattern,
"\n\t[Mythen3][Moench] Loads and runs default pattern in pattern "
"generator. It is to go back to initial settings.");
INTEGER_COMMAND_HEX_WIDTH16(patioctrl, getPatternIOControl,
setPatternIOControl, StringTo<uint64_t>,
"[64 bit mask]\n\t[Ctb][Moench] 64 bit mask "

View File

@ -5,6 +5,7 @@
#include "CmdProxy.h"
#include "DetectorImpl.h"
#include "Module.h"
#include "sls/Pattern.h"
#include "sls/container_utils.h"
#include "sls/logger.h"
#include "sls/sls_detector_defs.h"
@ -170,8 +171,10 @@ std::vector<defs::detectorSettings> Detector::getSettingsList() const {
defs::G2_HIGHCAP_HIGHGAIN, defs::G2_HIGHCAP_LOWGAIN,
defs::G2_LOWCAP_HIGHGAIN, defs::G2_LOWCAP_LOWGAIN,
defs::G4_HIGHGAIN, defs::G4_LOWGAIN};
case defs::CHIPTESTBOARD:
case defs::MYTHEN3:
return std::vector<defs::detectorSettings>{defs::STANDARD, defs::FAST,
defs::HIGHGAIN};
case defs::CHIPTESTBOARD:
throw RuntimeError("Settings not implemented for this detector");
default:
throw RuntimeError("Unknown detector type");
@ -183,7 +186,71 @@ Result<defs::detectorSettings> Detector::getSettings(Positions pos) const {
}
void Detector::setSettings(const defs::detectorSettings value, Positions pos) {
pimpl->Parallel(&Module::setSettings, pos, value);
if (value == defs::UNINITIALIZED || value == defs::UNDEFINED) {
throw RuntimeError(
"Cannot set settings with undefined or uninitialized settings.");
}
if (anyEqualTo<defs::detectorSettings>(getSettingsList(), value)) {
pimpl->Parallel(&Module::setSettings, pos, value);
} else {
throw RuntimeError("Unknown Settings " + ToString(value) +
" for this detector\n");
}
}
Result<int> Detector::getThresholdEnergy(Positions pos) const {
return pimpl->Parallel(&Module::getThresholdEnergy, pos);
}
Result<std::array<int, 3>>
Detector::getAllThresholdEnergy(Positions pos) const {
return pimpl->Parallel(&Module::getAllThresholdEnergy, pos);
}
void Detector::setThresholdEnergy(int threshold_ev,
defs::detectorSettings settings,
bool trimbits, Positions pos) {
defs::detectorType type = getDetectorType().squash();
if (type == defs::MYTHEN3) {
std::array<int, 3> energy = {threshold_ev, threshold_ev, threshold_ev};
setThresholdEnergy(energy, settings, trimbits, pos);
return;
}
if (type != defs::EIGER) {
throw RuntimeError(
"Set threshold energy not implemented for this detector");
}
if (anyEqualTo<defs::detectorSettings>(getSettingsList(), settings)) {
pimpl->Parallel(&Module::setThresholdEnergy, pos, threshold_ev,
settings, static_cast<int>(trimbits));
} else {
throw RuntimeError("Unknown Settings " + ToString(settings) +
" for this detector\n");
}
}
void Detector::setThresholdEnergy(std::array<int, 3> threshold_ev,
defs::detectorSettings settings,
bool trimbits, Positions pos) {
if (getDetectorType().squash() != defs::MYTHEN3) {
throw RuntimeError("Set threshold energy for different counters not "
"implemented for this detector");
}
if (anyEqualTo<defs::detectorSettings>(getSettingsList(), settings)) {
pimpl->Parallel(&Module::setAllThresholdEnergy, pos, threshold_ev,
settings, static_cast<int>(trimbits));
} else {
throw RuntimeError("Unknown Settings " + ToString(settings) +
" for this detector\n");
}
}
Result<std::string> Detector::getSettingsPath(Positions pos) const {
return pimpl->Parallel(&Module::getSettingsDir, pos);
}
void Detector::setSettingsPath(const std::string &value, Positions pos) {
pimpl->Parallel(&Module::setSettingsDir, pos, value);
}
void Detector::loadTrimbits(const std::string &fname, Positions pos) {
@ -198,6 +265,14 @@ void Detector::setAllTrimbits(int value, Positions pos) {
pimpl->Parallel(&Module::setAllTrimbits, pos, value);
}
Result<std::vector<int>> Detector::getTrimEnergies(Positions pos) const {
return pimpl->Parallel(&Module::getTrimEn, pos);
}
void Detector::setTrimEnergies(std::vector<int> energies, Positions pos) {
pimpl->Parallel(&Module::setTrimEn, pos, energies);
}
bool Detector::getGapPixelsinCallback() const {
return pimpl->getGapPixelsinCallback();
}
@ -599,7 +674,23 @@ void Detector::startReceiver() { pimpl->Parallel(&Module::startReceiver, {}); }
void Detector::stopReceiver() { pimpl->Parallel(&Module::stopReceiver, {}); }
void Detector::startDetector() {
pimpl->Parallel(&Module::startAcquisition, {});
auto detector_type = getDetectorType().squash();
if (detector_type == defs::MYTHEN3 && size() > 1){
auto is_master = getMaster();
std::vector<int> master;
std::vector<int> slaves;
for(int i=0; i<size(); ++i){
if (is_master[i])
master.push_back(i);
else
slaves.push_back(i);
}
pimpl->Parallel(&Module::startAcquisition, slaves);
pimpl->Parallel(&Module::startAcquisition, master);
}else{
pimpl->Parallel(&Module::startAcquisition, {});
}
}
void Detector::startDetectorReadout() {
@ -642,6 +733,9 @@ Result<defs::scanParameters> Detector::getScan(Positions pos) const {
}
void Detector::setScan(const defs::scanParameters t) {
if(getDetectorType().squash() == defs::MYTHEN3 && size()>1 && t.enable != 0){
throw DetectorError("Scan is only allowed for single module Mythen 3 because of synchronization");
}
pimpl->Parallel(&Module::setScan, {}, t);
}
@ -1157,25 +1251,6 @@ void Detector::setSubDeadTime(ns value, Positions pos) {
pimpl->Parallel(&Module::setSubDeadTime, pos, value.count());
}
Result<int> Detector::getThresholdEnergy(Positions pos) const {
return pimpl->Parallel(&Module::getThresholdEnergy, pos);
}
void Detector::setThresholdEnergy(int threshold_ev,
defs::detectorSettings settings,
bool trimbits, Positions pos) {
pimpl->Parallel(&Module::setThresholdEnergy, pos, threshold_ev, settings,
static_cast<int>(trimbits));
}
Result<std::string> Detector::getSettingsPath(Positions pos) const {
return pimpl->Parallel(&Module::getSettingsDir, pos);
}
void Detector::setSettingsPath(const std::string &value, Positions pos) {
pimpl->Parallel(&Module::setSettingsDir, pos, value);
}
Result<bool> Detector::getOverFlowMode(Positions pos) const {
return pimpl->Parallel(&Module::getOverFlowMode, pos);
}
@ -1192,14 +1267,6 @@ void Detector::setBottom(bool value, Positions pos) {
pimpl->Parallel(&Module::setFlippedDataX, pos, value);
}
Result<std::vector<int>> Detector::getTrimEnergies(Positions pos) const {
return pimpl->Parallel(&Module::getTrimEn, pos);
}
void Detector::setTrimEnergies(std::vector<int> energies, Positions pos) {
pimpl->Parallel(&Module::setTrimEn, pos, energies);
}
Result<ns> Detector::getRateCorrection(Positions pos) const {
return pimpl->Parallel(&Module::getRateCorrection, pos);
}
@ -1544,6 +1611,11 @@ Detector::getGateDelayForAllGates(Positions pos) const {
return pimpl->Parallel(&Module::getGateDelayForAllGates, pos);
}
Result<bool> Detector::getMaster(Positions pos) const{
return pimpl->Parallel(&Module::isMaster, pos);
}
// CTB/ Moench Specific
Result<int> Detector::getNumberOfAnalogSamples(Positions pos) const {
@ -1753,41 +1825,27 @@ void Detector::setLEDEnable(bool enable, Positions pos) {
// Pattern
void Detector::savePattern(const std::string &fname) {
std::ofstream outfile;
outfile.open(fname.c_str(), std::ios_base::out);
if (!outfile.is_open()) {
throw RuntimeError("Could not create file to save pattern");
}
// get pattern limits
auto r = pimpl->Parallel(&Module::getPatternLoopAddresses, {}, -1)
.tsquash("Inconsistent pattern limits");
CmdProxy proxy(this);
// pattern words
for (int i = r[0]; i <= r[1]; ++i) {
std::ostringstream os;
os << "0x" << std::hex << i;
auto addr = os.str();
proxy.Call("patword", {addr}, -1, defs::GET_ACTION, outfile);
}
// rest of pattern file
std::vector<std::string> commands{
"patioctrl", "patlimits", "patloop0", "patnloop0",
"patloop1", "patnloop1", "patloop2", "patnloop2",
"patwait0", "patwaittime0", "patwait1", "patwaittime1",
"patwait2", "patwaittime2", "patmask", "patsetbit",
};
auto det_type = getDetectorType().squash();
if (det_type == defs::MYTHEN3) {
commands.erase(commands.begin(), commands.begin() + 2);
}
for (const auto &cmd : commands)
proxy.Call(cmd, {}, -1, defs::GET_ACTION, outfile);
void Detector::setPattern(const std::string &fname, Positions pos) {
Pattern pat;
pat.load(fname);
pat.validate();
setPattern(pat, pos);
}
void Detector::setPattern(const std::string &fname, Positions pos) {
pimpl->Parallel(&Module::setPattern, pos, fname);
void Detector::setPattern(const Pattern &pat, Positions pos) {
pat.validate();
pimpl->Parallel(&Module::setPattern, pos, pat);
}
void Detector::savePattern(const std::string &fname) {
auto t = pimpl->Parallel(&Module::getPattern, {});
auto pat = t.tsquash("Inconsistent pattern parameters between modules");
pat.validate();
pat.save(fname);
}
void Detector::loadDefaultPattern(Positions pos) {
pimpl->Parallel(&Module::loadDefaultPattern, pos);
}
Result<uint64_t> Detector::getPatternIOControl(Positions pos) const {

View File

@ -1056,11 +1056,15 @@ void DetectorImpl::registerDataCallback(void (*userCallback)(detectorData *,
}
int DetectorImpl::acquire() {
// ensure acquire isnt started multiple times by same client
if (!isAcquireReady()) {
return FAIL;
}
// We need this to handle Mythen3 synchronization
auto detector_type = Parallel(&Module::getDetectorType, {}).squash();
try {
struct timespec begin, end;
clock_gettime(CLOCK_REALTIME, &begin);
@ -1088,7 +1092,25 @@ int DetectorImpl::acquire() {
// start and read all
try {
Parallel(&Module::startAndReadAll, {});
if(detector_type == defs::MYTHEN3 && detectors.size() > 1){
//Multi module mythen
std::vector<int> master;
std::vector<int> slaves;
auto is_master = Parallel(&Module::isMaster, {});
slaves.reserve(detectors.size()-1); //check this one!!
for (size_t i = 0; i<detectors.size(); ++i){
if(is_master[i])
master.push_back(i);
else
slaves.push_back(i);
}
Parallel(&Module::startAcquisition, slaves);
Parallel(&Module::startAndReadAll, master);
}else{
//Normal acquire
Parallel(&Module::startAndReadAll, {});
}
} catch (...) {
if (receiver)
Parallel(&Module::stopReceiver, {});

View File

@ -2,6 +2,7 @@
#include "SharedMemory.h"
#include "sls/ClientSocket.h"
#include "sls/ToString.h"
#include "sls/bit_utils.h"
#include "sls/container_utils.h"
#include "sls/file_utils.h"
#include "sls/network_utils.h"
@ -9,7 +10,6 @@
#include "sls/sls_detector_funcs.h"
#include "sls/string_utils.h"
#include "sls/versionAPI.h"
#include <algorithm>
#include <array>
#include <bitset>
@ -152,16 +152,269 @@ void Module::setSettings(detectorSettings isettings) {
sendToDetector<int>(F_SET_SETTINGS, isettings);
}
int Module::getThresholdEnergy() const {
return sendToDetector<int>(F_GET_THRESHOLD_ENERGY);
}
std::array<int, 3> Module::getAllThresholdEnergy() const {
return sendToDetector<std::array<int, 3>>(F_GET_ALL_THRESHOLD_ENERGY);
}
void Module::setThresholdEnergy(int e_eV, detectorSettings isettings,
bool trimbits) {
// verify e_eV exists in trimEneregies[]
if (shm()->trimEnergies.empty() || (e_eV < shm()->trimEnergies.front()) ||
(e_eV > shm()->trimEnergies.back())) {
throw RuntimeError("This energy " + std::to_string(e_eV) +
" not defined for this module!");
}
bool interpolate =
std::all_of(shm()->trimEnergies.begin(), shm()->trimEnergies.end(),
[e_eV](const int &e) { return e != e_eV; });
sls_detector_module myMod{shm()->myDetectorType};
if (!interpolate) {
std::string settingsfname = getTrimbitFilename(isettings, e_eV);
LOG(logDEBUG1) << "Settings File is " << settingsfname;
myMod = readSettingsFile(settingsfname, trimbits);
} else {
// find the trim values
int trim1 = -1, trim2 = -1;
for (size_t i = 0; i < shm()->trimEnergies.size(); ++i) {
if (e_eV < shm()->trimEnergies[i]) {
trim2 = shm()->trimEnergies[i];
trim1 = shm()->trimEnergies[i - 1];
break;
}
}
std::string settingsfname1 = getTrimbitFilename(isettings, trim1);
std::string settingsfname2 = getTrimbitFilename(isettings, trim2);
LOG(logDEBUG1) << "Settings Files are " << settingsfname1 << " and "
<< settingsfname2;
auto myMod1 = readSettingsFile(settingsfname1, trimbits);
auto myMod2 = readSettingsFile(settingsfname2, trimbits);
if (myMod1.iodelay != myMod2.iodelay) {
throw RuntimeError("setThresholdEnergyAndSettings: Iodelays do not "
"match between files");
}
myMod = interpolateTrim(&myMod1, &myMod2, e_eV, trim1, trim2, trimbits);
myMod.iodelay = myMod1.iodelay;
myMod.tau =
linearInterpolation(e_eV, trim1, trim2, myMod1.tau, myMod2.tau);
}
myMod.reg = isettings;
myMod.eV[0] = e_eV;
setModule(myMod, trimbits);
if (getSettings() != isettings) {
throw RuntimeError("setThresholdEnergyAndSettings: Could not set "
"settings in detector");
}
if (shm()->useReceiverFlag) {
sendToReceiver(F_RECEIVER_SET_THRESHOLD, e_eV, nullptr);
}
}
void Module::setAllThresholdEnergy(std::array<int, 3> e_eV,
detectorSettings isettings, bool trimbits) {
if (shm()->trimEnergies.empty()) {
throw RuntimeError(
"Trim energies have not been defined for this module yet!");
}
auto counters = getSetBits(getCounterMask());
enum mythen3_DacIndex {
M_VCASSH,
M_VTH2,
M_VRSHAPER,
M_VRSHAPER_N,
M_VIPRE_OUT,
M_VTH3,
M_VTH1,
M_VICIN,
M_VCAS,
M_VRPREAMP,
M_VCAL_N,
M_VIPRE,
M_VISHAPER,
M_VCAL_P,
M_VTRIM,
M_VDCSH
};
std::vector<sls_detector_module> myMods{shm()->myDetectorType};
std::vector<int> energy(e_eV.begin(), e_eV.end());
// if all energies are same
if (allEqualTo(energy, energy[0])) {
energy.resize(1);
}
myMods.resize(energy.size());
// for each threshold
for (size_t i = 0; i < energy.size(); ++i) {
// don't interpolate
if (shm()->trimEnergies.anyEqualTo(energy[i])) {
std::string settingsfname =
getTrimbitFilename(isettings, energy[i]);
LOG(logDEBUG1) << "Settings File is " << settingsfname;
myMods[i] = readSettingsFile(settingsfname, trimbits);
}
// interpolate
else {
if (shm()->trimEnergies.size() < 2) {
throw RuntimeError(
"Cannot interpolate with less than 2 trim energies");
}
// find the trim values
int trim1 = -1, trim2 = -1;
if (energy[i] < shm()->trimEnergies[0]) {
trim1 = shm()->trimEnergies[0];
trim2 = shm()->trimEnergies[1];
} else if (energy[i] >
shm()->trimEnergies[shm()->trimEnergies.size() - 1]) {
trim1 = shm()->trimEnergies[shm()->trimEnergies.size() - 2];
trim2 = shm()->trimEnergies[shm()->trimEnergies.size() - 1];
} else {
for (size_t j = 0; j < shm()->trimEnergies.size(); ++j) {
// std::cout << "checking " << energy[i] << " and "
// << shm()->trimEnergies[j] << std::endl;
if (energy[i] < shm()->trimEnergies[j]) {
trim1 = shm()->trimEnergies[j - 1];
trim2 = shm()->trimEnergies[j];
break;
}
}
}
LOG(logINFO) << "e_eV:" << energy[i] << " [" << trim1 << ", "
<< trim2 << "]";
std::string settingsfname1 = getTrimbitFilename(isettings, trim1);
std::string settingsfname2 = getTrimbitFilename(isettings, trim2);
LOG(logDEBUG1) << "Settings Files are " << settingsfname1 << " and "
<< settingsfname2;
auto myMod1 = readSettingsFile(settingsfname1, trimbits);
auto myMod2 = readSettingsFile(settingsfname2, trimbits);
myMods[i] = interpolateTrim(&myMod1, &myMod2, energy[i], trim1,
trim2, trimbits);
}
}
sls_detector_module myMod{shm()->myDetectorType};
myMod = myMods[0];
// if multiple thresholds, combine
if (myMods.size() > 1) {
// average vtrim of enabled counters
int sum = 0;
for (size_t i = 0; i < counters.size(); ++i) {
sum += myMods[counters[i]].dacs[M_VTRIM];
}
myMod.dacs[M_VTRIM] = sum / counters.size();
// copy vth1, vth2 and vth3 from the correct threshold mods
myMod.dacs[M_VTH1] = myMods[0].dacs[M_VTH1];
myMod.dacs[M_VTH2] = myMods[1].dacs[M_VTH2];
myMod.dacs[M_VTH3] = myMods[2].dacs[M_VTH3];
// check if dacs are different
for (size_t j = 0; j < 16; ++j) {
if (j == M_VTRIM || j == M_VTH1 || j == M_VTH2 || j == M_VTH3)
continue;
if (myMods[0].dacs[j] != myMods[1].dacs[j]) {
throw RuntimeError("Dac Index " + std::to_string(j) +
" differs for threshold[0]:[" +
std::to_string(myMods[0].dacs[j]) +
"] and threshold[1]:" +
std::to_string(myMods[1].dacs[j]) + "]");
}
if (myMods[1].dacs[j] != myMods[2].dacs[j]) {
throw RuntimeError("Dac Index " + std::to_string(j) +
" differs for threshold[1]:[" +
std::to_string(myMods[1].dacs[j]) +
"] and threshold[2]:" +
std::to_string(myMods[2].dacs[j]) + "]");
}
}
// replace correct trim values (interleaved)
for (int i = 0; i < myMod.nchan; ++i) {
myMod.chanregs[i] = myMods[i % 3].chanregs[i];
}
}
myMod.reg = isettings;
std::copy(e_eV.begin(), e_eV.end(), myMod.eV);
LOG(logDEBUG) << "ev:" << ToString(myMod.eV);
//check for trimbits that are out of range
bool out_of_range = false;
for(int i = 0; i!=myMod.nchan; ++i){
if (myMod.chanregs[i]<0){
myMod.chanregs[i] = 0;
out_of_range = true;
}else if(myMod.chanregs[i]>63){
myMod.chanregs[i]=63;
out_of_range = true;
}
}
if (out_of_range){
LOG(logWARNING) << "Some trimbits were out of range after interpolation, these have been replaced with 0 or 63.";
}
//check dacs
out_of_range = false;
for (auto dac : {M_VTRIM,M_VTH1,M_VTH2, M_VTH3}){
if (myMod.dacs[dac] < 600){
myMod.dacs[dac] = 600;
out_of_range = true;
}else if(myMod.dacs[dac] > 2400){
myMod.dacs[dac] = 2400;
out_of_range = true;
}
}
if (out_of_range){
LOG(logWARNING) << "Some dacs were out of range after interpolation, these have been replaced with 600 or 2400.";
}
setModule(myMod, trimbits);
if (getSettings() != isettings) {
throw RuntimeError("setThresholdEnergyAndSettings: Could not set "
"settings in detector");
}
if (shm()->useReceiverFlag) {
sendToReceiver(F_RECEIVER_SET_ALL_THRESHOLD, e_eV, nullptr);
}
}
std::string Module::getSettingsDir() const {
return std::string(shm()->settingsDir);
}
std::string Module::setSettingsDir(const std::string &dir) {
sls::strcpy_safe(shm()->settingsDir, dir.c_str());
return shm()->settingsDir;
}
void Module::loadSettingsFile(const std::string &fname) {
// find specific file if it has detid in file name (.snxxx)
if (shm()->myDetectorType == EIGER || shm()->myDetectorType == MYTHEN3) {
std::ostringstream ostfn;
ostfn << fname;
if (fname.find(".sn") == std::string::npos &&
fname.find(".trim") == std::string::npos &&
fname.find(".settings") == std::string::npos) {
ostfn << ".sn" << std::setfill('0') << std::setw(3) << std::dec
<< getSerialNumber();
int serialNumberWidth = 3;
if (shm()->myDetectorType == MYTHEN3) {
serialNumberWidth = 4;
}
if (fname.find(".sn") == std::string::npos) {
ostfn << ".sn" << std::setfill('0') << std::setw(serialNumberWidth)
<< std::dec << getSerialNumber();
}
auto myMod = readSettingsFile(ostfn.str());
setModule(myMod);
@ -178,6 +431,30 @@ void Module::setAllTrimbits(int val) {
sendToDetector<int>(F_SET_ALL_TRIMBITS, val);
}
std::vector<int> Module::getTrimEn() const {
if (shm()->myDetectorType != EIGER && shm()->myDetectorType != MYTHEN3) {
throw RuntimeError("getTrimEn not implemented for this detector.");
}
return std::vector<int>(shm()->trimEnergies.begin(),
shm()->trimEnergies.end());
}
int Module::setTrimEn(const std::vector<int> &energies) {
if (shm()->myDetectorType != EIGER && shm()->myDetectorType != MYTHEN3) {
throw RuntimeError("setTrimEn not implemented for this detector.");
}
if (energies.size() > MAX_TRIMEN) {
std::ostringstream os;
os << "Size of trim energies: " << energies.size()
<< " exceeds what can be stored in shared memory: " << MAX_TRIMEN
<< "\n";
throw RuntimeError(os.str());
}
shm()->trimEnergies = energies;
std::sort(shm()->trimEnergies.begin(), shm()->trimEnergies.end());
return shm()->trimEnergies.size();
}
bool Module::isVirtualDetectorServer() const {
return sendToDetector<int>(F_IS_VIRTUAL);
}
@ -1095,33 +1372,6 @@ void Module::setSubDeadTime(int64_t value) {
}
}
int Module::getThresholdEnergy() const {
return sendToDetector<int>(F_GET_THRESHOLD_ENERGY);
}
void Module::setThresholdEnergy(int e_eV, detectorSettings isettings,
bool trimbits) {
// check as there is client processing
if (shm()->myDetectorType == EIGER) {
setThresholdEnergyAndSettings(e_eV, isettings, trimbits);
if (shm()->useReceiverFlag) {
sendToReceiver(F_RECEIVER_SET_THRESHOLD, e_eV, nullptr);
}
} else {
throw RuntimeError(
"Set threshold energy not implemented for this detector");
}
}
std::string Module::getSettingsDir() const {
return std::string(shm()->settingsDir);
}
std::string Module::setSettingsDir(const std::string &dir) {
sls::strcpy_safe(shm()->settingsDir, dir.c_str());
return shm()->settingsDir;
}
bool Module::getOverFlowMode() const {
return sendToDetector<int>(F_GET_OVERFLOW_MODE);
}
@ -1138,29 +1388,6 @@ void Module::setFlippedDataX(bool value) {
sendToReceiver<int>(F_SET_FLIPPED_DATA_RECEIVER, static_cast<int>(value));
}
std::vector<int> Module::getTrimEn() const {
if (shm()->myDetectorType != EIGER) {
throw RuntimeError("getTrimEn not implemented for this detector.");
}
return std::vector<int>(shm()->trimEnergies.begin(),
shm()->trimEnergies.end());
}
int Module::setTrimEn(const std::vector<int> &energies) {
if (shm()->myDetectorType != EIGER) {
throw RuntimeError("setTrimEn not implemented for this detector.");
}
if (energies.size() > MAX_TRIMEN) {
std::ostringstream os;
os << "Size of trim energies: " << energies.size()
<< " exceeds what can be stored in shared memory: " << MAX_TRIMEN
<< "\n";
throw RuntimeError(os.str());
}
shm()->trimEnergies = energies;
return shm()->trimEnergies.size();
}
int64_t Module::getRateCorrection() const {
return sendToDetector<int64_t>(F_GET_RATE_CORRECT);
}
@ -1767,6 +1994,10 @@ std::array<time::ns, 3> Module::getGateDelayForAllGates() const {
return sendToDetector<std::array<time::ns, 3>>(F_GET_GATE_DELAY_ALL_GATES);
}
bool Module::isMaster() const{
return sendToDetector<int>(F_GET_MASTER);
}
// CTB / Moench Specific
int Module::getNumberOfAnalogSamples() const {
return sendToDetector<int>(F_GET_NUM_ANALOG_SAMPLES);
@ -1913,108 +2144,18 @@ void Module::setLEDEnable(bool enable) {
// Pattern
void Module::setPattern(const std::string &fname) {
auto pat = sls::make_unique<patternParameters>();
std::ifstream input_file(fname);
if (!input_file) {
throw RuntimeError("Could not open pattern file " + fname +
" for reading");
}
for (std::string line; std::getline(input_file, line);) {
if (line.find('#') != std::string::npos) {
line.erase(line.find('#'));
}
LOG(logDEBUG1) << "line after removing comments:\n\t" << line;
if (line.length() > 1) {
// convert command and string to a vector
std::istringstream iss(line);
auto it = std::istream_iterator<std::string>(iss);
std::vector<std::string> args = std::vector<std::string>(
it, std::istream_iterator<std::string>());
std::string cmd = args[0];
int nargs = args.size() - 1;
if (cmd == "patword") {
if (nargs != 2) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
uint32_t addr = StringTo<uint32_t>(args[1]);
if (addr >= MAX_PATTERN_LENGTH) {
throw RuntimeError("Invalid address for " + ToString(args));
}
pat->word[addr] = StringTo<uint64_t>(args[2]);
} else if (cmd == "patioctrl") {
if (nargs != 1) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
pat->patioctrl = StringTo<uint64_t>(args[1]);
} else if (cmd == "patlimits") {
if (nargs != 2) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
pat->patlimits[0] = StringTo<uint32_t>(args[1]);
pat->patlimits[1] = StringTo<uint32_t>(args[2]);
if (pat->patlimits[0] >= MAX_PATTERN_LENGTH ||
pat->patlimits[1] >= MAX_PATTERN_LENGTH) {
throw RuntimeError("Invalid address for " + ToString(args));
}
} else if (cmd == "patloop0" || cmd == "patloop1" ||
cmd == "patloop2") {
if (nargs != 2) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
int level = cmd[cmd.find_first_of("012")] - '0';
int patloop1 = StringTo<uint32_t>(args[1]);
int patloop2 = StringTo<uint32_t>(args[2]);
pat->patloop[level * 2 + 0] = patloop1;
pat->patloop[level * 2 + 1] = patloop2;
if (patloop1 >= MAX_PATTERN_LENGTH ||
patloop2 >= MAX_PATTERN_LENGTH) {
throw RuntimeError("Invalid address for " + ToString(args));
}
} else if (cmd == "patnloop0" || cmd == "patnloop1" ||
cmd == "patnloop2") {
if (nargs != 1) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
int level = cmd[cmd.find_first_of("012")] - '0';
pat->patnloop[level] = StringTo<uint32_t>(args[1]);
} else if (cmd == "patwait0" || cmd == "patwait1" ||
cmd == "patwait2") {
if (nargs != 1) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
int level = cmd[cmd.find_first_of("012")] - '0';
pat->patwait[level] = StringTo<uint32_t>(args[1]);
if (pat->patwait[level] >= MAX_PATTERN_LENGTH) {
throw RuntimeError("Invalid address for " + ToString(args));
}
} else if (cmd == "patwaittime0" || cmd == "patwaittime1" ||
cmd == "patwaittime2") {
if (nargs != 1) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
int level = cmd[cmd.find_first_of("012")] - '0';
pat->patwaittime[level] = StringTo<uint64_t>(args[1]);
} else {
throw RuntimeError("Unknown command in pattern file " + cmd);
}
}
}
LOG(logDEBUG1) << "Sending pattern from file to detector:" << *pat;
sendToDetector(F_SET_PATTERN, pat.get(), sizeof(patternParameters), nullptr,
0);
void Module::setPattern(const Pattern &pat) {
sendToDetector(F_SET_PATTERN, pat.data(), pat.size(), nullptr, 0);
}
Pattern Module::getPattern() {
Pattern pat;
sendToDetector(F_GET_PATTERN, nullptr, 0, pat.data(), pat.size());
return pat;
}
void Module::loadDefaultPattern() { sendToDetector(F_LOAD_DEFAULT_PATTERN); }
uint64_t Module::getPatternIOControl() const {
return sendToDetector<uint64_t>(F_SET_PATTERN_IO_CONTROL,
int64_t(GET_FLAG));
@ -2839,9 +2980,9 @@ int Module::sendModule(sls_detector_module *myMod, sls::ClientSocket &client) {
ts += n;
LOG(level) << "tau sent. " << n << " bytes. tau: " << myMod->tau;
n = client.Send(&(myMod->eV), sizeof(myMod->eV));
n = client.Send(myMod->eV, sizeof(myMod->eV));
ts += n;
LOG(level) << "ev sent. " << n << " bytes. ev: " << myMod->eV;
LOG(level) << "ev sent. " << n << " bytes. ev: " << ToString(myMod->eV);
n = client.Send(myMod->dacs, sizeof(int) * (myMod->ndac));
ts += n;
@ -2889,68 +3030,12 @@ void Module::updateRateCorrection() {
sendToDetector(F_UPDATE_RATE_CORRECTION);
}
void Module::setThresholdEnergyAndSettings(int e_eV, detectorSettings isettings,
bool trimbits) {
// verify e_eV exists in trimEneregies[]
if (shm()->trimEnergies.empty() || (e_eV < shm()->trimEnergies.front()) ||
(e_eV > shm()->trimEnergies.back())) {
throw RuntimeError("This energy " + std::to_string(e_eV) +
" not defined for this module!");
}
bool interpolate =
std::all_of(shm()->trimEnergies.begin(), shm()->trimEnergies.end(),
[e_eV](const int &e) { return e != e_eV; });
sls_detector_module myMod{shm()->myDetectorType};
if (!interpolate) {
std::string settingsfname = getTrimbitFilename(isettings, e_eV);
LOG(logDEBUG1) << "Settings File is " << settingsfname;
myMod = readSettingsFile(settingsfname, trimbits);
} else {
// find the trim values
int trim1 = -1, trim2 = -1;
for (size_t i = 0; i < shm()->trimEnergies.size(); ++i) {
if (e_eV < shm()->trimEnergies[i]) {
trim2 = shm()->trimEnergies[i];
trim1 = shm()->trimEnergies[i - 1];
break;
}
}
std::string settingsfname1 = getTrimbitFilename(isettings, trim1);
std::string settingsfname2 = getTrimbitFilename(isettings, trim2);
LOG(logDEBUG1) << "Settings Files are " << settingsfname1 << " and "
<< settingsfname2;
auto myMod1 = readSettingsFile(settingsfname1, trimbits);
auto myMod2 = readSettingsFile(settingsfname2, trimbits);
if (myMod1.iodelay != myMod2.iodelay) {
throw RuntimeError("setThresholdEnergyAndSettings: Iodelays do not "
"match between files");
}
myMod = interpolateTrim(&myMod1, &myMod2, e_eV, trim1, trim2, trimbits);
myMod.iodelay = myMod1.iodelay;
myMod.tau =
linearInterpolation(e_eV, trim1, trim2, myMod1.tau, myMod2.tau);
}
myMod.reg = isettings;
myMod.eV = e_eV;
setModule(myMod, trimbits);
if (getSettings() != isettings) {
throw RuntimeError("setThresholdEnergyAndSettings: Could not set "
"settings in detector");
}
}
sls_detector_module Module::interpolateTrim(sls_detector_module *a,
sls_detector_module *b,
const int energy, const int e1,
const int e2, bool trimbits) {
// only implemented for eiger currently (in terms of which dacs)
if (shm()->myDetectorType != EIGER) {
// dacs specified only for eiger and mythen3
if (shm()->myDetectorType != EIGER && shm()->myDetectorType != MYTHEN3) {
throw NotImplementedError(
"Interpolation of Trim values not implemented for this detector!");
}
@ -2974,36 +3059,72 @@ sls_detector_module Module::interpolateTrim(sls_detector_module *a,
E_VCN,
E_VIS
};
enum mythen3_DacIndex {
M_VCASSH,
M_VTH2,
M_VRSHAPER,
M_VRSHAPER_N,
M_VIPRE_OUT,
M_VTH3,
M_VTH1,
M_VICIN,
M_VCAS,
M_VRPREAMP,
M_VCAL_N,
M_VIPRE,
M_VISHAPER,
M_VCAL_P,
M_VTRIM,
M_VDCSH
};
// Copy other dacs
int dacs_to_copy[] = {E_SVP, E_VTR, E_SVN, E_VTGSTV,
E_RXB_RB, E_RXB_LB, E_VCN, E_VIS};
int num_dacs_to_copy = sizeof(dacs_to_copy) / sizeof(dacs_to_copy[0]);
for (int i = 0; i < num_dacs_to_copy; ++i) {
// create copy and interpolate dac lists
std::vector<int> dacs_to_copy, dacs_to_interpolate;
if (shm()->myDetectorType == EIGER) {
dacs_to_copy.insert(
dacs_to_copy.end(),
{E_SVP, E_VTR, E_SVN, E_VTGSTV, E_RXB_RB, E_RXB_LB, E_VCN, E_VIS});
// interpolate vrf, vcmp, vcp
dacs_to_interpolate.insert(
dacs_to_interpolate.end(),
{E_VRF, E_VCMP_LL, E_VCMP_LR, E_VCMP_RL, E_VCMP_RR, E_VCP, E_VRS});
} else {
dacs_to_copy.insert(dacs_to_copy.end(),
{M_VCASSH, M_VRSHAPER, M_VRSHAPER_N, M_VIPRE_OUT,
M_VICIN, M_VCAS, M_VRPREAMP, M_VCAL_N, M_VIPRE,
M_VISHAPER, M_VCAL_P, M_VDCSH});
// interpolate vtrim, vth1, vth2, vth3
dacs_to_interpolate.insert(dacs_to_interpolate.end(),
{M_VTH1, M_VTH2, M_VTH3, M_VTRIM});
}
// Copy Dacs
for (size_t i = 0; i < dacs_to_copy.size(); ++i) {
if (a->dacs[dacs_to_copy[i]] != b->dacs[dacs_to_copy[i]]) {
throw RuntimeError("Interpolate module: dacs different");
throw RuntimeError("Interpolate module: dacs " + std::to_string(i) +
" different");
}
myMod.dacs[dacs_to_copy[i]] = a->dacs[dacs_to_copy[i]];
}
// Copy irrelevant dacs (without failing): CAL
if (a->dacs[E_CAL] != b->dacs[E_CAL]) {
LOG(logWARNING) << "DAC CAL differs in both energies ("
<< a->dacs[E_CAL] << "," << b->dacs[E_CAL]
<< ")!\nTaking first: " << a->dacs[E_CAL];
}
myMod.dacs[E_CAL] = a->dacs[E_CAL];
// Interpolate vrf, vcmp, vcp
int dacs_to_interpolate[] = {E_VRF, E_VCMP_LL, E_VCMP_LR, E_VCMP_RL,
E_VCMP_RR, E_VCP, E_VRS};
int num_dacs_to_interpolate =
sizeof(dacs_to_interpolate) / sizeof(dacs_to_interpolate[0]);
for (int i = 0; i < num_dacs_to_interpolate; ++i) {
// Interpolate Dacs
for (size_t i = 0; i < dacs_to_interpolate.size(); ++i) {
myMod.dacs[dacs_to_interpolate[i]] =
linearInterpolation(energy, e1, e2, a->dacs[dacs_to_interpolate[i]],
b->dacs[dacs_to_interpolate[i]]);
}
// Copy irrelevant dacs (without failing)
if (shm()->myDetectorType == EIGER) {
// CAL
if (a->dacs[E_CAL] != b->dacs[E_CAL]) {
LOG(logWARNING)
<< "DAC CAL differs in both energies (" << a->dacs[E_CAL] << ","
<< b->dacs[E_CAL] << ")!\nTaking first: " << a->dacs[E_CAL];
}
myMod.dacs[E_CAL] = a->dacs[E_CAL];
}
// Interpolate all trimbits
if (trimbits) {
for (int i = 0; i < myMod.nchan; ++i) {
@ -3020,6 +3141,9 @@ std::string Module::getTrimbitFilename(detectorSettings s, int e_eV) {
case STANDARD:
ssettings = "/standard";
break;
case FAST:
ssettings = "/fast";
break;
case HIGHGAIN:
ssettings = "/highgain";
break;
@ -3038,8 +3162,20 @@ std::string Module::getTrimbitFilename(detectorSettings s, int e_eV) {
throw RuntimeError(ss.str());
}
std::ostringstream ostfn;
ostfn << shm()->settingsDir << ssettings << "/" << e_eV << "eV"
<< "/noise.sn" << std::setfill('0') << std::setw(3) << std::dec
ostfn << shm()->settingsDir << ssettings << "/" << e_eV << "eV";
if (shm()->myDetectorType == EIGER) {
ostfn << "/noise.sn";
} else if (shm()->myDetectorType == MYTHEN3) {
ostfn << "/trim.sn";
} else {
throw RuntimeError(
"Settings or trimbit files not defined for this detector.");
}
int serialNumberWidth = 3;
if (shm()->myDetectorType == MYTHEN3) {
serialNumberWidth = 4;
}
ostfn << std::setfill('0') << std::setw(serialNumberWidth) << std::dec
<< getSerialNumber() << std::setbase(10);
return ostfn.str();
}
@ -3066,6 +3202,11 @@ sls_detector_module Module::readSettingsFile(const std::string &fname,
infile.read(reinterpret_cast<char *>(&myMod.iodelay),
sizeof(myMod.iodelay));
infile.read(reinterpret_cast<char *>(&myMod.tau), sizeof(myMod.tau));
for (int i = 0; i < myMod.ndac; ++i) {
LOG(logDEBUG1) << "dac " << i << ":" << myMod.dacs[i];
}
LOG(logDEBUG1) << "iodelay:" << myMod.iodelay;
LOG(logDEBUG1) << "tau:" << myMod.tau;
if (trimbits) {
infile.read(reinterpret_cast<char *>(myMod.chanregs),
sizeof(int) * (myMod.nchan));
@ -3075,28 +3216,24 @@ sls_detector_module Module::readSettingsFile(const std::string &fname,
"for settings for " +
fname);
}
for (int i = 0; i < myMod.ndac; ++i) {
LOG(logDEBUG1) << "dac " << i << ":" << myMod.dacs[i];
}
LOG(logDEBUG1) << "iodelay:" << myMod.iodelay;
LOG(logDEBUG1) << "tau:" << myMod.tau;
}
// mythen3 (dacs, trimbits)
else if (shm()->myDetectorType == MYTHEN3) {
infile.read(reinterpret_cast<char *>(myMod.dacs),
sizeof(int) * (myMod.ndac));
infile.read(reinterpret_cast<char *>(myMod.chanregs),
sizeof(int) * (myMod.nchan));
for (int i = 0; i < myMod.ndac; ++i) {
LOG(logDEBUG) << "dac " << i << ":" << myMod.dacs[i];
}
if (trimbits) {
infile.read(reinterpret_cast<char *>(myMod.chanregs),
sizeof(int) * (myMod.nchan));
}
if (!infile) {
throw RuntimeError("readSettingsFile: Could not load all values "
"for settings for " +
fname);
}
for (int i = 0; i < myMod.ndac; ++i) {
LOG(logDEBUG1) << "dac " << i << ":" << myMod.dacs[i];
}
}
else {

View File

@ -1,6 +1,7 @@
#pragma once
#include "SharedMemory.h"
#include "sls/ClientSocket.h"
#include "sls/Pattern.h"
#include "sls/StaticVector.h"
#include "sls/logger.h"
#include "sls/network_utils.h"
@ -100,9 +101,19 @@ class Module : public virtual slsDetectorDefs {
void updateNumberOfDetector(slsDetectorDefs::xy det);
detectorSettings getSettings() const;
void setSettings(detectorSettings isettings);
int getThresholdEnergy() const;
std::array<int, 3> getAllThresholdEnergy() const;
void setThresholdEnergy(int e_eV, detectorSettings isettings,
bool trimbits);
void setAllThresholdEnergy(std::array<int, 3> e_eV,
detectorSettings isettings, bool trimbits);
std::string getSettingsDir() const;
std::string setSettingsDir(const std::string &dir);
void loadSettingsFile(const std::string &fname);
int getAllTrimbits() const;
void setAllTrimbits(int val);
std::vector<int> getTrimEn() const;
int setTrimEn(const std::vector<int> &energies = {});
bool isVirtualDetectorServer() const;
/**************************************************
@ -307,17 +318,10 @@ class Module : public virtual slsDetectorDefs {
void setSubExptime(int64_t value);
int64_t getSubDeadTime() const;
void setSubDeadTime(int64_t value);
int getThresholdEnergy() const;
void setThresholdEnergy(int e_eV, detectorSettings isettings,
bool trimbits);
std::string getSettingsDir() const;
std::string setSettingsDir(const std::string &dir);
bool getOverFlowMode() const;
void setOverFlowMode(const bool enable);
bool getFlippedDataX() const;
void setFlippedDataX(bool value);
std::vector<int> getTrimEn() const;
int setTrimEn(const std::vector<int> &energies = {});
int64_t getRateCorrection() const;
void setDefaultRateCorrection();
void setRateCorrection(int64_t t = 0);
@ -421,6 +425,7 @@ class Module : public virtual slsDetectorDefs {
int64_t getGateDelay(int gateIndex) const;
void setGateDelay(int gateIndex, int64_t value);
std::array<time::ns, 3> getGateDelayForAllGates() const;
bool isMaster() const;
/**************************************************
* *
@ -462,7 +467,9 @@ class Module : public virtual slsDetectorDefs {
* Pattern *
* *
* ************************************************/
void setPattern(const std::string &fname);
void setPattern(const Pattern &pat);
Pattern getPattern();
void loadDefaultPattern();
uint64_t getPatternIOControl() const;
void setPatternIOControl(uint64_t word);
uint64_t getPatternWord(int addr) const;
@ -677,8 +684,6 @@ class Module : public virtual slsDetectorDefs {
void updateReceiverStreamingIP();
void updateRateCorrection();
void setThresholdEnergyAndSettings(int e_eV, detectorSettings isettings,
bool trimbits = true);
/** Template function to do linear interpolation between two points (Eiger
only) */
template <typename E, typename V>

View File

@ -0,0 +1,240 @@
#include "sls/Pattern.h"
#include "sls/ToString.h"
#include "sls/logger.h"
#include <fstream>
#include <iterator>
#include <sstream>
namespace sls {
Pattern::Pattern() = default;
Pattern::~Pattern() { delete pat; }
Pattern::Pattern(const Pattern &other) {
memcpy(pat, other.pat, sizeof(patternParameters));
}
bool Pattern::operator==(const Pattern &other) const {
for (size_t i = 0; i < (sizeof(pat->word) / sizeof(pat->word[0])); ++i) {
if (pat->word[i] != other.pat->word[i])
return false;
}
if (pat->ioctrl != other.pat->ioctrl)
return false;
for (size_t i = 0; i < (sizeof(pat->limits) / sizeof(pat->limits[0]));
++i) {
if (pat->limits[i] != other.pat->limits[i])
return false;
}
for (size_t i = 0; i < (sizeof(pat->loop) / sizeof(pat->loop[0])); ++i) {
if (pat->loop[i] != other.pat->loop[i])
return false;
}
for (size_t i = 0; i < (sizeof(pat->nloop) / sizeof(pat->nloop[0])); ++i) {
if (pat->nloop[i] != other.pat->nloop[i])
return false;
}
for (size_t i = 0; i < (sizeof(pat->wait) / sizeof(pat->wait[0])); ++i) {
if (pat->wait[i] != other.pat->wait[i])
return false;
}
for (size_t i = 0; i < (sizeof(pat->waittime) / sizeof(pat->waittime[0]));
++i) {
if (pat->waittime[i] != other.pat->waittime[i])
return false;
}
return true;
}
bool Pattern::operator!=(const Pattern &other) const {
return !(*this == other);
}
patternParameters *Pattern::data() { return pat; }
patternParameters *Pattern::data() const { return pat; }
void Pattern::validate() const {
if (pat->limits[0] >= MAX_PATTERN_LENGTH ||
pat->limits[1] >= MAX_PATTERN_LENGTH) {
throw RuntimeError("Invalid Pattern limits address [" +
ToString(pat->limits[0]) + std::string(", ") +
ToString(pat->limits[1]) + std::string("]"));
}
for (int i = 0; i != 3; ++i) {
if (pat->loop[i * 2 + 0] >= MAX_PATTERN_LENGTH ||
pat->loop[i * 2 + 1] >= MAX_PATTERN_LENGTH) {
throw RuntimeError(
"Invalid Pattern loop address for level " + ToString(i) +
std::string(" [") + ToString(pat->loop[i * 2 + 0]) +
std::string(", ") + ToString(pat->loop[i * 2 + 1]) +
std::string("]"));
}
if (pat->wait[i] >= MAX_PATTERN_LENGTH) {
throw RuntimeError("Invalid Pattern wait address for level " +
ToString(i) + std::string(" ") +
ToString(pat->wait[i]));
}
}
}
void Pattern::load(const std::string &fname) {
std::ifstream input_file(fname);
if (!input_file) {
throw RuntimeError("Could not open pattern file " + fname +
" for reading");
}
for (std::string line; std::getline(input_file, line);) {
if (line.find('#') != std::string::npos) {
line.erase(line.find('#'));
}
LOG(logDEBUG1) << "line after removing comments:\n\t" << line;
if (line.length() > 1) {
// convert command and string to a vector
std::istringstream iss(line);
auto it = std::istream_iterator<std::string>(iss);
std::vector<std::string> args = std::vector<std::string>(
it, std::istream_iterator<std::string>());
std::string cmd = args[0];
int nargs = args.size() - 1;
if (cmd == "patword") {
if (nargs != 2) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
uint32_t addr = StringTo<uint32_t>(args[1]);
if (addr >= MAX_PATTERN_LENGTH) {
throw RuntimeError("Invalid address for " + ToString(args));
}
pat->word[addr] = StringTo<uint64_t>(args[2]);
} else if (cmd == "patioctrl") {
if (nargs != 1) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
pat->ioctrl = StringTo<uint64_t>(args[1]);
} else if (cmd == "patlimits") {
if (nargs != 2) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
pat->limits[0] = StringTo<uint32_t>(args[1]);
pat->limits[1] = StringTo<uint32_t>(args[2]);
} else if (cmd == "patloop0" || cmd == "patloop1" ||
cmd == "patloop2") {
if (nargs != 2) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
int level = cmd[cmd.find_first_of("012")] - '0';
int loop1 = StringTo<uint32_t>(args[1]);
int loop2 = StringTo<uint32_t>(args[2]);
pat->loop[level * 2 + 0] = loop1;
pat->loop[level * 2 + 1] = loop2;
} else if (cmd == "patnloop0" || cmd == "patnloop1" ||
cmd == "patnloop2") {
if (nargs != 1) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
int level = cmd[cmd.find_first_of("012")] - '0';
pat->nloop[level] = StringTo<uint32_t>(args[1]);
} else if (cmd == "patwait0" || cmd == "patwait1" ||
cmd == "patwait2") {
if (nargs != 1) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
int level = cmd[cmd.find_first_of("012")] - '0';
pat->wait[level] = StringTo<uint32_t>(args[1]);
} else if (cmd == "patwaittime0" || cmd == "patwaittime1" ||
cmd == "patwaittime2") {
if (nargs != 1) {
throw RuntimeError("Invalid arguments for " +
ToString(args));
}
int level = cmd[cmd.find_first_of("012")] - '0';
pat->waittime[level] = StringTo<uint64_t>(args[1]);
} else {
throw RuntimeError("Unknown command in pattern file " + cmd);
}
}
}
}
void Pattern::save(const std::string &fname) {
std::ofstream output_file(fname);
if (!output_file) {
throw RuntimeError("Could not open pattern file " + fname +
" for writing");
}
std::ostringstream os;
// pattern word
for (uint32_t i = pat->limits[0]; i <= pat->limits[1]; ++i) {
output_file << "patword " << ToStringHex(i, 4) << " "
<< ToStringHex(pat->word[i], 16) << std::endl;
}
// patioctrl
output_file << "patioctrl " << ToStringHex(pat->ioctrl, 16) << std::endl;
// patlimits
output_file << "patlimits " << ToStringHex(pat->limits[0], 4) << " "
<< ToStringHex(pat->limits[1], 4) << std::endl;
for (size_t i = 0; i < 3; ++i) {
// patloop
output_file << "patloop" << i << " "
<< ToStringHex(pat->loop[i * 2 + 0], 4) << " "
<< ToStringHex(pat->loop[i * 2 + 1], 4) << std::endl;
// patnloop
output_file << "patnloop" << i << " " << pat->nloop[i] << std::endl;
}
for (size_t i = 0; i < 3; ++i) {
// patwait
output_file << "patwait" << i << " " << ToStringHex(pat->wait[i], 4)
<< std::endl;
// patwaittime
output_file << "patwaittime" << i << " " << pat->waittime[i]
<< std::endl;
}
}
std::string Pattern::str() const {
std::ostringstream oss;
oss << '[' << std::setfill('0') << std::endl;
int addr_width = 4;
int word_width = 16;
for (int i = 0; i < MAX_PATTERN_LENGTH; ++i) {
if (pat->word[i] != 0) {
oss << "patword " << ToStringHex(i, addr_width) << " "
<< ToStringHex(pat->word[i], word_width) << std::endl;
}
}
oss << "patioctrl " << ToStringHex(pat->ioctrl, word_width) << std::endl
<< "patlimits " << ToStringHex(pat->limits[0], addr_width) << " "
<< ToStringHex(pat->limits[1], addr_width) << std::endl
<< "patloop0 " << ToStringHex(pat->loop[0], addr_width) << " "
<< ToStringHex(pat->loop[1], addr_width) << std::endl
<< "patnloop0 " << pat->nloop[0] << std::endl
<< "patloop1 " << ToStringHex(pat->loop[2], addr_width) << " "
<< ToStringHex(pat->loop[3], addr_width) << std::endl
<< "patnloop1 " << pat->nloop[1] << std::endl
<< "patloop2 " << ToStringHex(pat->loop[4], addr_width) << " "
<< ToStringHex(pat->loop[5], addr_width) << std::endl
<< "patnloop2 " << pat->nloop[2] << std::endl
<< "patwait0 " << ToStringHex(pat->wait[0], addr_width) << std::endl
<< "patwaittime0 " << pat->waittime[0] << std::endl
<< "patwait1 " << ToStringHex(pat->wait[1], addr_width) << std::endl
<< "patwaittime1 " << pat->waittime[1] << std::endl
<< "patwait2 " << ToStringHex(pat->wait[2], addr_width) << std::endl
<< "patwaittime2 " << pat->waittime[2] << std::endl
<< ']';
return oss.str();
}
} // namespace sls

View File

@ -15,6 +15,7 @@ target_sources(tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/test-Result.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test-CmdParser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test-Module.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test-Pattern.cpp
)
target_include_directories(tests PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../src>")

View File

@ -319,80 +319,6 @@ TEST_CASE("subdeadtime", "[.cmd]") {
}
}
TEST_CASE("threshold", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::EIGER) {
auto prev_threshold = det.getThresholdEnergy();
auto prev_energies =
det.getTrimEnergies().tsquash("inconsistent trim energies to test");
if (!prev_energies.empty()) {
std::ostringstream oss1, oss2;
proxy.Call("threshold", {"4500", "standard"}, -1, PUT, oss1);
REQUIRE(oss1.str() == "threshold [4500, standard]\n");
proxy.Call("threshold", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "threshold 4500\n");
det.setTrimEnergies(prev_energies);
for (int i = 0; i != det.size(); ++i) {
if (prev_threshold[i] >= 0) {
det.setThresholdEnergy(prev_threshold[i], defs::STANDARD,
true, {i});
}
}
}
REQUIRE_NOTHROW(proxy.Call("threshold", {}, -1, GET));
} else {
REQUIRE_THROWS(proxy.Call("threshold", {}, -1, GET));
}
}
TEST_CASE("thresholdnotb", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::EIGER) {
auto prev_threshold = det.getThresholdEnergy();
auto prev_energies =
det.getTrimEnergies().tsquash("inconsistent trim energies to test");
if (!prev_energies.empty()) {
std::ostringstream oss1, oss2;
proxy.Call("thresholdnotb", {"4500 standard"}, -1, PUT, oss1);
REQUIRE(oss1.str() == "thresholdnotb [4500 standard]\n");
proxy.Call("threshold", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "threshold 4500\n");
det.setTrimEnergies(prev_energies);
for (int i = 0; i != det.size(); ++i) {
if (prev_threshold[i] >= 0) {
det.setThresholdEnergy(prev_threshold[i], defs::STANDARD,
false, {i});
}
}
}
REQUIRE_NOTHROW(proxy.Call("threshold", {}, -1, GET));
} else {
REQUIRE_THROWS(proxy.Call("thresholdnotb", {}, -1, GET));
}
}
TEST_CASE("settingspath", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto prev_val = det.getSettingsPath();
{
std::ostringstream oss1, oss2;
proxy.Call("settingspath", {"/tmp"}, -1, PUT, oss1);
REQUIRE(oss1.str() == "settingspath /tmp\n");
proxy.Call("settingspath", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "settingspath /tmp\n");
}
for (int i = 0; i != det.size(); ++i) {
det.setSettingsPath(prev_val[i], {i});
}
}
TEST_CASE("overflow", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
@ -435,27 +361,6 @@ TEST_CASE("flippeddatax", "[.cmd]") {
}
}
TEST_CASE("trimen", "[.cmd][.this]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::EIGER) {
auto previous = det.getTrimEnergies();
std::ostringstream oss1, oss2;
proxy.Call("trimen", {"4500", "5400", "6400"}, -1, PUT, oss1);
REQUIRE(oss1.str() == "trimen [4500, 5400, 6400]\n");
proxy.Call("trimen", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "trimen [4500, 5400, 6400]\n");
for (int i = 0; i != det.size(); ++i) {
det.setTrimEnergies(previous[i], {i});
}
} else {
REQUIRE_THROWS(proxy.Call("trimen", {"4500", "5400", "6400"}, -1, PUT));
REQUIRE_THROWS(proxy.Call("trimen", {}, -1, GET));
}
}
TEST_CASE("ratecorr", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);

View File

@ -46,6 +46,19 @@ TEST_CASE("savepattern", "[.cmd]") {
}
}
TEST_CASE("defaultpattern", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::MOENCH || det_type == defs::MYTHEN3) {
REQUIRE_THROWS(proxy.Call("defaultpattern", {}, -1, GET));
REQUIRE_NOTHROW(proxy.Call("defaultpattern", {}, -1, PUT));
} else {
REQUIRE_THROWS(proxy.Call("defaultpattern", {}, -1, GET));
REQUIRE_NOTHROW(proxy.Call("defaultpattern", {}, -1, PUT));
}
}
TEST_CASE("patioctrl", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);

View File

@ -130,7 +130,7 @@ TEST_CASE("settingslist", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::CHIPTESTBOARD || det_type == defs::MYTHEN3) {
if (det_type == defs::CHIPTESTBOARD) {
REQUIRE_THROWS(proxy.Call("settingslist", {}, -1, GET));
} else {
REQUIRE_NOTHROW(proxy.Call("settingslist", {}, -1, GET));
@ -174,6 +174,11 @@ TEST_CASE("settings", "[.cmd]") {
sett.push_back("g4_hg");
sett.push_back("g4_lg");
break;
case defs::MYTHEN3:
sett.push_back("standard");
sett.push_back("fast");
sett.push_back("highgain");
break;
default:
if (det_type == defs::EIGER) {
// FIXME: need to remove when settings removed
@ -206,6 +211,181 @@ TEST_CASE("settings", "[.cmd]") {
}
}
TEST_CASE("threshold", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::EIGER) {
auto prev_threshold = det.getThresholdEnergy();
auto prev_energies =
det.getTrimEnergies().tsquash("inconsistent trim energies to test");
if (!prev_energies.empty()) {
std::string senergy = std::to_string(prev_energies[0]);
std::ostringstream oss1, oss2;
proxy.Call("threshold", {senergy, "standard"}, -1, PUT, oss1);
REQUIRE(oss1.str() == "threshold [" + senergy + ", standard]\n");
proxy.Call("threshold", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "threshold " + senergy + "\n");
REQUIRE_THROWS(proxy.Call(
"threshold", {senergy, senergy, senergy, "standard"}, -1, PUT));
REQUIRE_THROWS(
proxy.Call("threshold", {senergy, "undefined"}, -1, PUT));
det.setTrimEnergies(prev_energies);
for (int i = 0; i != det.size(); ++i) {
if (prev_threshold[i] >= 0) {
det.setThresholdEnergy(prev_threshold[i], defs::STANDARD,
true, {i});
}
}
}
REQUIRE_NOTHROW(proxy.Call("threshold", {}, -1, GET));
} else if (det_type == defs::MYTHEN3) {
auto prev_threshold = det.getAllThresholdEnergy();
auto prev_settings =
det.getSettings().tsquash("inconsistent settings to test");
auto prev_energies =
det.getTrimEnergies().tsquash("inconsistent trim energies to test");
if (!prev_energies.empty()) {
std::string senergy = std::to_string(prev_energies[0]);
std::ostringstream oss1, oss2;
proxy.Call("threshold", {senergy, "standard"}, -1, PUT, oss1);
REQUIRE(oss1.str() == "threshold [" + senergy + ", standard]\n");
proxy.Call("threshold", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "threshold [" + senergy + ", " + senergy +
", " + senergy + "]\n");
std::string senergy2 = std::to_string(prev_energies[1]);
std::string senergy3 = std::to_string(prev_energies[2]);
std::ostringstream oss3, oss4;
proxy.Call("threshold", {senergy, senergy2, senergy3, "standard"},
-1, PUT, oss3);
REQUIRE(oss3.str() == "threshold [" + senergy + ", " + senergy2 +
", " + senergy3 + ", standard]\n");
proxy.Call("threshold", {}, -1, GET, oss4);
REQUIRE(oss4.str() == "threshold [" + senergy + ", " + senergy2 +
", " + senergy3 + "]\n");
REQUIRE_THROWS(proxy.Call("threshold",
{senergy, senergy, "standard"}, -1, PUT));
REQUIRE_THROWS(
proxy.Call("threshold", {senergy, "undefined"}, -1, PUT));
REQUIRE_NOTHROW(proxy.Call("threshold", {senergy}, -1, PUT));
REQUIRE_NOTHROW(proxy.Call("threshold",
{senergy, senergy2, senergy3}, -1, PUT));
det.setTrimEnergies(prev_energies);
for (int i = 0; i != det.size(); ++i) {
if (prev_threshold[i][0] >= 0) {
std::cout
<< "prev cvalues:" << sls::ToString(prev_threshold[i])
<< std::endl;
det.setThresholdEnergy(prev_threshold[i], prev_settings,
true, {i});
}
}
}
REQUIRE_NOTHROW(proxy.Call("threshold", {}, -1, GET));
} else {
REQUIRE_THROWS(proxy.Call("threshold", {}, -1, GET));
}
}
TEST_CASE("thresholdnotb", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::EIGER) {
auto prev_threshold = det.getThresholdEnergy();
auto prev_energies =
det.getTrimEnergies().tsquash("inconsistent trim energies to test");
if (!prev_energies.empty()) {
std::string senergy = std::to_string(prev_energies[0]);
std::ostringstream oss1, oss2;
proxy.Call("thresholdnotb", {senergy, "standard"}, -1, PUT, oss1);
REQUIRE(oss1.str() == "threshold [" + senergy + ", standard]\n");
proxy.Call("threshold", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "threshold " + senergy + "\n");
REQUIRE_THROWS(proxy.Call("thresholdnotb",
{senergy, senergy, senergy, "standard"},
-1, PUT));
REQUIRE_THROWS(
proxy.Call("thresholdnotb", {senergy, "undefined"}, -1, PUT));
det.setTrimEnergies(prev_energies);
for (int i = 0; i != det.size(); ++i) {
if (prev_threshold[i] >= 0) {
det.setThresholdEnergy(prev_threshold[i], defs::STANDARD,
false, {i});
}
}
}
REQUIRE_NOTHROW(proxy.Call("threshold", {}, -1, GET));
} else if (det_type == defs::MYTHEN3) {
auto prev_threshold = det.getAllThresholdEnergy();
auto prev_settings =
det.getSettings().tsquash("inconsistent settings to test");
auto prev_energies =
det.getTrimEnergies().tsquash("inconsistent trim energies to test");
if (!prev_energies.empty()) {
std::string senergy = std::to_string(prev_energies[0]);
std::ostringstream oss1, oss2;
proxy.Call("thresholdnotb", {senergy, "standard"}, -1, PUT, oss1);
REQUIRE(oss1.str() ==
"thresholdnotb [" + senergy + ", standard]\n");
proxy.Call("threshold", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "threshold [" + senergy + ", " + senergy +
", " + senergy + "]\n");
std::string senergy2 = std::to_string(prev_energies[1]);
std::string senergy3 = std::to_string(prev_energies[2]);
std::ostringstream oss3, oss4;
proxy.Call("thresholdnotb",
{senergy, senergy2, senergy3, "standard"}, -1, PUT,
oss3);
REQUIRE(oss3.str() == "thresholdnotb [" + senergy + ", " +
senergy2 + ", " + senergy3 +
", standard]\n");
proxy.Call("threshold", {}, -1, GET, oss4);
REQUIRE(oss4.str() == "threshold [" + senergy + ", " + senergy2 +
", " + senergy3 + "]\n");
REQUIRE_THROWS(proxy.Call("thresholdnotb",
{senergy, senergy, "standard"}, -1, PUT));
REQUIRE_THROWS(
proxy.Call("thresholdnotb", {senergy, "undefined"}, -1, PUT));
REQUIRE_NOTHROW(proxy.Call("thresholdnotb", {senergy}, -1, PUT));
REQUIRE_NOTHROW(proxy.Call("thresholdnotb",
{senergy, senergy2, senergy3}, -1, PUT));
det.setTrimEnergies(prev_energies);
for (int i = 0; i != det.size(); ++i) {
if (prev_threshold[i][0] >= 0) {
det.setThresholdEnergy(prev_threshold[i], prev_settings,
true, {i});
}
}
}
REQUIRE_NOTHROW(proxy.Call("threshold", {}, -1, GET));
} else {
REQUIRE_THROWS(proxy.Call("thresholdnotb", {}, -1, GET));
}
}
TEST_CASE("settingspath", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto prev_val = det.getSettingsPath();
{
std::ostringstream oss1, oss2;
proxy.Call("settingspath", {"/tmp"}, -1, PUT, oss1);
REQUIRE(oss1.str() == "settingspath /tmp\n");
proxy.Call("settingspath", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "settingspath /tmp\n");
}
for (int i = 0; i != det.size(); ++i) {
det.setSettingsPath(prev_val[i], {i});
}
}
TEST_CASE("trimbits", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
@ -246,6 +426,65 @@ TEST_CASE("trimval", "[.cmd]") {
}
}
TEST_CASE("trimen", "[.cmd][.this]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::EIGER || det_type == defs::MYTHEN3) {
auto previous = det.getTrimEnergies();
std::ostringstream oss1, oss2;
proxy.Call("trimen", {"4500", "5400", "6400"}, -1, PUT, oss1);
REQUIRE(oss1.str() == "trimen [4500, 5400, 6400]\n");
proxy.Call("trimen", {}, -1, GET, oss2);
REQUIRE(oss2.str() == "trimen [4500, 5400, 6400]\n");
for (int i = 0; i != det.size(); ++i) {
det.setTrimEnergies(previous[i], {i});
}
} else {
REQUIRE_THROWS(proxy.Call("trimen", {"4500", "5400", "6400"}, -1, PUT));
REQUIRE_THROWS(proxy.Call("trimen", {}, -1, GET));
}
}
TEST_CASE("gappixels", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::JUNGFRAU || det_type == defs::EIGER) {
auto prev_val = det.getGapPixelsinCallback();
{
std::ostringstream oss;
proxy.Call("gappixels", {"1"}, -1, PUT, oss);
REQUIRE(oss.str() == "gappixels 1\n");
}
{
std::ostringstream oss;
proxy.Call("gappixels", {"0"}, -1, PUT, oss);
REQUIRE(oss.str() == "gappixels 0\n");
}
{
std::ostringstream oss;
proxy.Call("gappixels", {}, -1, GET, oss);
REQUIRE(oss.str() == "gappixels 0\n");
}
det.setGapPixelsinCallback(prev_val);
} else {
{
std::ostringstream oss;
proxy.Call("gappixels", {}, -1, GET, oss);
REQUIRE(oss.str() == "gappixels 0\n");
}
{
std::ostringstream oss;
proxy.Call("gappixels", {"0"}, -1, PUT, oss);
REQUIRE(oss.str() == "gappixels 0\n");
}
REQUIRE_THROWS(proxy.Call("gappixels", {"1"}, -1, PUT));
}
}
/* acquisition parameters */
// acquire: not testing
@ -1510,44 +1749,6 @@ TEST_CASE("scanerrmsg", "[.cmd]") {
REQUIRE_THROWS(proxy.Call("scanerrmsg", {""}, -1, PUT));
}
TEST_CASE("gappixels", "[.cmd]") {
Detector det;
CmdProxy proxy(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::JUNGFRAU || det_type == defs::EIGER) {
auto prev_val = det.getGapPixelsinCallback();
{
std::ostringstream oss;
proxy.Call("gappixels", {"1"}, -1, PUT, oss);
REQUIRE(oss.str() == "gappixels 1\n");
}
{
std::ostringstream oss;
proxy.Call("gappixels", {"0"}, -1, PUT, oss);
REQUIRE(oss.str() == "gappixels 0\n");
}
{
std::ostringstream oss;
proxy.Call("gappixels", {}, -1, GET, oss);
REQUIRE(oss.str() == "gappixels 0\n");
}
det.setGapPixelsinCallback(prev_val);
} else {
{
std::ostringstream oss;
proxy.Call("gappixels", {}, -1, GET, oss);
REQUIRE(oss.str() == "gappixels 0\n");
}
{
std::ostringstream oss;
proxy.Call("gappixels", {"0"}, -1, PUT, oss);
REQUIRE(oss.str() == "gappixels 0\n");
}
REQUIRE_THROWS(proxy.Call("gappixels", {"1"}, -1, PUT));
}
}
/* Network Configuration (Detector<->Receiver) */
TEST_CASE("numinterfaces", "[.cmd]") {

View File

@ -0,0 +1,31 @@
#include "catch.hpp"
#include "sls/Pattern.h"
using sls::Pattern;
TEST_CASE("Pattern is default constructable and has zeroed fields"){
Pattern p;
for (int i = 0; i!=MAX_PATTERN_LENGTH; ++i)
REQUIRE(p.data()->word[i] == 0);
REQUIRE(p.data()->ioctrl == 0);
}
TEST_CASE("Copy construct pattern"){
Pattern p;
p.data()->loop[0] = 7;
Pattern p1(p);
REQUIRE(p1.data()->loop[0] == 7);
}
TEST_CASE("Compare patterns"){
Pattern p;
Pattern p1;
REQUIRE(p == p1);
p1.data()->word[500] = 1;
REQUIRE_FALSE(p == p1);
}