diff --git a/.gitignore b/.gitignore index cdbb78d8e..8cec4c3be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ #Build files *.o -build/ #Backup files *~ @@ -16,6 +15,8 @@ latex/ tmp/ doc/classdiagramRawEvent.svg ValgrindOut.xml +doc/ +doxywarning.txt #Compressed Files *.bz2 @@ -24,8 +25,9 @@ ValgrindOut.xml #Ignore the build and exec directory build/ +*build-*/ install/ -doc/ + #Ignore IDE files *.cbp @@ -44,4 +46,4 @@ HIS LDF HIS/* LDF/* -data +data \ No newline at end of file diff --git a/Acquisition/CMakeLists.txt b/Acquisition/CMakeLists.txt new file mode 100644 index 000000000..ff6dabe01 --- /dev/null +++ b/Acquisition/CMakeLists.txt @@ -0,0 +1,21 @@ +#Adds the install prefix for referencing in the source code +add_definitions(-D INSTALL_PREFIX="\\"${CMAKE_INSTALL_PREFIX}\\"") + +#Build the pixie interface +include_directories(Interface/include) +add_subdirectory(Interface/source) + +#Build the MCA objects +include_directories(MCA/include) +add_subdirectory(MCA) + +#Build PxiDump +add_subdirectory(PxiDump) + +#Build poll +add_subdirectory(Poll) + +#Build the setup tools +if (PAASS_BUILD_SETUP) + add_subdirectory(Setup) +endif(PAASS_BUILD_SETUP) \ No newline at end of file diff --git a/Interface/include/Lock.h b/Acquisition/Interface/include/Lock.h similarity index 100% rename from Interface/include/Lock.h rename to Acquisition/Interface/include/Lock.h diff --git a/Interface/include/PixieInterface.h b/Acquisition/Interface/include/PixieInterface.h similarity index 92% rename from Interface/include/PixieInterface.h rename to Acquisition/Interface/include/PixieInterface.h index 18637f491..860d7c4b0 100644 --- a/Interface/include/PixieInterface.h +++ b/Acquisition/Interface/include/PixieInterface.h @@ -91,6 +91,8 @@ class PixieInterface bool ReadConfigurationFile(const char *fn); + /// @brief Parses the input from configuration file for the ModuleType tag. + std::string ParseModuleTypeTag(std::string value); bool GetSlots(const char *slotF = NULL); // wrappers to the pixie-16 app functions bool Init(bool offlineMode = false); @@ -119,7 +121,7 @@ class PixieInterface double GetLiveTime(int mod, int chan); double GetRealTime(int mod); double GetProcessedEvents(int mod); - bool GetModuleInfo(unsigned short mod, unsigned short *rev, unsigned int *serNum, unsigned short *adcBits, unsigned short *adcMsps); + bool GetModuleInfo(const unsigned short &mod, unsigned short *rev, unsigned int *serNum, unsigned short *adcBits, unsigned short *adcMsps); // # # bool StartHistogramRun(unsigned short mode = NEW_RUN); bool StartHistogramRun(unsigned short mod, unsigned short mode); @@ -180,19 +182,17 @@ class PixieInterface #endif static std::set validConfigKeys; - std::map configStrings; + std::map> configStrings; bool doneInit; - // convert a configuration string to be relative to pixieBaseDir unless it begins with a . - std::string ConfigFileName(const std::string &str); + /// @brief Convert a configuration string to be relative to PixieBaseDir unless it begins with a . + std::string ConfigFileName(const std::string &type, const std::string &str); // checks retval and outputs default OK/ERROR message bool CheckError(bool exitOnError = false) const; unsigned short numberCards; unsigned short slotMap[MAX_MODULES]; - unsigned short firmwareConfig[MAX_MODULES]; - bool hasAlternativeConfig; stats_t statistics; diff --git a/Interface/include/PixieSupport.h b/Acquisition/Interface/include/PixieSupport.h similarity index 85% rename from Interface/include/PixieSupport.h rename to Acquisition/Interface/include/PixieSupport.h index f62aaded1..03c20cfd3 100644 --- a/Interface/include/PixieSupport.h +++ b/Acquisition/Interface/include/PixieSupport.h @@ -28,60 +28,58 @@ class PixieFunction : public std::unary_function -bool forChannel(PixieInterface *pif, int mod, int ch, PixieFunction &f, T par){ +bool forChannel(PixieInterface *pif, int modStart, int modStop, int chStart, int chStop, PixieFunction &f, T par){ PixieFunctionParms parms(pif, par); bool hadError = false; - if (mod < 0) { - for (parms.mod = 0; parms.mod < pif->GetNumberCards(); parms.mod++) { - if (ch < 0) { - for (parms.ch = 0; parms.ch < pif->GetNumberChannels(); parms.ch++) { - if (!f(parms)){ hadError = true; } - } - } - else { - parms.ch = ch; - if (!f(parms)) - hadError = true; - } + if (modStart < 0) { + modStart = 0; + modStop = pif->GetNumberCards() - 1; + } + if (chStart < 0) { + chStart = 0; + chStop = pif->GetNumberChannels() - 1; + } + + for (parms.mod = modStart; parms.mod <= modStop; parms.mod++) { + for (parms.ch = chStart; parms.ch <= chStop; parms.ch++) { + if (!f(parms)){ hadError = true; } } } - else { - parms.mod = mod; - if (ch < 0) { - for (parms.ch = 0; parms.ch < pif->GetNumberChannels(); parms.ch++) { - if (!f(parms)){ hadError = true; } - } - } - else { - parms.ch = ch; - hadError = !f(parms); - } - } return !hadError; } template -bool forModule(PixieInterface *pif, int mod, PixieFunction &f, T par) +bool forChannel(PixieInterface *pif, int mod, int ch, PixieFunction &f, T par){ + return forChannel(pif, mod, mod, ch, ch, f, par); +} + +template +bool forModule(PixieInterface *pif, int modStart, int modStop, PixieFunction &f, T par) { PixieFunctionParms parms(pif, par); bool hadError = false; - if (mod < 0) { - for (parms.mod = 0; parms.mod < pif->GetNumberCards(); parms.mod++) { - if (!f(parms)){ hadError = true; } - } - } - else { - parms.mod = mod; - hadError = !f(parms); + if (modStart < 0) { + modStart = 0; + modStop = pif->GetNumberCards() -1; + } + for (parms.mod = modStart; parms.mod <= modStop; parms.mod++) { + if (!f(parms)){ hadError = true; } } return !hadError; } +template +bool forModule(PixieInterface *pif, int mod, PixieFunction &f, T par) +{ + return forModule(pif, mod, mod, f, par); +} + + std::string PadStr(const std::string &input_, int width_); template diff --git a/Interface/include/Utility.h b/Acquisition/Interface/include/Utility.h similarity index 100% rename from Interface/include/Utility.h rename to Acquisition/Interface/include/Utility.h diff --git a/Interface/source/CMakeLists.txt b/Acquisition/Interface/source/CMakeLists.txt similarity index 64% rename from Interface/source/CMakeLists.txt rename to Acquisition/Interface/source/CMakeLists.txt index afbf41bac..f8afd3d44 100644 --- a/Interface/source/CMakeLists.txt +++ b/Acquisition/Interface/source/CMakeLists.txt @@ -2,8 +2,9 @@ set(Interface_SOURCES PixieInterface.cpp Lock.cpp) add_library(PixieInterface STATIC ${Interface_SOURCES}) -#Order is important, PXI before PLX -target_link_libraries(PixieInterface PixieCoreStatic ${PXI_LIBRARIES} ${PLX_LIBRARIES}) +#Order is important, XIA before PLX +target_link_libraries(PixieInterface PaassCoreStatic ${XIA_LIBRARIES} + ${PLX_LIBRARIES}) set(Support_SOURCES PixieSupport.cpp) add_library(PixieSupport STATIC ${Support_SOURCES}) diff --git a/Interface/source/Lock.cpp b/Acquisition/Interface/source/Lock.cpp similarity index 100% rename from Interface/source/Lock.cpp rename to Acquisition/Interface/source/Lock.cpp diff --git a/Interface/source/PixieInterface.cpp b/Acquisition/Interface/source/PixieInterface.cpp similarity index 69% rename from Interface/source/PixieInterface.cpp rename to Acquisition/Interface/source/PixieInterface.cpp index b81bba3b5..a14e1858d 100644 --- a/Interface/source/PixieInterface.cpp +++ b/Acquisition/Interface/source/PixieInterface.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -96,39 +97,37 @@ bool PixieInterface::Histogram::Write(ofstream &out) return true; } -PixieInterface::PixieInterface(const char *fn) : hasAlternativeConfig(false), lock("PixieInterface") +PixieInterface::PixieInterface(const char *fn) : lock("PixieInterface") { SetColorTerm(); // Set-up valid configuration keys if they don't exist yet if (validConfigKeys.empty()) { - //? perhaps these should allow more than just one alternate firmware configuration - validConfigKeys.insert("AltComFpgaFile"); - validConfigKeys.insert("AltDspConfFile"); - validConfigKeys.insert("AltDspVarFile"); - validConfigKeys.insert("AltSpFpgaFile"); - validConfigKeys.insert("AltTrigFpgaFile"); - // standard files + // module files + validConfigKeys.insert("ModuleType"); + validConfigKeys.insert("ModuleBaseDir"); + validConfigKeys.insert("SpFpgaFile"); + validConfigKeys.insert("TrigFpgaFile"); validConfigKeys.insert("ComFpgaFile"); validConfigKeys.insert("DspConfFile"); validConfigKeys.insert("DspVarFile"); + // global files + validConfigKeys.insert("PixieBaseDir"); validConfigKeys.insert("DspSetFile"); validConfigKeys.insert("DspWorkingSetFile"); validConfigKeys.insert("ListModeFile"); - validConfigKeys.insert("PixieBaseDir"); validConfigKeys.insert("SlotFile"); - validConfigKeys.insert("SpFpgaFile"); - validConfigKeys.insert("TrigFpgaFile"); validConfigKeys.insert("CrateConfig"); } if (!ReadConfigurationFile(fn)) { - std::cout << Display::ErrorStr() << " Unable to read configuration file: '" << fn << "\n"; - std::cout << Display::InfoStr() << " Did you forget to copy default " - "configuration files from '" << INSTALL_PREFIX - << "/share/config' to the running directory?\n"; + std::cout << Display::ErrorStr() << " Unable to read configuration file: '" << fn << "'\n"; + if (configStrings.find("global") == configStrings.end()) { + std::cout << Display::InfoStr() << " Are the configuration files in the running directory?\n"; + std::cout << "Autoconfigured files are avaialable in " << INSTALL_PREFIX << "\n"; + } exit(EXIT_FAILURE); } //Overwrite the default path 'pxisys.ini' with the one specified in the scan file. - PCISysIniFile = configStrings["CrateConfig"].c_str(); + PCISysIniFile = configStrings["global"]["CrateConfig"].c_str(); } @@ -145,6 +144,68 @@ PixieInterface::~PixieInterface() retval = Pixie16ExitSystem(numberCards); CheckError(); } +std::string PixieInterface::ParseModuleTypeTag(std::string value) { + std::string moduleType = "invalid"; + int adcRes = -1, msps = -1; + string revision = ""; + + string adcResStr = ""; + size_t adcResLocEnd = value.find('b'); + if (adcResLocEnd != string::npos) { + size_t adcResLocBegin = value.find_last_not_of("0123456789", adcResLocEnd - 1); + if (adcResLocBegin == string::npos) adcResLocBegin = 0; + else adcResLocBegin++; + adcResStr = value.substr(adcResLocBegin, adcResLocEnd - adcResLocBegin); + } + + if (adcResStr.empty()) { + std::cout << ErrorStr() << " Invalid ModuleType, ADC resolution not specified: '" << InfoStr(value) << "'.\n"; + } + else { + try { adcRes = std::stoi(adcResStr); } + catch (const std::invalid_argument& ia) { + std::cout << ErrorStr() << " Invalid ADC resolution: '" << value << "' (" << adcResStr << ")\n"; + } + } + + string mspsStr = ""; + size_t mspsLocEnd = value.find('m'); + if (mspsLocEnd != string::npos) { + size_t mspsLocBegin = value.find_last_not_of("0123456789", mspsLocEnd - 1); + if (mspsLocBegin == string::npos) mspsLocBegin = 0; + else mspsLocBegin++; + mspsStr = value.substr(mspsLocBegin, mspsLocEnd - mspsLocBegin); + } + + if (mspsStr.empty()) { + std::cout << ErrorStr() << " Invalid ModuleType, sample rate not specified: '" << InfoStr(value) << "'.\n"; + } + else { + try { msps = std::stoi(mspsStr); } + catch (const std::invalid_argument& ia) { + std::cout << ErrorStr() << " Invalid sample rate: '" << value << "' (" << mspsStr << ")\n"; + } + } + + size_t revLoc = value.find("rev"); + string revStr = "not specified"; + if (revLoc != string::npos) revStr = value.substr(revLoc+3, 1); + if (revStr.length() == 1) { + revision = revStr; + } + else { + std::cout << ErrorStr() << " Invalid Revision: '" << value << "' (" << revStr << ")\n"; + } + + if (adcRes > 0 && msps > 0 && revision != "") { + stringstream moduleTypeStream; + moduleTypeStream << adcRes << "b" << msps << "m-rev" << revision; + moduleType = moduleTypeStream.str(); + } + + return moduleType; + +} bool PixieInterface::ReadConfigurationFile(const char *fn) { @@ -155,8 +216,13 @@ bool PixieInterface::ReadConfigurationFile(const char *fn) stringbuf tag, value; string line; + std::cout << "Reading Pixie Configuration\n"; + //Loop over lines in config file - while (std::getline(in,line)) { + bool error = false; + bool newModule = false; + string moduleType = ""; + while (std::getline(in,line)) { //Get a string stream of current line std::istringstream lineStream(line); //If the line leads with a '#' we ignore it. @@ -169,13 +235,63 @@ bool PixieInterface::ReadConfigurationFile(const char *fn) if (validConfigKeys.find(tag) == validConfigKeys.end()) { cout << "Unrecognized tag " << WarningStr(tag) << " in PixieInterface configuration file." << endl; } + + //Parse the ModuleType tag. + //Moule type is expected as with the following three items ##b, ###m, rev# + if (tag == "ModuleType") { + + moduleType = ParseModuleTypeTag(value); + + std::cout << "Module Type: "; + + //If we have multiple entires for one type we throw and error. + if (configStrings.find(moduleType) != configStrings.end()) { + error = true; + + std::cout << ErrorStr(moduleType) << "\n"; + + std::cout << ErrorStr() << " Duplicate module type information found for " << moduleType << "!\n"; + std::cout << " Remove or comment out tags to be ignored.\n"; + + moduleType = "ignored_" + moduleType; + } + else {std::cout << InfoStr(moduleType) << "\n";} + + newModule = true; + } //Store configuration - configStrings[tag] = ConfigFileName(value); + else if (tag == "SpFpgaFile" || tag == "ComFpgaFile" || tag == "DspConfFile" || tag == "DspVarFile" || tag == "TrigFpgaFile" || tag == "ModuleBaseDir") { + if (moduleType == "") { + moduleType = "default"; + std::cout << "Module Type: " << InfoStr(moduleType) << "\n"; + } + if (newModule && tag != "ModuleBaseDir") { + std::cout << " PixieBaseDir\t" << configStrings["global"]["PixieBaseDir"] << "\n"; + } + newModule = false; + if (configStrings[moduleType][tag] != "") { + error = true; + + std::cout << " " << ErrorStr(tag) << "\t" << value << endl; + + std::cout << ErrorStr() << " Duplicate " << tag << " specified for " << moduleType << "!\n"; + std::cout << " Remove or comment out tags to be ignored.\n"; + + tag = "ignored_" + tag; + } + else { + std::cout << " " << tag << "\t" << value << endl; + } + configStrings[moduleType][tag] = ConfigFileName(moduleType,value); + } + else { + std::cout << " " << tag << "\t" << value << endl; + configStrings["global"][tag] = ConfigFileName("global",value); + } //Check if BaseDir is defined differently then in the environment if (tag == "PixieBaseDir") { - cout << "Pixie base directory is " << InfoStr(value) << endl; // check if this matches the environment PXI_ROOT if it is set if (getenv("PXI_ROOT") != NULL) { if ( value != string(getenv("PXI_ROOT")) ) { @@ -186,6 +302,8 @@ bool PixieInterface::ReadConfigurationFile(const char *fn) } } + if (error) return false; + return true; } @@ -194,7 +312,7 @@ bool PixieInterface::GetSlots(const char *slotF) char restOfLine[CONFIG_LINE_LENGTH]; if (slotF == NULL) - slotF = configStrings["SlotFile"].c_str(); + slotF = configStrings["global"]["SlotFile"].c_str(); ifstream in(slotF); @@ -213,13 +331,6 @@ bool PixieInterface::GetSlots(const char *slotF) } for (int i = 0; i < numberCards; i++) { - // check if this is a module with an alternative firmware configuration (tagged with '*') - if (in.peek() == '*') { - in.ignore(); - hasAlternativeConfig = true; - firmwareConfig[i] = 1; // alternative config - } else firmwareConfig[i] = 0; // standard config - in >> slotMap[i]; in.getline(restOfLine, CONFIG_LINE_LENGTH, '\n'); if (!in.good()) { @@ -259,51 +370,86 @@ bool PixieInterface::Init(bool offlineMode) bool PixieInterface::Boot(int mode, bool useWorkingSetFile) { string &setFile = useWorkingSetFile ? - configStrings["DspWorkingSetFile"] : configStrings["DspSetFile"]; + configStrings["global"]["DspWorkingSetFile"] : configStrings["global"]["DspSetFile"]; - LeaderPrint("Booting Pixie"); + LeaderPrint("Boot Configuration"); - //Break the leader print for the Boot status. - if (mode == BootAll) std::cout << std::endl; + //Loop through each module and determine its type. + //We also check if the modules are all the same. If not we set multiConf to true. + bool multiConf = false; + std::vector< std::string > moduleTypes; + for (int mod = 0; mod < numberCards; mod++) { + unsigned short rev, adcBits, adcMsps; + unsigned int serNum; + GetModuleInfo(mod, &rev, &serNum, &adcBits, &adcMsps); + + stringstream moduleType; + moduleType << adcBits << "b" << adcMsps << "m"; + moduleType << "-rev" << (char)(97 + rev - 10 ); + + if (mod > 0 && moduleType.str() != moduleTypes.back()) multiConf = true; + moduleTypes.push_back(moduleType.str()); + } bool goodBoot = true; - if (hasAlternativeConfig) { + if (multiConf) { // must proceed through boot module by module - cout << InfoStr("[MULTICONFIG]") << "\n"; - for (int i=0; i < numberCards; i++) { - if (firmwareConfig[i] == 1) { - // use the Alt... files - retval = Pixie16BootModule(&configStrings["AltComFpgaFile"][0], - &configStrings["AltSpFpgaFile"][0], - &configStrings["AltTrigFpgaFile"][0], - &configStrings["AltDspConfFile"][0], - &setFile[0], - &configStrings["AltDspVarFile"][0], - i, mode); - } else { - // use the standard files - retval = Pixie16BootModule(&configStrings["ComFpgaFile"][0], - &configStrings["SpFpgaFile"][0], - &configStrings["TrigFpgaFile"][0], - &configStrings["DspConfFile"][0], - &setFile[0], - &configStrings["DspVarFile"][0], - i, mode); + + //Break the leader print for the boot configuration status. + cout << InfoStr("[MULTI]") << "\n"; + + //Check that all module types are valid. + bool error = false; + for (int mod = 0; mod < numberCards; mod++) { + if (configStrings.find(moduleTypes.at(mod)) == configStrings.end()) { + std::cout << ErrorStr() << " Configuration not defined for type " << moduleTypes.at(mod) << " (mod " << mod << ")\n"; + error = true; } - LeaderPrint("Booting Pixie Module "); + } + if (error) return false; + + for (int i=0; i < numberCards; i++) { + retval = Pixie16BootModule(&configStrings[moduleTypes.at(i)]["ComFpgaFile"][0], + &configStrings[moduleTypes.at(i)]["SpFpgaFile"][0], + &configStrings[moduleTypes.at(i)]["TrigFpgaFile"][0], + &configStrings[moduleTypes.at(i)]["DspConfFile"][0], + &setFile[0], + &configStrings[moduleTypes.at(i)]["DspVarFile"][0], + i, mode); + + stringstream leader; + leader << "Booting Pixie (" << moduleTypes.at(i) << ") Module " << i; + LeaderPrint(leader.str()); goodBoot = (goodBoot && !CheckError(true)); } } else { + //Break the leader print for the boot configuration status. + cout << InfoStr("[SINGLE]") << "\n"; + + //Determine if we need to use "default" type. + string moduleType = moduleTypes.front(); + if (configStrings.find(moduleType) == configStrings.end()) { + moduleType = "default"; + if (configStrings.find(moduleType) == configStrings.end()) { + std::cout << ErrorStr() << " Config not defined for type " << moduleTypes.back() << "\n"; + return false; + } + } + //std::cout << "Booting all modules as type " << InfoStr(moduleType) << "\n"; + // boot all at once - retval = Pixie16BootModule(&configStrings["ComFpgaFile"][0], - &configStrings["SpFpgaFile"][0], - &configStrings["TrigFpgaFile"][0], - &configStrings["DspConfFile"][0], + retval = Pixie16BootModule(&configStrings[moduleType]["ComFpgaFile"][0], + &configStrings[moduleType]["SpFpgaFile"][0], + &configStrings[moduleType]["TrigFpgaFile"][0], + &configStrings[moduleType]["DspConfFile"][0], &setFile[0], - &configStrings["DspVarFile"][0], + &configStrings[moduleType]["DspVarFile"][0], numberCards, mode); - if (mode == BootAll) LeaderPrint("Booting Pixie"); + + stringstream leader; + leader << "Booting Pixie (" << moduleType << ")"; + LeaderPrint(leader.str()); goodBoot = !CheckError(true); } @@ -325,6 +471,7 @@ bool PixieInterface::Boot(int mode, bool useWorkingSetFile) hadError = true; } } + if (hadError) cout << ErrorStr() << endl; else if (updated) @@ -446,7 +593,7 @@ void PixieInterface::PrintSglChanPar(const char *name, int mod, int chan, double bool PixieInterface::SaveDSPParameters(const char *fn) { if (fn == NULL) - fn = &configStrings["DspWorkingSetFile"][0]; + fn = &configStrings["global"]["DspWorkingSetFile"][0]; strncpy(tmpName, fn, nameSize); LeaderPrint("Writing DSP parameters"); @@ -824,12 +971,25 @@ bool PixieInterface::ToggleChannelBit(int mod, int chan, const char *parameter, return WriteSglChanPar(parameter, dval, mod, chan); } -string PixieInterface::ConfigFileName(const string &str) +string PixieInterface::ConfigFileName(const string &type, const string &str) { - if (str[0] == '.' || str[0] == '/') - return str; - else - return configStrings["PixieBaseDir"] + '/' + str; + //If the file name starts with a '.' or a '/' then we assume the BaseDir should be ignored. + if (str[0] == '.' || str[0] == '/') return str; + + //Try to determine correct BaseDir. + string baseDir; + //If the file is a global type we use PixieBaseDir + if (type == "global") baseDir = configStrings["global"]["PixieBaseDir"]; + //Otherwise we try the ModuleBaseDir for the specified type and then the PixieBaseDir + else { + baseDir = configStrings[type]["ModuleBaseDir"]; + if (baseDir.empty()) baseDir = configStrings["global"]["PixieBaseDir"]; + } + //No success so we assume they want the local directory. + if (baseDir.empty()) baseDir = "."; + + //Return the appended string. + return baseDir + '/' + str; } bool PixieInterface::CheckError(bool exitOnError) const @@ -841,7 +1001,7 @@ bool PixieInterface::CheckError(bool exitOnError) const return (retval < 0); } -bool PixieInterface::GetModuleInfo(unsigned short mod, unsigned short *rev, unsigned int *serNum, unsigned short *adcBits, unsigned short *adcMsps) { +bool PixieInterface::GetModuleInfo(const unsigned short &mod, unsigned short *rev, unsigned int *serNum, unsigned short *adcBits, unsigned short *adcMsps) { //Return false if error code provided. return (Pixie16ReadModuleInfo(mod,rev,serNum,adcBits,adcMsps) == 0); } diff --git a/Interface/source/PixieSupport.cpp b/Acquisition/Interface/source/PixieSupport.cpp similarity index 100% rename from Interface/source/PixieSupport.cpp rename to Acquisition/Interface/source/PixieSupport.cpp diff --git a/Interface/source/Utility.cpp b/Acquisition/Interface/source/Utility.cpp similarity index 100% rename from Interface/source/Utility.cpp rename to Acquisition/Interface/source/Utility.cpp diff --git a/Interface/source/test.cpp b/Acquisition/Interface/source/test.cpp similarity index 100% rename from Interface/source/test.cpp rename to Acquisition/Interface/source/test.cpp diff --git a/MCA/CMakeLists.txt b/Acquisition/MCA/CMakeLists.txt similarity index 100% rename from MCA/CMakeLists.txt rename to Acquisition/MCA/CMakeLists.txt diff --git a/MCA/include/DrrBlock.h b/Acquisition/MCA/include/DrrBlock.h similarity index 100% rename from MCA/include/DrrBlock.h rename to Acquisition/MCA/include/DrrBlock.h diff --git a/MCA/include/Exceptions.h b/Acquisition/MCA/include/Exceptions.h similarity index 100% rename from MCA/include/Exceptions.h rename to Acquisition/MCA/include/Exceptions.h diff --git a/MCA/include/HisDrr.h b/Acquisition/MCA/include/HisDrr.h similarity index 100% rename from MCA/include/HisDrr.h rename to Acquisition/MCA/include/HisDrr.h diff --git a/MCA/include/MCA.h b/Acquisition/MCA/include/MCA.h similarity index 100% rename from MCA/include/MCA.h rename to Acquisition/MCA/include/MCA.h diff --git a/MCA/include/MCA_DAMM.h b/Acquisition/MCA/include/MCA_DAMM.h similarity index 100% rename from MCA/include/MCA_DAMM.h rename to Acquisition/MCA/include/MCA_DAMM.h diff --git a/MCA/include/MCA_ROOT.h b/Acquisition/MCA/include/MCA_ROOT.h similarity index 100% rename from MCA/include/MCA_ROOT.h rename to Acquisition/MCA/include/MCA_ROOT.h diff --git a/MCA/share/mca_input.txt b/Acquisition/MCA/share/mca_input.txt similarity index 100% rename from MCA/share/mca_input.txt rename to Acquisition/MCA/share/mca_input.txt diff --git a/MCA/source/CMakeLists.txt b/Acquisition/MCA/source/CMakeLists.txt similarity index 79% rename from MCA/source/CMakeLists.txt rename to Acquisition/MCA/source/CMakeLists.txt index cdaaa5b21..7b3b57389 100644 --- a/MCA/source/CMakeLists.txt +++ b/Acquisition/MCA/source/CMakeLists.txt @@ -1,21 +1,23 @@ #Determine the sources based on use flags set (MCA_LIB_SOURCES MCA.cpp) -if (${USE_ROOT}) + +if (PAASS_USE_ROOT) set (MCA_LIB_SOURCES ${MCA_LIB_SOURCES} MCA_ROOT.cpp) -endif() -if (${USE_DAMM}) +endif(PAASS_USE_ROOT) + +if (PAASS_USE_DAMM) set (MCA_LIB_SOURCES ${MCA_LIB_SOURCES} MCA_DAMM.cpp HisDrr.cpp) -endif() +endif(PAASS_USE_DAMM) #build the MCA library add_library(MCA_LIBRARY STATIC ${MCA_LIB_SOURCES}) target_link_libraries(MCA_LIBRARY PixieInterface Utility) -if (${USE_ROOT}) +if (PAASS_USE_ROOT) target_link_libraries(MCA_LIBRARY ${ROOT_LIBRARIES}) -endif() +endif(PAASS_USE_ROOT) #If we can we build the MCA program -if (${USE_DAMM} OR ${USE_ROOT}) +if (PAASS_USE_DAMM OR PAASS_USE_ROOT) add_executable(MCA MCA_exec.cpp) target_link_libraries(MCA MCA_LIBRARY) install(TARGETS MCA DESTINATION bin) diff --git a/MCA/source/HisDrr.cpp b/Acquisition/MCA/source/HisDrr.cpp similarity index 99% rename from MCA/source/HisDrr.cpp rename to Acquisition/MCA/source/HisDrr.cpp index 56ffec8be..bb33bb4e7 100644 --- a/MCA/source/HisDrr.cpp +++ b/Acquisition/MCA/source/HisDrr.cpp @@ -150,7 +150,7 @@ HisDrr::HisDrr(const string &drr, const string &his, const string &input) { // Using information from drrData drr header is created DrrHeader head; - int totLength; + int totLength = 0; for (unsigned int i = 0; i < drrData.size(); ++i) totLength += (drrData[i].scaled[0]+drrData[i].scaled[1])*drrData[i].halfWords; // Magic words (whatever they do...) diff --git a/MCA/source/MCA.cpp b/Acquisition/MCA/source/MCA.cpp similarity index 100% rename from MCA/source/MCA.cpp rename to Acquisition/MCA/source/MCA.cpp diff --git a/MCA/source/MCA_DAMM.cpp b/Acquisition/MCA/source/MCA_DAMM.cpp similarity index 100% rename from MCA/source/MCA_DAMM.cpp rename to Acquisition/MCA/source/MCA_DAMM.cpp diff --git a/MCA/source/MCA_ROOT.cpp b/Acquisition/MCA/source/MCA_ROOT.cpp similarity index 100% rename from MCA/source/MCA_ROOT.cpp rename to Acquisition/MCA/source/MCA_ROOT.cpp diff --git a/MCA/source/MCA_exec.cpp b/Acquisition/MCA/source/MCA_exec.cpp similarity index 100% rename from MCA/source/MCA_exec.cpp rename to Acquisition/MCA/source/MCA_exec.cpp diff --git a/Poll/CMakeLists.txt b/Acquisition/Poll/CMakeLists.txt similarity index 98% rename from Poll/CMakeLists.txt rename to Acquisition/Poll/CMakeLists.txt index 240248e61..673ea3c38 100644 --- a/Poll/CMakeLists.txt +++ b/Acquisition/Poll/CMakeLists.txt @@ -1,4 +1,3 @@ - include_directories(include) add_subdirectory(source) diff --git a/Poll/include/poll2_core.h b/Acquisition/Poll/include/poll2_core.h similarity index 95% rename from Poll/include/poll2_core.h rename to Acquisition/Poll/include/poll2_core.h index c8457c17b..0c04a4729 100644 --- a/Poll/include/poll2_core.h +++ b/Acquisition/Poll/include/poll2_core.h @@ -187,6 +187,9 @@ class Poll{ /// Print help dialogue for reading/writing pixie module parameters. void pmod_help(); + /// Print help dialogue for writing pixie DSP parameters. + void save_help(); + /// Start a data recording run. bool start_run(const bool &record_=true, const double &time_=-1.0); @@ -235,6 +238,13 @@ class Poll{ /// Broadcast a data spill onto the network in the classic pacman format. void broadcast_pac_data(); + /// @brief Splits the arguments to pread and pwrite on a colon delimeter. + /// @param[in] arg The argument to be split. + /// @param[out] start The first value in the string indicating the first mod / ch. + /// @param[out] start The second value in the string indicating the last mod / ch. + /// @return Whether the attempt was succesful. + bool SplitParameterArgs(const std::string &arg, int &start, int &stop); + public: /// Default constructor. Poll(); diff --git a/Poll/include/poll2_stats.h b/Acquisition/Poll/include/poll2_stats.h similarity index 100% rename from Poll/include/poll2_stats.h rename to Acquisition/Poll/include/poll2_stats.h diff --git a/Poll/monitor.bash b/Acquisition/Poll/monitor.bash similarity index 100% rename from Poll/monitor.bash rename to Acquisition/Poll/monitor.bash diff --git a/Poll/send_alarm b/Acquisition/Poll/send_alarm similarity index 100% rename from Poll/send_alarm rename to Acquisition/Poll/send_alarm diff --git a/Poll/source/CMakeLists.txt b/Acquisition/Poll/source/CMakeLists.txt similarity index 78% rename from Poll/source/CMakeLists.txt rename to Acquisition/Poll/source/CMakeLists.txt index 86f4a8672..842a1fe56 100644 --- a/Poll/source/CMakeLists.txt +++ b/Acquisition/Poll/source/CMakeLists.txt @@ -1,18 +1,18 @@ -if(USE_NCURSES) +if(PAASS_USE_NCURSES) set(POLL2_SOURCES poll2.cpp poll2_core.cpp poll2_stats.cpp) add_executable(poll2 ${POLL2_SOURCES}) target_link_libraries(poll2 PixieInterface PixieSupport Utility MCA_LIBRARY ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS poll2 DESTINATION bin) else() message(WARNING "Cannot build poll2 without ncurses!") -endif() +endif(PAASS_USE_NCURSES) set(LISTENER_SOURCES listener.cpp) add_executable(listener ${LISTENER_SOURCES}) -target_link_libraries(listener PixieCoreStatic) +target_link_libraries(listener PaassCoreStatic) set(MONITOR_SOURCES monitor.cpp) add_executable(monitor ${MONITOR_SOURCES}) -target_link_libraries(monitor PixieCoreStatic) +target_link_libraries(monitor PaassCoreStatic) install(TARGETS listener monitor DESTINATION bin) diff --git a/Poll/source/listener.cpp b/Acquisition/Poll/source/listener.cpp similarity index 100% rename from Poll/source/listener.cpp rename to Acquisition/Poll/source/listener.cpp diff --git a/Poll/source/monitor.cpp b/Acquisition/Poll/source/monitor.cpp similarity index 96% rename from Poll/source/monitor.cpp rename to Acquisition/Poll/source/monitor.cpp index 8549970d8..1fb0fa9f8 100644 --- a/Poll/source/monitor.cpp +++ b/Acquisition/Poll/source/monitor.cpp @@ -214,7 +214,17 @@ int main(){ << std::setfill('-') << ""; } std::cout << "|\n"; + + std::cout << " | "; + for(unsigned int j = 0; j < (unsigned int)num_modules; j++){ + std::cout << "ICR "; + std::cout << " OCR "; + std::cout << " Data "; + std::cout << " Total | "; + } + std::cout << "\n"; + for(unsigned int i = 0; i < 16; i++){ std::cout << "C" << std::setw(2) << std:: setfill('0') << i << "|"; for(unsigned int j = 0; j < (unsigned int)num_modules; j++){ diff --git a/Poll/source/poll2.cpp b/Acquisition/Poll/source/poll2.cpp similarity index 100% rename from Poll/source/poll2.cpp rename to Acquisition/Poll/source/poll2.cpp diff --git a/Poll/source/poll2_core.cpp b/Acquisition/Poll/source/poll2_core.cpp similarity index 86% rename from Poll/source/poll2_core.cpp rename to Acquisition/Poll/source/poll2_core.cpp index 58b7d2434..125e22d5d 100644 --- a/Poll/source/poll2_core.cpp +++ b/Acquisition/Poll/source/poll2_core.cpp @@ -8,18 +8,20 @@ * CTerminal. Pixie16 data acquisition is handled by interfacing * with the PixieInterface library. * - * \author Cory R. Thornsberry + * \author Karl Smith, Robert Grzywacz, David Miller, and Cory R. Thornsberry * - * \date Oct. 6th, 2015 + * \date Apr. 25th, 2017 * - * \version 1.3.10 + * \version 1.3.11 */ #include #include #include #include +#include #include + #include #include #include @@ -49,8 +51,8 @@ // Adjusted to help alleviate the issue with data corruption #define POLL_TRIES 100 -// 4 GB. Maximum allowable .ldf file size in bytes -#define MAX_FILE_SIZE 4294967296ll +// 2 GB. Maximum allowable .ldf file size in bytes +#define MAX_FILE_SIZE 2147483648ll // Length of shm packet header (in bytes) #define PKT_HEAD_LEN 8 @@ -88,7 +90,7 @@ const std::vector Poll::runControlCommands_ ({"run", "stop", const std::vector Poll::paramControlCommands_ ({"dump", "pread", "pmread", "pwrite", "pmwrite", "adjust_offsets", "find_tau", "toggle", - "toggle_bit", "csr_test", "bit_test", "get_traces"}); + "toggle_bit", "csr_test", "bit_test", "get_traces", "save"}); const std::vector Poll::pollStatusCommands_ ({"status", "thresh", "debug", "quiet", "quit", "help", "version"}); @@ -233,8 +235,8 @@ void Poll::PrintModuleInfo() { unsigned short revision, adcBits, adcMsps; unsigned int serialNumber; if (pif->GetModuleInfo(mod, &revision, &serialNumber, &adcBits, &adcMsps)) { - std::cout << "Module " << mod << ": " << - "Serial Number " << serialNumber << ", " << + std::cout << "Module " << std::right << std::setw(2) << mod << ": " << + "Serial Number " << std::right << std::setw(4) << serialNumber << ", " << "Rev " << std::hex << std::uppercase << revision << std::dec << " " << "(" << revision << "), " << adcBits << "-bit " << adcMsps << " MS/s " << @@ -464,6 +466,7 @@ int Poll::write_data(word_t *data, unsigned int nWords){ } // Handle the writing of buffers to the file + //65552 = 8194 * 4 * 2 , 2 EOF buffers are need 8194 words at 4 bytes per word std::streampos current_filesize = output_file.GetFilesize(); if(current_filesize + (std::streampos)(4*nWords + 65552) > MAX_FILE_SIZE){ // Adding nWords plus 2 EOF buffers to the file will push it over MAX_FILE_SIZE. @@ -655,6 +658,7 @@ void Poll::help(){ std::cout << " toggle_bit - Toggle any bit of any parameter of 32 bits or less\n"; std::cout << " csr_test - Output the CSRA parameters for a given integer\n"; std::cout << " bit_test - Display active bits in a given integer up to 32 bits long\n"; + std::cout << " save [setFilename] - Writes the DSP Parameters to [setFileName] (default='active .set from pixie_cfg')\n"; std::cout << " get_traces [threshold] - Get traces for all channels in a specified module\n"; std::cout << " status - Display system status information\n"; std::cout << " thresh [threshold] - Modify or display the current polling threshold.\n"; @@ -665,6 +669,11 @@ void Poll::help(){ std::cout << " version (v) - Display Poll2 version information\n"; } +void Poll::save_help() { + std::cout << " Saves the DSP parameters to disk. Optionally, a file can be" + "provided, otherwise the file set file from pixie.cfg is used.\n"; +} + /* Print help dialogue for reading/writing pixie channel parameters. */ void Poll::pchan_help(){ std::cout << " Valid Pixie16 channel parameters:\n"; @@ -829,6 +838,31 @@ void Poll::get_traces(int mod_, int chan_, int thresh_/*=0*/){ delete[] module_data; } +/// This method splits the arguments for pread and pwrite on a colon delimeter. +/// This allows the user to proivde a range for the function for example, +/// \code pread 0 0:4 TRIGGER_THRESHOLD \code +/// will only read the TRIGGER_THRESHOLD for module 0, channels 0 to 4. +/// If the argument has no colons start and stop will be equal. +/// If the attmept is unsuccesful the mehtod returns false. +bool Poll::SplitParameterArgs(const std::string &arg, int &start, int &stop) { + //If a character is found that is nonnumeric or is not the delimeter we stop. + if (arg.find_first_not_of("-0123456789:") != std::string::npos) return false; + + size_t delimeterPos = arg.find(':'); + try { + start = std::stoi(arg.substr(0, delimeterPos)); + //If the delimeter was found we can seperate the stop otherwise set start = stop. + if (delimeterPos != std::string::npos) { + stop = std::stoi(arg.substr(delimeterPos + 1)); + if (start < 0 || stop < 0 || start > stop) return false; + } + else stop = start; + } + catch (const std::invalid_argument &ia) { + return false; + } + return true; +} /////////////////////////////////////////////////////////////////////////////// // Poll::CommandControl /////////////////////////////////////////////////////////////////////////////// @@ -846,10 +880,10 @@ void Poll::CommandControl(){ int select_dummy; if(server->Select(select_dummy)){ UDP_Packet pacman_command; - + // We have a pacman command. Retrieve the command server->RecvMessage((char *)&pacman_command, sizeof(UDP_Packet)); - + /* Valid poll commands 0x11 - INIT_ACQ 0x22 - START_ACQ @@ -1037,15 +1071,37 @@ void Poll::CommandControl(){ if(cmd == "pwrite"){ // Syntax "pwrite " if(p_args > 0 && arguments.at(0) == "help"){ pchan_help(); } else if(p_args >= 4){ - if(!IsNumeric(arguments.at(0), sys_message_head, "Invalid module specification")) continue; - else if(!IsNumeric(arguments.at(1), sys_message_head, "Invalid channel specification")) continue; - else if(!IsNumeric(arguments.at(3), sys_message_head, "Invalid parameter value specification")) continue; - int mod = atoi(arguments.at(0).c_str()); - int ch = atoi(arguments.at(1).c_str()); - double value = std::strtod(arguments.at(3).c_str(), NULL); + int modStart, modStop; + if (!SplitParameterArgs(arguments.at(0), modStart, modStop)) { + std::cout << "ERROR: Invalid module argument: '" << arguments.at(0) << "'\n"; + continue; + } + int chStart, chStop; + if (!SplitParameterArgs(arguments.at(1), chStart, chStop)) { + std::cout << "ERROR: Invalid channel argument: '" << arguments.at(1) << "'\n"; + continue; + } + + //Check that there are no characters in the string unless it is hex. + std::string &valueStr = arguments.at(3); + if (valueStr.find_last_not_of("+-eE0123456789.") != std::string::npos && + !((valueStr.find("0x") == 0 || valueStr.find("0X") == 0) && + valueStr.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos) ) { + std::cout << "ERROR: Invalid parameter value: '" << valueStr << "'\n"; + continue; + } + + double value; + try { value = std::stod(valueStr); } + catch (const std::invalid_argument &ia) { + std::cout << "ERROR: Invalid parameter value: '" << valueStr << "'\n"; + continue; + } ParameterChannelWriter writer; - if(forChannel(pif, mod, ch, writer, make_pair(arguments.at(2), value))){ pif->SaveDSPParameters(); } + if(forChannel(pif, modStart, modStop, chStart, chStop, writer, make_pair(arguments.at(2), value))){ + pif->SaveDSPParameters(); + } } else{ std::cout << sys_message_head << "Invalid number of parameters to pwrite\n"; @@ -1055,13 +1111,34 @@ void Poll::CommandControl(){ else if(cmd == "pmwrite"){ // Syntax "pmwrite " if(p_args > 0 && arguments.at(0) == "help"){ pmod_help(); } else if(p_args >= 3){ - if(!IsNumeric(arguments.at(0), sys_message_head, "Invalid module specification")) continue; - else if(!IsNumeric(arguments.at(2), sys_message_head, "Invalid parameter value specification")) continue; - int mod = atoi(arguments.at(0).c_str()); - unsigned int value = (unsigned int)std::strtoul(arguments.at(2).c_str(), NULL, 0); - + int modStart, modStop; + if (!SplitParameterArgs(arguments.at(0), modStart, modStop)) { + std::cout << "ERROR: Invalid module argument: '" << arguments.at(0) << "'\n"; + continue; + } + + //Check that there are no characters in the string unless it is hex. + std::string &valueStr = arguments.at(2); + if (valueStr.find_last_not_of("0123456789") != std::string::npos && + !((valueStr.find("0x") == 0 || valueStr.find("0X") == 0) && + valueStr.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos) ) { + std::cout << "ERROR: Invalid parameter value: '" << valueStr << "'\n"; + continue; + } + + unsigned int value; + //Use stod to add hex capability. The decimal and negative values are + // caught above and rejected. + try { value = (unsigned int) std::stod(valueStr); } + catch (const std::invalid_argument &ia) { + std::cout << "ERROR: Invalid parameter value: '" << valueStr << "'\n"; + continue; + } + ParameterModuleWriter writer; - if(forModule(pif, mod, writer, make_pair(arguments.at(1), value))){ pif->SaveDSPParameters(); } + if(!forModule(pif, modStart, modStop, writer, make_pair(arguments.at(1), value))){ + pif->SaveDSPParameters(); + } } else{ std::cout << sys_message_head << "Invalid number of parameters to pmwrite\n"; @@ -1069,22 +1146,49 @@ void Poll::CommandControl(){ } } } + else if (cmd == "save") { + if(acq_running || do_MCA_run){ + std::cout << sys_message_head << "Warning! Cannot view pixie parameters while acquisition is running\n\n"; + continue; + } + if(p_args > 0 && arguments.at(0) == "help"){ + save_help(); + continue; + } + if(p_args == 0) { + pif->SaveDSPParameters(); + } + else if (p_args == 1) { + pif->SaveDSPParameters(arguments.at(0).c_str()); + } + else { + std::cout << sys_message_head << "Invalid number of parameters to save\n"; + std::cout << sys_message_head << " -SYNTAX- save [setFilename]\n"; + continue; + } + } else if(cmd == "pread" || cmd == "pmread"){ // Read pixie parameters if(acq_running || do_MCA_run){ std::cout << sys_message_head << "Warning! Cannot view pixie parameters while acquisition is running\n\n"; continue; } - + if(cmd == "pread"){ // Syntax "pread " if(p_args > 0 && arguments.at(0) == "help"){ pchan_help(); } else if(p_args >= 3){ - if(!IsNumeric(arguments.at(0), sys_message_head, "Invalid module specification")) continue; - else if(!IsNumeric(arguments.at(1), sys_message_head, "Invalid channel specification")) continue; - int mod = atoi(arguments.at(0).c_str()); - int ch = atoi(arguments.at(1).c_str()); - + int modStart, modStop; + if (!SplitParameterArgs(arguments.at(0), modStart, modStop)) { + std::cout << "ERROR: Invalid module argument: '" << arguments.at(0) << "'\n"; + continue; + } + int chStart, chStop; + if (!SplitParameterArgs(arguments.at(1), chStart, chStop)) { + std::cout << "ERROR: Invalid channel argument: '" << arguments.at(1) << "'\n"; + continue; + } + ParameterChannelReader reader; - forChannel(pif, mod, ch, reader, arguments.at(2)); + forChannel(pif, modStart, modStop, chStart, chStop, reader, arguments.at(2)); } else{ std::cout << sys_message_head << "Invalid number of parameters to pread\n"; @@ -1094,11 +1198,14 @@ void Poll::CommandControl(){ else if(cmd == "pmread"){ // Syntax "pmread " if(p_args > 0 && arguments.at(0) == "help"){ pmod_help(); } else if(p_args >= 2){ - if(!IsNumeric(arguments.at(0), sys_message_head, "Invalid module specification")) continue; - int mod = atoi(arguments.at(0).c_str()); + int modStart, modStop; + if (!SplitParameterArgs(arguments.at(0), modStart, modStop)) { + std::cout << "ERROR: Invalid module argument: '" << arguments.at(0) << "'\n"; + continue; + } ParameterModuleReader reader; - forModule(pif, mod, reader, arguments.at(1)); + forModule(pif, modStart, modStop, reader, arguments.at(1)); } else{ std::cout << sys_message_head << "Invalid number of parameters to pmread\n"; @@ -1113,11 +1220,16 @@ void Poll::CommandControl(){ } if(p_args >= 1){ - if(!IsNumeric(arguments.at(0), sys_message_head, "Invalid module specification")) continue; - int mod = atoi(arguments.at(0).c_str()); - + int modStart, modStop; + if (!SplitParameterArgs(arguments.at(0), modStart, modStop)) { + std::cout << "ERROR: Invalid module argument: '" << arguments.at(0) << "'\n"; + continue; + } + OffsetAdjuster adjuster; - if(forModule(pif, mod, adjuster, 0)){ pif->SaveDSPParameters(); } + if(!forModule(pif, modStart, modStop, adjuster, 0)) { + pif->SaveDSPParameters(); + } } else{ std::cout << sys_message_head << "Invalid number of parameters to adjust_offsets\n"; @@ -1153,14 +1265,24 @@ void Poll::CommandControl(){ BitFlipper flipper; if(p_args >= 3){ - if(!IsNumeric(arguments.at(0), sys_message_head, "Invalid module specification")) continue; - else if(!IsNumeric(arguments.at(1), sys_message_head, "Invalid channel specification")) continue; + int modStart, modStop; + if (!SplitParameterArgs(arguments.at(0), modStart, modStop)) { + std::cout << "ERROR: Invalid module argument: '" << arguments.at(0) << "'\n"; + continue; + } + int chStart, chStop; + if (!SplitParameterArgs(arguments.at(1), chStart, chStop)) { + std::cout << "ERROR: Invalid channel argument: '" << arguments.at(1) << "'\n"; + continue; + } flipper.SetCSRAbit(arguments.at(2)); std::string dum_str = "CHANNEL_CSRA"; - if(forChannel(pif, atoi(arguments.at(0).c_str()), atoi(arguments.at(1).c_str()), flipper, dum_str)){ - pif->SaveDSPParameters(); + bool error = false; + if(!forChannel(pif, modStart, modStop, chStart, chStop, flipper, dum_str)){ + error = true; } + if (!error) pif->SaveDSPParameters(); } else{ std::cout << sys_message_head << "Invalid number of parameters to toggle\n"; @@ -1194,8 +1316,24 @@ void Poll::CommandControl(){ else if(cmd == "csr_test"){ // Run CSRAtest method BitFlipper flipper; if(p_args >= 1){ - if(!IsNumeric(arguments.at(0), sys_message_head, "Invalid CSRA value specification")) continue; - flipper.CSRAtest((unsigned int)atoi(arguments.at(0).c_str())); + //Check that there are no characters in the string unless it is hex. + std::string &valueStr = arguments.at(0); + if (valueStr.find_last_not_of("0123456789") != std::string::npos && + !((valueStr.find("0x") == 0 || valueStr.find("0X") == 0) && + valueStr.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos) ) { + std::cout << "ERROR: Invalid parameter value: '" << valueStr << "'\n"; + continue; + } + unsigned int value; + //Use stod to add hex capability. The decimal and negative values are + // caught above and rejected. + try { value = (unsigned int) std::stod(valueStr); } + catch (const std::invalid_argument &ia) { + std::cout << "ERROR: Invalid parameter value: '" << valueStr << "'\n"; + continue; + } + + flipper.CSRAtest(value); } else{ std::cout << sys_message_head << "Invalid number of parameters to csr_test\n"; @@ -1485,7 +1623,7 @@ void Poll::RunControl(){ else{ std::cout << sys_message_head << "Attempting PIXIE crate reboot\n"; pif->Boot(PixieInterface::BootAll); - printf("Press any key to continue..."); + printf("Press Enter key to continue..."); std::cin.get(); do_reboot = false; } @@ -1580,7 +1718,7 @@ void Poll::RunControl(){ //Handle a stop signal if(do_stop_acq){ // Read data from the modules. - ReadFIFO(); + if (!had_error) ReadFIFO(); // Instruct all modules to end the current run. pif->EndRun(); @@ -1596,7 +1734,7 @@ void Poll::RunControl(){ //We sleep to allow the module to finish. sleep(1); //We read the FIFO out. - ReadFIFO(); + if (!had_error) ReadFIFO(); } //Print the module status. @@ -1778,15 +1916,15 @@ bool Poll::ReadFIFO() { std::cout << " to buffer position " << dataWords << std::endl; } - //After reading the FIFO and printing a sttus message we can update the number of words to include the partial event. + //After reading the FIFO and printing a status message we can update the number of words to include the partial event. nWords[mod] += partialEvents[mod].size(); //Clear the partial event partialEvents[mod].clear(); - //We now ned to parse the event to determine if there is a hanging event. Also, allows a check for corrupted data. - size_t parseWords = dataWords; + //We now need to parse the event to determine if there is a hanging event. Also, allows a check for corrupted data. + size_t parseWords = dataWords; //We declare the eventSize outside the loop in case there is a partial event. - word_t eventSize = 0; + word_t eventSize = 0, prevEventSize = 0; word_t slotExpected = pif->GetSlotNumber(mod); while (parseWords < dataWords + nWords[mod]) { //Check first word to see if data makes sense. @@ -1797,19 +1935,20 @@ bool Poll::ReadFIFO() { bool virtualChannel = ((fifoData[parseWords] & 0x20000000) != 0); if( slotRead != slotExpected ){ - std::cout << Display::ErrorStr() << " Slot read (" << slotRead - << ") not the same as" << " slot expected (" - << slotExpected << ")" << std::endl; - break; + std::cout << Display::ErrorStr() << " Slot read " << slotRead + << " not the same as slot expected " + << slotExpected << std::endl; + had_error = true; } - else if (chanRead < 0 || chanRead > 15) { + if (chanRead < 0 || chanRead > 15) { std::cout << Display::ErrorStr() << " Channel read (" << chanRead << ") not valid!\n"; - break; + had_error = true; } - else if(eventSize == 0){ - std::cout << Display::ErrorStr() << "ZERO EVENT SIZE in mod " << mod << "!\n"; - break; + if(eventSize == 0){ + std::cout << Display::ErrorStr() << " ZERO EVENT SIZE in mod " << mod << "!\n"; + had_error = true; } + if (had_error) break; // Update the statsHandler with the event (for monitor.bash) if(!virtualChannel && statsHandler){ @@ -1818,6 +1957,7 @@ bool Poll::ReadFIFO() { //Iterate to the next event and continue parsing parseWords += eventSize; + prevEventSize = eventSize; } //We now check the outcome of the data parsing. @@ -1833,26 +1973,62 @@ bool Poll::ReadFIFO() { //Update the number of words to indicate removal or partial event. nWords[mod] -= partialSize; - } //If parseWords is small then the parse failed for some reason else if (parseWords < dataWords + nWords[mod]) { - std::cout << Display::ErrorStr() << " Parsing indicated corrupted data at " << parseWords - dataWords << " words into FIFO.\n"; + //Determine the fifo position from successfully parsed words plus the last event length. + std::cout << Display::ErrorStr() << " Parsing indicated corrupted data for module " << mod << ".\n"; + std::cout << "| Parsing failed at " << parseWords - dataWords << "/" << nWords[mod] + << " (" << parseWords << "/" << dataWords + nWords[mod] << ") words into FIFO." << std::endl; + + //Print the previous event + std::cout << "|\n| Event prior to parsing error (" << prevEventSize << " words):"; + std::cout << std::hex << std::setfill('1'); + for(size_t i=0;i< prevEventSize;i++) { + if (i%5 == 0) std::cout << std::endl << "| "; + std::cout << "0x" << std::right << std::setw(8) << std::setfill('0'); + std::cout << fifoData[parseWords - prevEventSize + i] << " "; + } + std::cout << std::dec << std::setfill(' ') << std::endl; + + //Print the parsed event + std::cout << "|\n| Event at parsing error (" << eventSize << " words):"; + size_t outputSize = eventSize; + if (eventSize > 50) { + outputSize = 50; + std::cout << "\n| (Truncated at " << outputSize << " words.)"; + } + std::cout << std::hex << std::setfill('0'); + for(size_t i=0;i< outputSize;i++) { + if (i%5 == 0) std::cout << std::endl << "| "; + std::cout << "0x" << std::right << std::setw(8) << std::setfill('0'); + std::cout << fifoData[parseWords + i] << " "; + } + std::cout << std::dec << std::setfill(' ') << std::endl; - std::cout << std::hex; - //Print the previous words - std::cout << "Words prior to parsing error:\n"; - for(int i=0;i< 100;i++) { - if (i%10 == 0) std::cout << std::endl << "\t"; - std::cout << fifoData[dataWords + parseWords - 100 + i] << " "; + //Print the following event + //Determine size of following event. + word_t nextEventSize = 0; + if (parseWords + eventSize < dataWords + nWords[mod]) { + nextEventSize = ((fifoData[parseWords + eventSize] & 0x7FFE2000) >> 17); } - //Print the following words - std::cout << "Words following parsing error:\n"; - for(int i=0;i< 100;i++) { - if (i%10 == 0) std::cout << std::endl << "\t"; - std::cout << fifoData[dataWords + parseWords + i] << " "; + std::cout << "|\n| Event after parsing error (" << nextEventSize << " words):"; + + //Determine output size for event. + outputSize = nextEventSize; + if (eventSize > 50) outputSize = 50; + if (parseWords + eventSize + outputSize >= dataWords + nWords[mod]) + outputSize = dataWords + nWords[mod] - (parseWords + eventSize); + if (outputSize != nextEventSize) + std::cout << "\n| (Truncated at " << outputSize << " words.)"; + + std::cout << std::hex << std::setfill('0'); + for(size_t i=0;i< outputSize;i++) { + if (i%5 == 0) std::cout << std::endl << "| "; + std::cout << "0x" << std::right << std::setw(8); + std::cout << fifoData[parseWords + eventSize + i] << " "; } - std::cout << std::dec << std::endl; + std::cout << std::dec << std::setfill(' ') << std::endl << "|\n"; do_stop_acq = true; had_error = true; @@ -1916,3 +2092,4 @@ std::string yesno(bool value_){ if(value_){ return "Yes"; } return "No"; } + diff --git a/Poll/source/poll2_stats.cpp b/Acquisition/Poll/source/poll2_stats.cpp similarity index 100% rename from Poll/source/poll2_stats.cpp rename to Acquisition/Poll/source/poll2_stats.cpp diff --git a/PxiDump/CMakeLists.txt b/Acquisition/PxiDump/CMakeLists.txt similarity index 100% rename from PxiDump/CMakeLists.txt rename to Acquisition/PxiDump/CMakeLists.txt diff --git a/PxiDump/include/set2root.hpp b/Acquisition/PxiDump/include/set2root.hpp similarity index 100% rename from PxiDump/include/set2root.hpp rename to Acquisition/PxiDump/include/set2root.hpp diff --git a/PxiDump/source/CMakeLists.txt b/Acquisition/PxiDump/source/CMakeLists.txt similarity index 90% rename from PxiDump/source/CMakeLists.txt rename to Acquisition/PxiDump/source/CMakeLists.txt index a9f7af88c..f81cee9fc 100644 --- a/PxiDump/source/CMakeLists.txt +++ b/Acquisition/PxiDump/source/CMakeLists.txt @@ -4,9 +4,9 @@ add_executable(set2ascii ${SET2ROOT_SOURCES}) target_link_libraries(set2ascii) install(TARGETS set2ascii DESTINATION bin) -if(USE_ROOT) +if(PAASS_USE_ROOT) add_executable(set2root ${SET2ROOT_SOURCES}) set_target_properties(set2root PROPERTIES COMPILE_FLAGS "-DUSE_ROOT_OUTPUT") target_link_libraries(set2root ${ROOT_LIBRARIES}) install(TARGETS set2root DESTINATION bin) -endif() +endif(PAASS_USE_ROOT) diff --git a/PxiDump/source/set2root.cpp b/Acquisition/PxiDump/source/set2root.cpp similarity index 97% rename from PxiDump/source/set2root.cpp rename to Acquisition/PxiDump/source/set2root.cpp index ce5246a16..27e99d676 100644 --- a/PxiDump/source/set2root.cpp +++ b/Acquisition/PxiDump/source/set2root.cpp @@ -17,6 +17,7 @@ #include "TFile.h" #endif +#include "HelperFunctions.hpp" #include "set2root.hpp" #define FILTER_CLOCK 8E-3 // Filter clock (in us) @@ -68,7 +69,12 @@ std::string parameter::print(){ stream << name << "["; if(count < 10) stream << "0"; - stream << count << "]" << "\t" << (*iter) << "\n"; + stream << count << "]" << "\t"; + if(name != "PreampTau") + stream << (*iter); + else + stream << IeeeStandards::IeeeFloatingToDecimal((*iter)); + stream << "\n"; count++; } } diff --git a/Scan/util/CMakeLists.txt b/Acquisition/Setup/CMakeLists.txt similarity index 68% rename from Scan/util/CMakeLists.txt rename to Acquisition/Setup/CMakeLists.txt index d9cffdf0b..7f7855c04 100644 --- a/Scan/util/CMakeLists.txt +++ b/Acquisition/Setup/CMakeLists.txt @@ -1,2 +1,3 @@ include_directories(include) add_subdirectory(source) +add_subdirectory(Traces) diff --git a/Setup/Traces/CMakeLists.txt b/Acquisition/Setup/Traces/CMakeLists.txt similarity index 100% rename from Setup/Traces/CMakeLists.txt rename to Acquisition/Setup/Traces/CMakeLists.txt diff --git a/Setup/Traces/share/traces/plotTraces_ch b/Acquisition/Setup/Traces/share/traces/plotTraces_ch similarity index 100% rename from Setup/Traces/share/traces/plotTraces_ch rename to Acquisition/Setup/Traces/share/traces/plotTraces_ch diff --git a/Setup/Traces/share/traces/plotTraces_mod b/Acquisition/Setup/Traces/share/traces/plotTraces_mod similarity index 100% rename from Setup/Traces/share/traces/plotTraces_mod rename to Acquisition/Setup/Traces/share/traces/plotTraces_mod diff --git a/Setup/Traces/share/traces/tra b/Acquisition/Setup/Traces/share/traces/tra similarity index 100% rename from Setup/Traces/share/traces/tra rename to Acquisition/Setup/Traces/share/traces/tra diff --git a/Setup/Traces/source/viewBaseline.sh b/Acquisition/Setup/Traces/source/viewBaseline.sh similarity index 100% rename from Setup/Traces/source/viewBaseline.sh rename to Acquisition/Setup/Traces/source/viewBaseline.sh diff --git a/Setup/include/utilities.h b/Acquisition/Setup/include/utilities.h similarity index 100% rename from Setup/include/utilities.h rename to Acquisition/Setup/include/utilities.h diff --git a/Setup/source/CMakeLists.txt b/Acquisition/Setup/source/CMakeLists.txt similarity index 94% rename from Setup/source/CMakeLists.txt rename to Acquisition/Setup/source/CMakeLists.txt index ac21a9488..8adfffb0c 100644 --- a/Setup/source/CMakeLists.txt +++ b/Acquisition/Setup/source/CMakeLists.txt @@ -10,9 +10,9 @@ endforeach(UTIL) install(TARGETS ${SETUP_UTILS} DESTINATION bin) -if(${USE_ROOT}) +if(PAASS_USE_ROOT) add_executable(paramScan paramScan.cpp) target_link_libraries(paramScan PixieInterface MCA_LIBRARY ${ROOT_LIBRARIES} "-lSpectrum") install(TARGETS paramScan DESTINATION bin) -endif() +endif(PAASS_USE_ROOT) diff --git a/Setup/source/adjust_offsets.cpp b/Acquisition/Setup/source/adjust_offsets.cpp similarity index 100% rename from Setup/source/adjust_offsets.cpp rename to Acquisition/Setup/source/adjust_offsets.cpp diff --git a/Setup/source/boot.cpp b/Acquisition/Setup/source/boot.cpp similarity index 100% rename from Setup/source/boot.cpp rename to Acquisition/Setup/source/boot.cpp diff --git a/Setup/source/copy_params.cpp b/Acquisition/Setup/source/copy_params.cpp similarity index 100% rename from Setup/source/copy_params.cpp rename to Acquisition/Setup/source/copy_params.cpp diff --git a/Setup/source/csr_test.cpp b/Acquisition/Setup/source/csr_test.cpp similarity index 100% rename from Setup/source/csr_test.cpp rename to Acquisition/Setup/source/csr_test.cpp diff --git a/Setup/source/find_tau.cpp b/Acquisition/Setup/source/find_tau.cpp similarity index 100% rename from Setup/source/find_tau.cpp rename to Acquisition/Setup/source/find_tau.cpp diff --git a/Setup/source/get_traces.cpp b/Acquisition/Setup/source/get_traces.cpp similarity index 100% rename from Setup/source/get_traces.cpp rename to Acquisition/Setup/source/get_traces.cpp diff --git a/Setup/source/input_whole_crate.txt b/Acquisition/Setup/source/input_whole_crate.txt similarity index 100% rename from Setup/source/input_whole_crate.txt rename to Acquisition/Setup/source/input_whole_crate.txt diff --git a/Setup/source/mca_paw.cpp b/Acquisition/Setup/source/mca_paw.cpp similarity index 100% rename from Setup/source/mca_paw.cpp rename to Acquisition/Setup/source/mca_paw.cpp diff --git a/Setup/source/paramScan.cpp b/Acquisition/Setup/source/paramScan.cpp similarity index 100% rename from Setup/source/paramScan.cpp rename to Acquisition/Setup/source/paramScan.cpp diff --git a/Setup/source/pmread.cpp b/Acquisition/Setup/source/pmread.cpp similarity index 100% rename from Setup/source/pmread.cpp rename to Acquisition/Setup/source/pmread.cpp diff --git a/Setup/source/pmwrite.cpp b/Acquisition/Setup/source/pmwrite.cpp similarity index 100% rename from Setup/source/pmwrite.cpp rename to Acquisition/Setup/source/pmwrite.cpp diff --git a/Setup/source/pread.cpp b/Acquisition/Setup/source/pread.cpp similarity index 100% rename from Setup/source/pread.cpp rename to Acquisition/Setup/source/pread.cpp diff --git a/Setup/source/pwrite.cpp b/Acquisition/Setup/source/pwrite.cpp similarity index 100% rename from Setup/source/pwrite.cpp rename to Acquisition/Setup/source/pwrite.cpp diff --git a/Setup/source/rate.cpp b/Acquisition/Setup/source/rate.cpp similarity index 100% rename from Setup/source/rate.cpp rename to Acquisition/Setup/source/rate.cpp diff --git a/Setup/source/set_hybrid.cpp b/Acquisition/Setup/source/set_hybrid.cpp similarity index 100% rename from Setup/source/set_hybrid.cpp rename to Acquisition/Setup/source/set_hybrid.cpp diff --git a/Setup/source/set_pileups_only.cpp b/Acquisition/Setup/source/set_pileups_only.cpp similarity index 100% rename from Setup/source/set_pileups_only.cpp rename to Acquisition/Setup/source/set_pileups_only.cpp diff --git a/Setup/source/set_pileups_reject.cpp b/Acquisition/Setup/source/set_pileups_reject.cpp similarity index 100% rename from Setup/source/set_pileups_reject.cpp rename to Acquisition/Setup/source/set_pileups_reject.cpp diff --git a/Setup/source/set_standard.cpp b/Acquisition/Setup/source/set_standard.cpp similarity index 100% rename from Setup/source/set_standard.cpp rename to Acquisition/Setup/source/set_standard.cpp diff --git a/Setup/source/toggle.cpp b/Acquisition/Setup/source/toggle.cpp similarity index 100% rename from Setup/source/toggle.cpp rename to Acquisition/Setup/source/toggle.cpp diff --git a/Setup/source/toggle_catcher.cpp b/Acquisition/Setup/source/toggle_catcher.cpp similarity index 100% rename from Setup/source/toggle_catcher.cpp rename to Acquisition/Setup/source/toggle_catcher.cpp diff --git a/Setup/source/toggle_gain.cpp b/Acquisition/Setup/source/toggle_gain.cpp similarity index 100% rename from Setup/source/toggle_gain.cpp rename to Acquisition/Setup/source/toggle_gain.cpp diff --git a/Setup/source/toggle_good.cpp b/Acquisition/Setup/source/toggle_good.cpp similarity index 100% rename from Setup/source/toggle_good.cpp rename to Acquisition/Setup/source/toggle_good.cpp diff --git a/Setup/source/toggle_pileup.cpp b/Acquisition/Setup/source/toggle_pileup.cpp similarity index 100% rename from Setup/source/toggle_pileup.cpp rename to Acquisition/Setup/source/toggle_pileup.cpp diff --git a/Setup/source/toggle_polarity.cpp b/Acquisition/Setup/source/toggle_polarity.cpp similarity index 100% rename from Setup/source/toggle_polarity.cpp rename to Acquisition/Setup/source/toggle_polarity.cpp diff --git a/Setup/source/toggle_trace.cpp b/Acquisition/Setup/source/toggle_trace.cpp similarity index 100% rename from Setup/source/toggle_trace.cpp rename to Acquisition/Setup/source/toggle_trace.cpp diff --git a/Setup/source/trace.cpp b/Acquisition/Setup/source/trace.cpp similarity index 100% rename from Setup/source/trace.cpp rename to Acquisition/Setup/source/trace.cpp diff --git a/Setup/source/trace_paw.cpp b/Acquisition/Setup/source/trace_paw.cpp similarity index 100% rename from Setup/source/trace_paw.cpp rename to Acquisition/Setup/source/trace_paw.cpp diff --git a/Analysis/CMakeLists.txt b/Analysis/CMakeLists.txt new file mode 100644 index 000000000..25c8c2b9a --- /dev/null +++ b/Analysis/CMakeLists.txt @@ -0,0 +1,37 @@ +option(PAASS_USE_HRIBF "Use HRIBF library for scan base." OFF) +CMAKE_DEPENDENT_OPTION(PAASS_USE_GSL "Compile with GSL" ON + "PAASS_BUILD_UTKSCAN" OFF) +mark_as_advanced(PAASS_USE_GSL) + +#Check if GSL is installed +if(PAASS_USE_GSL) + find_package(GSL REQUIRED) + add_definitions("-D usegsl") +endif(PAASS_USE_GSL) + +#Everything below is dependent on these two sets of libaries so we include the +#headers. +include_directories(Resources/include) +include_directories(ScanLibraries/include) + +if(PAASS_USE_HRIBF) + #Find HRIBF Libraries + find_package(HRIBF REQUIRED) + add_definitions("-D USE_HRIBF") + + #If we are using HRIBF interface we need to include the ScanorInterface header + # for the following code. + include_directories(Scanor/include) + add_subdirectory(Scanor) +endif(PAASS_USE_HRIBF) + +#We will always build these two since they include static lib for the rest +add_subdirectory(ScanLibraries) +add_subdirectory(Resources) + +#Build utilities. +add_subdirectory(Utilities) + +if(PAASS_BUILD_UTKSCAN) + add_subdirectory(Utkscan) +endif(PAASS_BUILD_UTKSCAN) \ No newline at end of file diff --git a/Analysis/Resources/CMakeLists.txt b/Analysis/Resources/CMakeLists.txt new file mode 100644 index 000000000..c40d096b0 --- /dev/null +++ b/Analysis/Resources/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(source) + +if(PAASS_BUILD_TESTS OR PAASS_BUILD_UNITTESTS) + add_subdirectory(tests) +endif(PAASS_BUILD_TESTS OR PAASS_BUILD_UNITTESTS) \ No newline at end of file diff --git a/Analysis/Resources/include/GslFitter.hpp b/Analysis/Resources/include/GslFitter.hpp new file mode 100644 index 000000000..48a43c4f8 --- /dev/null +++ b/Analysis/Resources/include/GslFitter.hpp @@ -0,0 +1,67 @@ +/// @file GslFitter.hpp +/// @brief Implementation of the GSL fitting routine for GSL v2+ +/// @author S. V. Paulauskas +/// @date August 8, 2016 +#ifndef PIXIESUITE_GSLFITTER_HPP +#define PIXIESUITE_GSLFITTER_HPP + +#include + +#include + +#include +#include +#include + +#include "TimingDriver.hpp" + +class GslFitter : public TimingDriver { +public: + ///Default Constructor + GslFitter() : TimingDriver() { isFastSiPm_ = false; } + + ///Default Destructor + ~GslFitter() {} + + /// @return the amplitude from the GSL fit + double GetAmplitude(void) { return amp_; } + + /// @return the chi^2 from the GSL fit + double GetChiSq(void) { return chi_ * chi_; } + + /// @return the chi^2dof from the GSL fit + double GetChiSqPerDof(void) { return GetChiSq() / dof_; } + + ///The ever important phase calculation + /// @param[in] data The baseline subtracted data for the fitting + /// @param[in] pars The parameters for the fit + /// @param[in] max : Information about the maximum position and value + /// @param[in] baseline : The average and standard deviation of the baseline + double CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &max, + const std::pair baseline); + + ///Sets the isFastSiPm_ flag + ///@param[in] a : The value that we are going to set + void SetIsFastSiPm(const bool &a) { isFastSiPm_ = a; } + + /// @brief Structure necessary for the GSL fitting routines + struct FitData { + size_t n;//!< size of the fitting parameters + double *y;//!< ydata to fit + double *sigma;//!< weights used for the fit + double beta; //!< the beta parameter for the fit + double gamma; //!< the gamma parameter for the fit + double qdc;//!< the QDC for the fit + }; +private: + bool isFastSiPm_; + + double amp_; + double chi_; + double dof_; +}; + + +#endif //PIXIESUITE_GSLFITTER_HPP diff --git a/Analysis/Resources/include/PolynomialCfd.hpp b/Analysis/Resources/include/PolynomialCfd.hpp new file mode 100644 index 000000000..661b799ab --- /dev/null +++ b/Analysis/Resources/include/PolynomialCfd.hpp @@ -0,0 +1,23 @@ +/// @file PolynomialCfd.hpp +/// @brief A method that uses the +/// @author C. R. Thornsberry and S. V. Paulauskas +/// @date December 6, 2016 +#ifndef PIXIESUITE_POLYNOMIALCFD_HPP +#define PIXIESUITE_POLYNOMIALCFD_HPP + +#include "TimingDriver.hpp" + +class PolynomialCfd : public TimingDriver { +public: + PolynomialCfd() {}; + + ~PolynomialCfd() {}; + + /// Perform CFD analysis on the waveform using the pol2 algorithm. + double CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &max, + const std::pair baseline); +}; + +#endif //PIXIESUITE_POLYNOMIALCFD_HPP diff --git a/Analysis/Resources/include/RootFitter.hpp b/Analysis/Resources/include/RootFitter.hpp new file mode 100644 index 000000000..1705bb2ff --- /dev/null +++ b/Analysis/Resources/include/RootFitter.hpp @@ -0,0 +1,31 @@ +/// @file RootFitter.hpp +/// @brief Class to handle fitting traces using ROOT +/// @author S. V. Paulauskas +/// @date December 18, 2016 +#ifndef _PIXIESUITE_ROOTFITTER_HPP_ +#define _PIXIESUITE_ROOTFITTER_HPP_ + +#include "TimingDriver.hpp" + +class TF1; +class VandleTimingFunction; + +class RootFitter : public TimingDriver { +public: + RootFitter(); + + ~RootFitter(); + + /// Perform fitting analysis using ROOT + double CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &maxInfo, + std::pair baseline); + +private: + TF1 *func_; + VandleTimingFunction *vandleTimingFunction_; +}; + + +#endif //#ifndef _PIXIESUITE_ROOTFITTER_HPP_ diff --git a/Analysis/Resources/include/TimingDriver.hpp b/Analysis/Resources/include/TimingDriver.hpp new file mode 100644 index 000000000..2d5396776 --- /dev/null +++ b/Analysis/Resources/include/TimingDriver.hpp @@ -0,0 +1,69 @@ +/// \file TimingDriver.hpp +/// \brief An abstract class that will provide the base for fitting +/// \author S. V. Paulauskas +/// \date August 8, 2016 + +#ifndef PIXIESUITE_TIMINGDRIVER_HPP +#define PIXIESUITE_TIMINGDRIVER_HPP + +#include +#include + +/// An abstract class that will be used to handle timing. +class TimingDriver { +public: + ///Default Constructor + TimingDriver() {}; + + ///Default destructor + virtual ~TimingDriver() {}; + + ///This virtual function provides results other than the phase to the + /// user. Please look at the documentation of the children to see exactly + /// what is returned with this vector. + ///@TODO Not sure this is the best way to do things, but it cut the + /// number of methods necessary by a factor of 5 or 6. + ///@return A vector containing useful information calculated in addition + /// to the phase. + virtual std::vector GetResults(void) { return results_; } + + ///This is a virtual function that actually deifnes how we are going to + /// determine the phase. We have several different implementations of how + /// we can do this but we'll overload this method in the children to + /// provide specific implementation. + ///@param[in] data : The vector of data that we are going to work with. + /// This usually means a trace or waveform. + ///@param[in] pars : The pair of parameters that we want to use for the + /// algorithm. For Fitters this will be beta and gamma, for CFDs this + /// will be the fraction and the delay. + /// @param[in] maxInfo : The information about the maximum in a pair + /// of NOTE : The value of the maximum for CFD based + /// calculations should be the extrapolated maximum. + /// @param[in] a : The baseline information in a pair + ///@return The phase calculated by the algorithm. + virtual double CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &max, + const std::pair baseline) { + return 0.0; + } + + ///@Brief Overload of the Calculate phase method to allow for data + /// vectors of type double. We do this since we cannot template a virtual + /// method. + virtual double CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &max, + const std::pair baseline) { + return 0.0; + } + + /// Sets the QDC that we want to set + /// \param[in] a the qdc of the waveform for the fit + void SetQdc(const double &a) { qdc_ = a; } +protected: + std::vector results_; //!< Vector containing results + double qdc_;//!< qdc of the waveform being fitted +}; + +#endif //PIXIESUITE_TIMINGDRIVER_HPP diff --git a/Analysis/Resources/include/TraditionalCfd.hpp b/Analysis/Resources/include/TraditionalCfd.hpp new file mode 100644 index 000000000..d266c3241 --- /dev/null +++ b/Analysis/Resources/include/TraditionalCfd.hpp @@ -0,0 +1,24 @@ +///@file TraditionalCfd.hpp +///@brief Traditional CFD implemented digitally +///@author S. V. Paulauskas +///@date July 22, 2011 + +#ifndef PIXIESUITE_TRADITIONALCFD_HPP +#define PIXIESUITE_TRADITIONALCFD_HPP + +#include "TimingDriver.hpp" + +class TraditionalCfd : public TimingDriver { +public: + /// Default constructor + TraditionalCfd() {}; + + /// Default destructor + ~TraditionalCfd() {}; + + double CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &max, + const std::pair baseline); +}; +#endif //PIXIESUITE_TRADITIONALCFD_HPP diff --git a/Analysis/Resources/include/VandleTimingFunction.hpp b/Analysis/Resources/include/VandleTimingFunction.hpp new file mode 100644 index 000000000..7d7f68984 --- /dev/null +++ b/Analysis/Resources/include/VandleTimingFunction.hpp @@ -0,0 +1,17 @@ +/// @file VandleTimingFunction.hpp +/// @brief A class to handle the processing of traces +/// @author S. V. Paulauskas +/// @date October 3, 2014 +#ifndef __VANDLETIMINGFUNCITON__HPP__ +#define __VANDLETIMINGFUNCITON__HPP__ + +class VandleTimingFunction { +public: + VandleTimingFunction() {}; + + virtual ~VandleTimingFunction() {}; + + double operator()(double *x, double *p); +}; + +#endif diff --git a/Analysis/Resources/include/XiaCfd.hpp b/Analysis/Resources/include/XiaCfd.hpp new file mode 100644 index 000000000..7473a143f --- /dev/null +++ b/Analysis/Resources/include/XiaCfd.hpp @@ -0,0 +1,21 @@ +// +// Created by vincent on 12/6/16. +// + +#ifndef PIXIESUITE_XIACFD_HPP +#define PIXIESUITE_XIACFD_HPP + + +class XiaCfd : public TimingDriver { +public: + XiaCfd() {}; + + ~XiaCfd() {}; + + /// Perform CFD analysis on the waveform using the XIA algorithm. + double CalculatePhase(const double &F_ = 0.5, const size_t &D_ = 1, + const size_t &L_ = 1); +}; + + +#endif //PIXIESUITE_XIACFD_HPP diff --git a/Analysis/Resources/source/CMakeLists.txt b/Analysis/Resources/source/CMakeLists.txt new file mode 100644 index 000000000..c013548a0 --- /dev/null +++ b/Analysis/Resources/source/CMakeLists.txt @@ -0,0 +1,33 @@ +#Set the utility sources that we will make a lib out of +set(ResourceSources PolynomialCfd.cpp TraditionalCfd.cpp) + +if (PAASS_USE_GSL) + if (${GSL_VERSION} GREATER 1.9) + list(APPEND ResourceSources Gsl2Fitter.cpp) + else (${GSL_VERSION} LESS 2.0) + list(APPEND ResourceSources Gsl1Fitter.cpp) + endif (${GSL_VERSION} GREATER 1.9) +endif (PAASS_USE_GSL) + +if (PAASS_USE_ROOT) + list(APPEND ResourceSources RootFitter.cpp VandleTimingFunction.cpp) +endif (PAASS_USE_ROOT) + +#Add the sources to the library +add_library(ResourceObjects OBJECT ${ResourceSources}) + +if (BUILD_SHARED_LIBS) + message(STATUS "Building Utility Shared Objects") + add_library(UtilityLibrary SHARED $) + target_link_libraries(UtilityLibrary PaassCoreStatic) + if (PAASS_USE_ROOT) + target_link_libraries(UtilityLibrary ${ROOT_LIBRARIES}) + endif (PAASS_USE_ROOT) + install(TARGETS UtilityLibrary DESTINATION lib) +endif (BUILD_SHARED_LIBS) + +#Create Utility static library and add ncurses if we have it +add_library(ResourceStatic STATIC $) +if (PAASS_USE_ROOT) + target_link_libraries(ResourceStatic ${ROOT_LIBRARIES}) +endif (PAASS_USE_ROOT) \ No newline at end of file diff --git a/Scan/utkscan/analyzers/source/Gsl1Fitter.cpp b/Analysis/Resources/source/Gsl1Fitter.cpp similarity index 73% rename from Scan/utkscan/analyzers/source/Gsl1Fitter.cpp rename to Analysis/Resources/source/Gsl1Fitter.cpp index 30798b964..02d2d3af6 100644 --- a/Scan/utkscan/analyzers/source/Gsl1Fitter.cpp +++ b/Analysis/Resources/source/Gsl1Fitter.cpp @@ -2,14 +2,6 @@ /// \brief Implementation of the GSL fitting routine for GSL v2+ /// \author S. V. Paulauskas /// \date August 8, 2016 -#include - -#include - -#include -#include -#include - #include "GslFitter.hpp" /** Defines the GSL fitting function for standard PMTs @@ -55,32 +47,34 @@ int SiPmtFunctionDerivative(const gsl_vector *x, void *FitData, gsl_vector *f, using namespace std; -void GslFitter::PerformFit(const std::vector &data, - const std::pair &pars, - const double &weight/* = 1.*/, - const double &area/* = 1.*/) { +double GslFitter::CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &max, + const std::pair baseline) { + gsl_multifit_function_fdf f; int status; const size_t sizeFit = data.size(); size_t numParams; - double xInit[3]; + double xInit[2]; - double y[sizeFit], sigma[sizeFit]; + double *y = new double[sizeFit]; + double *sigma = new double[sizeFit]; for(unsigned int i = 0; i < sizeFit; i++) { y[i] = data.at(i); - sigma[i] = weight; + sigma[i] = baseline.second; } - struct FitDriver::FitData fitData = - {sizeFit, y, sigma, pars.first, pars.second, area}; + struct GslFitter::FitData fitData = + {sizeFit, y, sigma, pars.first, pars.second, qdc_}; f.n = sizeFit; f.params = &fitData; - if(!isFastSipm_) { + if(!isFastSiPm_) { numParams = 2; xInit[0] = 0.0; - xInit[1] = 2.5; + xInit[1] = 0.3; f.f = &PmtFunction; f.df = &CalcPmtJacobian; f.fdf = &PmtFunctionDerivative; @@ -91,7 +85,6 @@ void GslFitter::PerformFit(const std::vector &data, f.df = &CalcSiPmtJacobian; f.fdf = &SiPmtFunctionDerivative; } - dof_ = sizeFit - numParams; const gsl_multifit_fdfsolver_type *T = gsl_multifit_fdfsolver_lmsder; gsl_vector_view x = gsl_vector_view_array (xInit, numParams); @@ -100,37 +93,35 @@ void GslFitter::PerformFit(const std::vector &data, f.p = numParams; gsl_multifit_fdfsolver_set (s, &f, &x.vector); - for(unsigned int iter = 0; iter < 1e8; iter++) { + static const double maxIter = 1e8; + static const double tolerance = 1e-4; + + for(unsigned int iter = 0; iter < maxIter; iter++) { status = gsl_multifit_fdfsolver_iterate(s); if(status) break; - status = gsl_multifit_test_delta (s->dx, s->x, 1e-4, 1e-4); + status = gsl_multifit_test_delta (s->dx, s->x, tolerance, tolerance); if(status != GSL_CONTINUE) break; } - gsl_multifit_covar (s->J, 0.0, covar); - chi_ = gsl_blas_dnrm2(s->f); - - if(!isFastSipm_) { - phase_ = gsl_vector_get(s->x,0); - amp_ = gsl_vector_get(s->x,1); - } else { - phase_ = gsl_vector_get(s->x,0); - amp_ = 0.0; - } + double phase = gsl_vector_get(s->x, 0); gsl_multifit_fdfsolver_free (s); gsl_matrix_free (covar); + delete y; + delete sigma; + + return phase; } int PmtFunction (const gsl_vector * x, void *FitData, gsl_vector * f) { - size_t n = ((struct FitDriver::FitData *)FitData)->n; - double *y = ((struct FitDriver::FitData *)FitData)->y; - double *sigma = ((struct FitDriver::FitData *)FitData)->sigma; - double beta = ((struct FitDriver::FitData *)FitData)->beta; - double gamma = ((struct FitDriver::FitData *)FitData)->gamma; - double qdc = ((struct FitDriver::FitData *)FitData)->qdc; + size_t n = ((struct GslFitter::FitData *)FitData)->n; + double *y = ((struct GslFitter::FitData *)FitData)->y; + double *sigma = ((struct GslFitter::FitData *)FitData)->sigma; + double beta = ((struct GslFitter::FitData *)FitData)->beta; + double gamma = ((struct GslFitter::FitData *)FitData)->gamma; + double qdc = ((struct GslFitter::FitData *)FitData)->qdc; double phi = gsl_vector_get (x, 0); double alpha = gsl_vector_get (x, 1); @@ -151,11 +142,11 @@ int PmtFunction (const gsl_vector * x, void *FitData, gsl_vector * f) { } int CalcPmtJacobian (const gsl_vector * x, void *FitData, gsl_matrix * J) { - size_t n = ((struct FitDriver::FitData *)FitData)->n; - double *sigma = ((struct FitDriver::FitData *) FitData)->sigma; - double beta = ((struct FitDriver::FitData *)FitData)->beta; - double gamma = ((struct FitDriver::FitData *)FitData)->gamma; - double qdc = ((struct FitDriver::FitData *)FitData)->qdc; + size_t n = ((struct GslFitter::FitData *)FitData)->n; + double *sigma = ((struct GslFitter::FitData *) FitData)->sigma; + double beta = ((struct GslFitter::FitData *)FitData)->beta; + double gamma = ((struct GslFitter::FitData *)FitData)->gamma; + double qdc = ((struct GslFitter::FitData *)FitData)->qdc; double phi = gsl_vector_get (x, 0); double alpha = gsl_vector_get (x, 1); @@ -189,11 +180,11 @@ int PmtFunctionDerivative (const gsl_vector * x, void *FitData, gsl_vector * f, } int SiPmtFunction (const gsl_vector * x, void *FitData, gsl_vector * f) { - size_t n = ((struct FitDriver::FitData *)FitData)->n; - double *y = ((struct FitDriver::FitData *)FitData)->y; - double *sigma = ((struct FitDriver::FitData *)FitData)->sigma; - double gamma = ((struct FitDriver::FitData *)FitData)->gamma; - double qdc = ((struct FitDriver::FitData *)FitData)->qdc; + size_t n = ((struct GslFitter::FitData *)FitData)->n; + double *y = ((struct GslFitter::FitData *)FitData)->y; + double *sigma = ((struct GslFitter::FitData *)FitData)->sigma; + double gamma = ((struct GslFitter::FitData *)FitData)->gamma; + double qdc = ((struct GslFitter::FitData *)FitData)->qdc; double phi = gsl_vector_get (x, 0); @@ -207,10 +198,10 @@ int SiPmtFunction (const gsl_vector * x, void *FitData, gsl_vector * f) { } int CalcSiPmtJacobian (const gsl_vector * x, void *FitData, gsl_matrix * J) { - size_t n = ((struct FitDriver::FitData *)FitData)->n; - double *sigma = ((struct FitDriver::FitData *)FitData)->sigma; - double gamma = ((struct FitDriver::FitData *)FitData)->gamma; - double qdc = ((struct FitDriver::FitData *)FitData)->qdc; + size_t n = ((struct GslFitter::FitData *)FitData)->n; + double *sigma = ((struct GslFitter::FitData *)FitData)->sigma; + double gamma = ((struct GslFitter::FitData *)FitData)->gamma; + double qdc = ((struct GslFitter::FitData *)FitData)->qdc; double phi = gsl_vector_get (x, 0); double dphi; diff --git a/Analysis/Resources/source/Gsl2Fitter.cpp b/Analysis/Resources/source/Gsl2Fitter.cpp new file mode 100644 index 000000000..bf40176c5 --- /dev/null +++ b/Analysis/Resources/source/Gsl2Fitter.cpp @@ -0,0 +1,208 @@ +/// \file Gsl2Fitter.cpp +/// \brief Implementation of the GSL fitting routine for GSL v2+ +/// \author S. V. Paulauskas +/// \date August 8, 2016 +#include "GslFitter.hpp" + +/** Defines the GSL fitting function for standard PMTs + * \param [in] x : the vector of gsl starting parameters + * \param [in] FitData : The data to use for the fit + * \param [in] f : pointer to the function + * \return an integer that GSL does something magical with */ +int PmtFunction(const gsl_vector *x, void *FitData, gsl_vector *f); + +/** Defines the GSL fitting function for standard PMTs + * \param [in] x : the vector of gsl starting parameters + * \param [in] FitData : The data to use for the fit + * \param [in] J : pointer to the Jacobian of the function + * \return an integer that GSL does something magical with */ +int CalcPmtJacobian(const gsl_vector *x, void *FitData, gsl_matrix *J); + +/** Defines the GSL fitting function for the fast output of SiPMTs + * \param [in] x : the vector of gsl starting parameters + * \param [in] FitData : The data to use for the fit + * \param [in] f : pointer to the function + * \return an integer that GSL does something magical with */ +int SiPmtFunction(const gsl_vector *x, void *FitData, gsl_vector *f); + +/** Defines the GSL fitting function for the fast output SiPMTs + * \param [in] x : the vector of gsl starting parameters + * \param [in] FitData : The data to use for the fit + * \param [in] J : pointer to the Jacobian of the function + * \return an integer that GSL does something magical with */ +int CalcSiPmtJacobian(const gsl_vector *x, void *FitData, gsl_matrix *J); + +using namespace std; + +double GslFitter::CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &max, + const std::pair baseline) { + gsl_multifit_function_fdf f; + int info; + const size_t n = data.size(); + size_t p; + double xInit[3]; + + if (!isFastSiPm_) { + p = 2; + xInit[0] = 0.0; + xInit[1] = 2.5; + + f.f = &PmtFunction; + f.df = &CalcPmtJacobian; + } else { + p = 1; + xInit[0] = (double) data.size() * 0.5; + + f.f = &SiPmtFunction; + f.df = &CalcSiPmtJacobian; + } + + dof_ = n - p; + + const gsl_multifit_fdfsolver_type *T = gsl_multifit_fdfsolver_lmsder; + gsl_multifit_fdfsolver *s = gsl_multifit_fdfsolver_alloc(T, n, p); + gsl_matrix *jac = gsl_matrix_alloc(n, p); + gsl_matrix *covar = gsl_matrix_alloc(p, p); + double *y = new double[n]; + double *weights = new double[n]; + struct FitData fitData = {n, y, weights, pars.first, + pars.second, qdc_}; + gsl_vector_view x = gsl_vector_view_array(xInit, p); + gsl_vector_view w = gsl_vector_view_array(weights, n); + + static const unsigned int maxIter = 100; + static const double xtol = 1e-4; + static const double gtol = 1e-4; + static const double ftol = 0.0; + + f.n = n; + f.p = p; + f.params = &fitData; + + for (unsigned int i = 0; i < n; i++) { + weights[i] = baseline.second; + y[i] = data[i]; + } + + gsl_multifit_fdfsolver_wset(s, &f, &x.vector, &w.vector); + gsl_multifit_fdfsolver_driver(s, maxIter, xtol, gtol, ftol, &info); + gsl_multifit_fdfsolver_jac(s, jac); + gsl_multifit_covar(jac, 0.0, covar); + + gsl_vector *res_f = gsl_multifit_fdfsolver_residual(s); + chi_ = gsl_blas_dnrm2(res_f); + + double phase = 0.0; + if (!isFastSiPm_) { + phase = gsl_vector_get(s->x, 0); + amp_ = gsl_vector_get(s->x, 1); + } else { + phase = gsl_vector_get(s->x, 0); + amp_ = 0.0; + } + + gsl_multifit_fdfsolver_free(s); + gsl_matrix_free(covar); + gsl_matrix_free(jac); + delete y; + delete weights; + + return phase; +} + +int PmtFunction(const gsl_vector *x, void *FitData, gsl_vector *f) { + size_t n = ((struct GslFitter::FitData *) FitData)->n; + double *y = ((struct GslFitter::FitData *) FitData)->y; + double beta = ((struct GslFitter::FitData *) FitData)->beta; + double gamma = ((struct GslFitter::FitData *) FitData)->gamma; + double qdc = ((struct GslFitter::FitData *) FitData)->qdc; + + double phi = gsl_vector_get(x, 0); + double alpha = gsl_vector_get(x, 1); + + for (size_t i = 0; i < n; i++) { + double t = i; + double diff = t - phi; + double Yi = 0; + + if (t < phi) + Yi = 0; + else + Yi = qdc * alpha * exp(-beta * diff) * + (1 - exp(-pow(gamma * diff, 4.))); + + gsl_vector_set(f, i, Yi - y[i]); + } + return (GSL_SUCCESS); +} + +int CalcPmtJacobian(const gsl_vector *x, void *FitData, gsl_matrix *J) { + size_t n = ((struct GslFitter::FitData *) FitData)->n; + double beta = ((struct GslFitter::FitData *) FitData)->beta; + double gamma = ((struct GslFitter::FitData *) FitData)->gamma; + double qdc = ((struct GslFitter::FitData *) FitData)->qdc; + + double phi = gsl_vector_get(x, 0); + double alpha = gsl_vector_get(x, 1); + + double dphi, dalpha; + + for (size_t i = 0; i < n; i++) { + double t = i; + double diff = t - phi; + double gaussSq = exp(-pow(gamma * diff, 4.)); + if (t < phi) { + dphi = 0; + dalpha = 0; + } else { + dphi = alpha * beta * qdc * exp(-beta * diff) * (1 - gaussSq) - + 4 * alpha * qdc * pow(diff, 3.) * exp(-beta * diff) * + pow(gamma, 4.) * gaussSq; + dalpha = qdc * exp(-beta * diff) * (1 - gaussSq); + } + gsl_matrix_set(J, i, 0, dphi); + gsl_matrix_set(J, i, 1, dalpha); + } + return (GSL_SUCCESS); +} + +int SiPmtFunction(const gsl_vector *x, void *FitData, gsl_vector *f) { + size_t n = ((struct GslFitter::FitData *) FitData)->n; + double *y = ((struct GslFitter::FitData *) FitData)->y; + double gamma = ((struct GslFitter::FitData *) FitData)->gamma; + double qdc = ((struct GslFitter::FitData *) FitData)->qdc; + + double phi = gsl_vector_get(x, 0); + + for (size_t i = 0; i < n; i++) { + double t = i; + double diff = t - phi; + double Yi = (qdc / (gamma * sqrt(2 * M_PI))) * + exp(-diff * diff / (2 * gamma * gamma)); + gsl_vector_set(f, i, Yi - y[i]); + } + return (GSL_SUCCESS); +} + +int CalcSiPmtJacobian(const gsl_vector *x, void *FitData, gsl_matrix *J) { + size_t n = ((struct GslFitter::FitData *) FitData)->n; + double gamma = ((struct GslFitter::FitData *) FitData)->gamma; + double qdc = ((struct GslFitter::FitData *) FitData)->qdc; + + double phi = gsl_vector_get(x, 0); + double dphi; + + for (size_t i = 0; i < n; i++) { + double t = i; + double diff = t - phi; + + dphi = (qdc * diff / (pow(gamma, 3) * sqrt(2 * M_PI))) * + exp(-diff * diff / (2 * gamma * gamma)); + + gsl_matrix_set(J, i, 0, dphi); + } + return (GSL_SUCCESS); +} + diff --git a/Analysis/Resources/source/PolynomialCfd.cpp b/Analysis/Resources/source/PolynomialCfd.cpp new file mode 100644 index 000000000..957652095 --- /dev/null +++ b/Analysis/Resources/source/PolynomialCfd.cpp @@ -0,0 +1,50 @@ +/// @file PolynomialCfd.cpp +/// @brief Timing method that calculates the timing using a Polynomial based +/// CFD. +/// @author C. R. Thornsberry and S. V. Paulauskas +/// @date December 6, 2016 +#include + +#include "HelperFunctions.hpp" +#include "PolynomialCfd.hpp" + +using namespace std; + +/// Perform CFD analysis on the waveform. +double PolynomialCfd::CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &max, + const std::pair baseline) { + if (data.size() == 0) + throw range_error("PolynomialCfd::CalculatePhase - The data vector " + "was empty!"); + if (data.size() < max.first) + throw range_error("PolynomialCfd::CalculatePhase - The maximum " + "position is larger than the size of the " + "data vector."); + + double threshold = pars.first * max.second; + double phase = -9999; + float multiplier = 1.; + + vector result; + for (unsigned int cfdIndex = max.first; cfdIndex > 0; cfdIndex--) { + if (data[cfdIndex - 1] < threshold && data[cfdIndex] >= threshold) { + // Fit the rise of the trace to a 2nd order polynomial. + result = Polynomial::CalculatePoly2(data, cfdIndex - 1).second; + + // Calculate the phase of the trace. + if(result[2] > 1) + multiplier = -1.; + + phase = (-result[1] + multiplier * + sqrt(result[1] * result[1] - + 4 * result[2] * + (result[0] - threshold))) / + (2 * result[2]); + + break; + } + } + return phase; +} \ No newline at end of file diff --git a/Analysis/Resources/source/RootFitter.cpp b/Analysis/Resources/source/RootFitter.cpp new file mode 100644 index 000000000..f4dd85b0a --- /dev/null +++ b/Analysis/Resources/source/RootFitter.cpp @@ -0,0 +1,48 @@ +/// @file RootFitter.cpp +/// @brief Class to handle fitting traces using ROOT +/// @author S. V. Paulauskas +/// @date December 18, 2016 +#include + +#include + +#include + +#include "RootFitter.hpp" +#include "VandleTimingFunction.hpp" + +using namespace std; + +RootFitter::RootFitter() { + vandleTimingFunction_ = new VandleTimingFunction(); + func_ = new TF1("func", vandleTimingFunction_, 0., 1.e6, 5); +} + +RootFitter::~RootFitter() { + delete vandleTimingFunction_; + delete func_; +} + +double RootFitter::CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &maxInfo, + std::pair baseline) { + if (data.size() == 0) + throw range_error( + "RootFitter::CalculatePhase - The data was sized zero."); + + vector xvals; + for (unsigned int i = 0; i < data.size(); i++) + xvals.push_back(double(i)); + + TGraph graph((int) data.size(), &(xvals[0]), &(data[0])); + + func_->SetParameters(0, qdc_ * 0.5); + func_->FixParameter(2, pars.first); + func_->FixParameter(3, pars.second); + func_->FixParameter(4, 0.0); + + graph.Fit(func_, "WRQ", "", 0, data.size()); + + return func_->GetParameter(0); +} \ No newline at end of file diff --git a/Analysis/Resources/source/TraditionalCfd.cpp b/Analysis/Resources/source/TraditionalCfd.cpp new file mode 100644 index 000000000..f36388141 --- /dev/null +++ b/Analysis/Resources/source/TraditionalCfd.cpp @@ -0,0 +1,58 @@ +///@file TraditionalCfd.cpp +///@brief Traditional CFD implemented digitally, similar behavior to a NIM +/// Module. +///@author S. V. Paulauskas +///@date July 22, 2011 + +#include "HelperFunctions.hpp" +#include "TraditionalCfd.hpp" + +using namespace std; + +double TraditionalCfd::CalculatePhase(const std::vector &data, + const std::pair &pars, + const std::pair &max, + const std::pair baseline) { + if (data.size() == 0) + throw range_error("PolynomialCfd::CalculatePhase - The data vector " + "was empty!"); + if (data.size() < max.first) + throw range_error("PolynomialCfd::CalculatePhase - The maximum " + "position is larger than the size of the " + "data vector."); + + unsigned int delay = (unsigned int) pars.second; + double fraction = pars.first; + vector cfd; + + //We are going to calculate the CFD here. + for (unsigned int i = 0; i < data.size() - delay; i++) + cfd.push_back(fraction * (data[i] - data[i + delay])); + + //Now we find the maximum and minimum position to locate the zero crossing. + vector::iterator cfdMin = min_element(cfd.begin(), cfd.end()); + vector::iterator cfdMax = max_element(cfd.begin(), cfd.end()); + + vector fitY(cfdMin, cfdMax); + vector fitX; + + for (int i = int(cfdMin - cfd.begin()); i < int(cfdMax - cfd.begin()); i++) + fitX.push_back((double) i); + + double num = fitY.size(); + + double sumXSq = 0, sumX = 0, sumXY = 0, sumY = 0; + + for (unsigned int i = 0; i < num; i++) { + sumXSq += fitX.at(i) * fitX.at(i); + sumX += fitX.at(i); + sumY += fitY.at(i); + sumXY += fitX.at(i) * fitY.at(i); + } + + double deltaPrime = num * sumXSq - sumX * sumX; + + //Rerun the negative of the intercept / slope + return -((1 / deltaPrime) * (sumXSq * sumY - sumX * sumXY)) / + ((1 / deltaPrime) * (num * sumXY - sumX * sumY)); +} \ No newline at end of file diff --git a/Analysis/Resources/source/VandleTimingFunction.cpp b/Analysis/Resources/source/VandleTimingFunction.cpp new file mode 100644 index 000000000..fbe0b0e7d --- /dev/null +++ b/Analysis/Resources/source/VandleTimingFunction.cpp @@ -0,0 +1,22 @@ +/// @file VandleTimingFunction.hpp +/// @brief A class to handle the processing of traces +/// @author S. V. Paulauskas +/// @date October 3, 2014 +#include + +#include "VandleTimingFunction.hpp" + +///This defines the stock VANDLE timing function. Here is a breakdown of the +/// parameters: +/// * p[0] = phase +/// * p[1] = amplitude +/// * p[2] = beta +/// * p[3] = gamma +/// * p[4] = baseline +double VandleTimingFunction::operator()(double *x, double *p) { + if (x[0] < p[0]) + return p[4]; + + return p[1] * std::exp(-p[2] * (x[0] - p[0])) * + (1 - std::exp(-std::pow(p[3] * (x[0] - p[0]), 4.))) + p[4]; +} diff --git a/Analysis/Resources/source/XiaCfd.cpp b/Analysis/Resources/source/XiaCfd.cpp new file mode 100644 index 000000000..a68e26d46 --- /dev/null +++ b/Analysis/Resources/source/XiaCfd.cpp @@ -0,0 +1,49 @@ +///@file TraditionalCfd.cpp +///@brief Same CFD algorithm implemented by Xia LLC but offline. +///@author S. V. Paulauskas +///@date July 22, 2011 + +#include "XiaCfd.hpp" + +/// Perform CFD analysis on the waveform +double XiaCfd::CalculatePhase(const double &F_/*=0.5*/, + const size_t &D_/*=1*/, + const size_t &L_/*=1*/) { + if (size == 0 || baseline < 0) { return -9999; } + if (!cfdvals) + cfdvals = new double[size]; + + double cfdMinimum = 9999; + size_t cfdMinIndex = 0; + + phase = -9999; + + // Compute the cfd waveform. + for (size_t cfdIndex = 0; cfdIndex < size; ++cfdIndex) { + cfdvals[cfdIndex] = 0.0; + if (cfdIndex >= L_ + D_ - 1) { + for (size_t i = 0; i < L_; i++) + cfdvals[cfdIndex] += + F_ * (event->adcTrace[cfdIndex - i] - baseline) - + (event->adcTrace[cfdIndex - i - D_] - baseline); + } + if (cfdvals[cfdIndex] < cfdMinimum) { + cfdMinimum = cfdvals[cfdIndex]; + cfdMinIndex = cfdIndex; + } + } + + // Find the zero-crossing. + if (cfdMinIndex > 0) { + // Find the zero-crossing. + for (size_t cfdIndex = cfdMinIndex - 1; cfdIndex >= 0; cfdIndex--) { + if (cfdvals[cfdIndex] >= 0.0 && cfdvals[cfdIndex + 1] < 0.0) { + phase = cfdIndex - cfdvals[cfdIndex] / + (cfdvals[cfdIndex + 1] - cfdvals[cfdIndex]); + break; + } + } + } + + return phase; +} \ No newline at end of file diff --git a/Analysis/Resources/tests/CMakeLists.txt b/Analysis/Resources/tests/CMakeLists.txt new file mode 100644 index 000000000..8852ad4ca --- /dev/null +++ b/Analysis/Resources/tests/CMakeLists.txt @@ -0,0 +1,30 @@ +if (PAASS_USE_GSL) + if (${GSL_VERSION} GREATER 1.9) + set(GSL_FITTER_SOURCES ../source/Gsl2Fitter.cpp) + else (${GSL_VERSION} LESS 2.0) + set(GSL_FITTER_SOURCES ../source/Gsl1Fitter.cpp) + endif (${GSL_VERSION} GREATER 1.9) + + #Build the test to see if the GSL fitting algorithm is behaving. + set(GSL_FITTER_SOURCES ${GSL_FITTER_SOURCES} unittest-GslFitter.cpp) + add_executable(unittest-GslFitter ${GSL_FITTER_SOURCES}) + target_link_libraries(unittest-GslFitter ${GSL_LIBRARIES} UnitTest++) + install(TARGETS unittest-GslFitter DESTINATION bin/unittests) +endif (PAASS_USE_GSL) + +add_executable(unittest-PolynomialCfd unittest-PolynomialCfd.cpp + ../source/PolynomialCfd.cpp) +target_link_libraries(unittest-PolynomialCfd UnitTest++) +install(TARGETS unittest-PolynomialCfd DESTINATION bin/unittests) + +add_executable(unittest-TraditionalCfd unittest-TraditionalCfd.cpp + ../source/TraditionalCfd.cpp) +target_link_libraries(unittest-TraditionalCfd UnitTest++) +install(TARGETS unittest-TraditionalCfd DESTINATION bin/unittests) + +if (PAASS_USE_ROOT) + add_executable(unittest-RootFitter unittest-RootFitter.cpp + ../source/RootFitter.cpp ../source/VandleTimingFunction.cpp) + target_link_libraries(unittest-RootFitter ${ROOT_LIBRARIES} UnitTest++) + install(TARGETS unittest-RootFitter DESTINATION bin/unittests) +endif (PAASS_USE_ROOT) diff --git a/Analysis/Resources/tests/unittest-GslFitter.cpp b/Analysis/Resources/tests/unittest-GslFitter.cpp new file mode 100644 index 000000000..c290eeb3b --- /dev/null +++ b/Analysis/Resources/tests/unittest-GslFitter.cpp @@ -0,0 +1,30 @@ +///\file unittest-GslFitter.cpp +///\brief A small code to test the functionality of the FitDriver +///\author S. V. Paulauskas +///\date August 8, 2016 +#include +#include + +#include + +#include "GslFitter.hpp" +#include "UnitTestSampleData.hpp" + +using namespace std; +using namespace unittest_trace_variables; +using namespace unittest_fit_variables; + +TEST_FIXTURE(GslFitter, TestGslFitter) { + //We need to set the QDC before the fit + SetQdc(21329.85714285); + + //Actually perform the fitting + double phase = CalculatePhase(waveform, fitting_parameters, + max_pair, baseline_pair); + + CHECK_CLOSE(-0.0826487, phase, 1.); +} + +int main(int argv, char *argc[]) { + return (UnitTest::RunAllTests()); +} \ No newline at end of file diff --git a/Analysis/Resources/tests/unittest-PolynomialCfd.cpp b/Analysis/Resources/tests/unittest-PolynomialCfd.cpp new file mode 100644 index 000000000..11efe5f5e --- /dev/null +++ b/Analysis/Resources/tests/unittest-PolynomialCfd.cpp @@ -0,0 +1,42 @@ +///@file unittest-PolynomialCfd.cpp +///@author S. V. Paulauskas +///@date December 12, 2016 +#include +#include +#include +#include + +#include + +#include "PolynomialCfd.hpp" +#include "UnitTestSampleData.hpp" + +using namespace std; +using namespace unittest_trace_variables; +using namespace unittest_cfd_variables; + + +TEST_FIXTURE(PolynomialCfd, TestPolynomialCfd) { + //Checking that we throw a range_error when the data vector is zero + CHECK_THROW(CalculatePhase(empty_vector_double, cfd_test_pars, max_pair, + baseline_pair), range_error); + + //Check that we throw a range error when the max position is larger than + // the data we provided. + CHECK_THROW(CalculatePhase(trace_sans_baseline, cfd_test_pars, + make_pair(trace_sans_baseline.size()+3, 100), + baseline_pair), range_error); + + //The expected value in this case is the value that I obtained after + // debugging the algorithm using other means. This check is here simply + // to tell us whether or not the algorithm has changed drastically from + // the "acceptable" value. + CHECK_CLOSE(73.9898, + CalculatePhase(trace_sans_baseline, cfd_test_pars, + extrapolated_maximum_pair,baseline_pair), + 5); +} + +int main(int argv, char *argc[]) { + return (UnitTest::RunAllTests()); +} \ No newline at end of file diff --git a/Analysis/Resources/tests/unittest-RootFitter.cpp b/Analysis/Resources/tests/unittest-RootFitter.cpp new file mode 100644 index 000000000..df8531ea2 --- /dev/null +++ b/Analysis/Resources/tests/unittest-RootFitter.cpp @@ -0,0 +1,28 @@ +/// @file unittest-RootFitter.cpp +/// @brief Unit tests for the RootFitter class +/// @author S. V. Paulauskas +/// @date December 18, 2016 +#include +#include + +#include + +#include "RootFitter.hpp" +#include "UnitTestSampleData.hpp" + +using namespace std; +using namespace unittest_trace_variables; +using namespace unittest_fit_variables; + +TEST_FIXTURE(RootFitter, TestRootFitter) { + CHECK_THROW(CalculatePhase(empty_vector_double, fitting_parameters, + max_pair, baseline_pair), range_error); + + SetQdc(waveform_qdc); + CHECK_CLOSE(-0.581124, CalculatePhase(waveform, fitting_parameters, + max_pair, baseline_pair), 1.); +} + +int main(int argv, char *argc[]) { + return (UnitTest::RunAllTests()); +} \ No newline at end of file diff --git a/Analysis/Resources/tests/unittest-TraditionalCfd.cpp b/Analysis/Resources/tests/unittest-TraditionalCfd.cpp new file mode 100644 index 000000000..3616edd0b --- /dev/null +++ b/Analysis/Resources/tests/unittest-TraditionalCfd.cpp @@ -0,0 +1,39 @@ +///@file unittest-TraditionalCfd.cpp +///@author S. V. Paulauskas +///@date December 12, 2016 +#include +#include + +#include + +#include "TraditionalCfd.hpp" +#include "UnitTestSampleData.hpp" + +using namespace std; +using namespace unittest_trace_variables; +using namespace unittest_cfd_variables; + +TEST_FIXTURE(TraditionalCfd, TestTraditionalCfd) { + //Checking that we throw a range_error when the data vector is zero + CHECK_THROW(CalculatePhase(empty_vector_double, cfd_test_pars, max_pair, + baseline_pair), range_error); + + //Check that we throw a range error when the max position is larger than + // the data we provided. + CHECK_THROW(CalculatePhase(trace_sans_baseline, cfd_test_pars, + make_pair(trace_sans_baseline.size()+3, 100), + baseline_pair), range_error); + + //The expected value in this case is the value that I obtained after + // debugging the algorithm using other means. This check is here simply + // to tell us whether or not the algorithm has changed drastically from + // the "acceptable" value. + CHECK_CLOSE(75.1408, + CalculatePhase(trace_sans_baseline, cfd_test_pars, + extrapolated_maximum_pair,baseline_pair), + 5); +} + +int main(int argv, char *argc[]) { + return (UnitTest::RunAllTests()); +} \ No newline at end of file diff --git a/Scan/scanor/CMakeLists.txt b/Analysis/ScanLibraries/CMakeLists.txt similarity index 51% rename from Scan/scanor/CMakeLists.txt rename to Analysis/ScanLibraries/CMakeLists.txt index 33d664731..b2324b769 100644 --- a/Scan/scanor/CMakeLists.txt +++ b/Analysis/ScanLibraries/CMakeLists.txt @@ -1,6 +1,10 @@ #Install include directories to support the shared library. -if(BUILD_SHARED_LIBS) +if(PAASS_BUILD_SHARED_LIBS) install(DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX}) -endif(BUILD_SHARED_LIBS) +endif(PAASS_BUILD_SHARED_LIBS) + +if(PAASS_BUILD_UNITTESTS) + add_subdirectory(tests) +endif(PAASS_BUILD_UNITTESTS) add_subdirectory(source) diff --git a/Analysis/ScanLibraries/include/ProcessedXiaData.hpp b/Analysis/ScanLibraries/include/ProcessedXiaData.hpp new file mode 100644 index 000000000..e23b524f0 --- /dev/null +++ b/Analysis/ScanLibraries/include/ProcessedXiaData.hpp @@ -0,0 +1,80 @@ +///@file ProcessedXiaData.hpp +///@brief An XiaData object that has undergone additional processing. +///@author S. V. Paulauskas and C. R. Thornsberry +///@date December 2, 2016 +#ifndef PIXIESUITE_PROCESSEDXIADATA_HPP +#define PIXIESUITE_PROCESSEDXIADATA_HPP +#include "Trace.hpp" +#include "XiaData.hpp" + +///This class contains additional information about the XiaData after +/// additional processing has been done. The processing includes, but is not +/// limited to energy/time calibrations, high resolution timing analysis, +/// trace analysis, etc. +class ProcessedXiaData : public XiaData { +public: + /// Default constructor. + ProcessedXiaData() { } + + ///Constructor taking the base class as an argument so that we can set + /// the trace information properly + ///@param[in] evt : The event that we are going to assign here. + ProcessedXiaData(XiaData &evt) : XiaData(evt) { + trace_ = evt.GetTrace(); + trace_.SetIsSaturated(evt.IsSaturated()); + }; + + /// Default Destructor. + ~ProcessedXiaData() { } + + ///@return The calibrated energy for the channel + double GetCalibratedEnergy() const { return calibratedEnergy_; } + + ///@return The sub-sampling arrival time of the signal in nanoseconds. + double GetHighResTimeInNs() const { return highResTimeInNs_; } + + ///@return A constant reference to the trace. + const Trace &GetTrace() const { return trace_; } + + ///@return An editable trace. + Trace &GetTrace() { return trace_; } + + ///@return The Walk corrected time of the channel + double GetWalkCorrectedTime() const { return walkCorrectedTime_; } + + ///Set the calibrated energy + ///@param [in] a : the calibrated energy + void SetCalibratedEnergy(const double &a) { calibratedEnergy_ = a; } + + ///Set to True if we would like to ignore this channel + ///@param[in] a : The value that we want to set + void SetIsIgnored(const bool &a) { isIgnored_ = a; } + + ///Set to true if the energy and time are not bogus values. + ///@param[in] a : The value that we would like to set + void SetIsValidData(const bool &a) { isValidData_ = a; } + + ///Set the high resolution time (Filter time (sans CFD) + phase ). + ///@param [in] a : the high resolution time + void SetHighResTime(const double &a) { highResTimeInNs_ = a; } + + ///Sets the trace appropriately + ///@param[in] a : The trace that we want to set + void SetTrace(const std::vector &a) { trace_ = a; } + + ///Set the Walk corrected time + ///@param [in] a : the walk corrected time */ + void SetWalkCorrectedTime(const double &a) { walkCorrectedTime_ = a; } + +private: + Trace trace_; ///< A Trace object to handle the Trace related stuff. + + bool isIgnored_; ///< True if we ignore this event. + bool isValidData_; ///< True if the energy and High Res time are valid. + + double calibratedEnergy_; ///< The energy after calibration + double highResTimeInNs_; ///< High Res time taken from pulse fits (in ns). + double walkCorrectedTime_; ///< The time after walk corrections +}; + +#endif //PIXIESUITE_PROCESSEDXIADATA_HPP diff --git a/Analysis/ScanLibraries/include/RootScanner.hpp b/Analysis/ScanLibraries/include/RootScanner.hpp new file mode 100644 index 000000000..63b5cf544 --- /dev/null +++ b/Analysis/ScanLibraries/include/RootScanner.hpp @@ -0,0 +1,35 @@ +#ifndef ROOTSCANNER_H +#define ROOTSCANNER_H + +#include + +#include "TCanvas.h" +#include "TApplication.h" + +#include "ScanInterface.hpp" + +class RootScanner : public ScanInterface { + public: + RootScanner(); + ~RootScanner(); + TCanvas *GetCanvas() {return canvas_;}; + void IdleTask(); + void UpdateZoom(TVirtualPad* pad = gPad); + void ResetZoom(TVirtualPad *pad = gPad); + + private: + TCanvas *canvas_; + + static const int numAxes_ = 3; + struct AxisInfo { + double rangeUserMin[numAxes_]; + double rangeUserMax[numAxes_]; + double limitMin[numAxes_]; + double limitMax[numAxes_]; + bool reset; + }; + std::map< TVirtualPad*, AxisInfo > zoomInfo_; + +}; + +#endif //ROOTSCANNER_H diff --git a/Scan/ScanLib/include/ScanInterface.hpp b/Analysis/ScanLibraries/include/ScanInterface.hpp similarity index 96% rename from Scan/ScanLib/include/ScanInterface.hpp rename to Analysis/ScanLibraries/include/ScanInterface.hpp index bf5798e69..8c499cc96 100644 --- a/Scan/ScanLib/include/ScanInterface.hpp +++ b/Analysis/ScanLibraries/include/ScanInterface.hpp @@ -111,8 +111,11 @@ class ScanInterface{ /// \return The name of the configuration file std::string GetSetupFilename(){ return(setup_filename); } - /// \return The name of the output file - std::string GetOutputFilename(){ return(output_filename); } + /// \return The name of the output file that doesn't include the path + std::string GetOutputFilename(){ return outputFilename_ ; } + + /// @return The path for the output file + std::string GetOutputPath() { return outputPath_; } /// Return a pointer to a fileInformation object used to store file header info. fileInformation *GetFileInfo(){ return &finfo; } @@ -279,7 +282,8 @@ class ScanInterface{ std::string workDir; /// Linux system current working directory. std::string homeDir; /// Linux user home directory. std::string setup_filename; //!< Configuration file to be opened - std::string output_filename; //!< Name of file to be used for output + std::string outputFilename_; //!< Name of file to be used for output + std::string outputPath_; int max_spill_size; /// Maximum size of a spill to read. int file_format; /// Input file format to use (0=.ldf, 1=.pld, 2=.root). @@ -332,6 +336,10 @@ class ScanInterface{ /// Open a new binary input file for reading. bool open_input_file(const std::string &fname_); + + ///Sets output Filename and path that were passed using the -o flag. + ///@param[in] a : The parameter that we are going to set + void SetOutputInformation(const std::string &a); }; /// Get the file extension from an input filename string. diff --git a/Analysis/ScanLibraries/include/Trace.hpp b/Analysis/ScanLibraries/include/Trace.hpp new file mode 100644 index 000000000..65a677f47 --- /dev/null +++ b/Analysis/ScanLibraries/include/Trace.hpp @@ -0,0 +1,226 @@ +///@file Trace.hpp +///@brief A simple class to store the traces. +///@author S. Liddick, D. T. Miller, S. V. Paualuskas +///@date Long time ago. +#ifndef __TRACE_HPP__ +#define __TRACE_HPP__ + +#include +#include +#include +#include + +#include + +/// @brief This defines a more extensible implementation of a digitized trace. +/// The class is derived from a vector of unsigned integers. This is the basic +/// form of a trace from most digitizers. The Trace class enables processed +/// information about the trace (such as the baseline, integral, etc.). +/// +/// We also store information about the Waveform. The waveform is the part +/// of the trace that actually contains information about the signal that +/// was captured. This excludes the baseline. +class Trace : public std::vector { +public: + ///Default constructor + Trace() : std::vector() {} + + ///An automatic conversion for the trace + ///@param [in] x : the trace to store in the class + Trace(const std::vector &x) : std::vector(x) {} + + ///@return Returns a std::pair containing the average and + /// standard deviation of the baseline as the .first and .second + /// respectively. + std::pair GetBaselineInfo() const { return baseline_; } + + ///@return Returns the energy sums that were set. + std::vector GetEnergySums() const { return esums_; } + + ///@return Returns a std::pair containing the + /// position of the maximum value in the trace and the amplitude of the + /// maximum that's been extrapolated and baseline subtracted as the .first + /// and .second respectively. + std::pair GetExtrapolatedMaxInfo() const { + return extrapolatedMax_; + } + + ///@return The baseline that goes with the filtered energies + double GetFilteredBaseline() const { return filteredBaseline_; } + + ///@return The energies found by filtering the trace. + std::vector GetFilteredEnergies() const { + return filteredEnergies_; + } + + ///@return Returns a std::pair containing the + /// position of the maximum value in the trace and the amplitude of the + /// maximum that's been baseline subtracted as the .first and .second + /// respectively. + std::pair GetMaxInfo() const { return max_; } + + ///@return The number of triggers that were found in the trace + unsigned int GetNumberOfTriggers() const { + return triggerPositions_.size(); + } + + ///@return The phase of the trace. + double GetPhase() const { return phase_; } + + ///@return The value of the QDC for the waveform + double GetQdc() const { return qdc_; } + + ///@returns the Signal to noise ratio of the trace + double GetSignalToNoiseRatio() const { + return 20*std::log10(max_.second / baseline_.second); + } + + ///@return The value of the tail-ratio for the waveform. + double GetTailRatio() const { return tailRatio_; } + + ///@return The value of tau that was calculated from the trace. + double GetTau() const { return tau_; } + + ///@return Returns the waveform sans baseline + std::vector GetTraceSansBaseline() const { + return traceSansBaseline_; + } + + ///@return Returns the Trigger Filter that was set. + std::vector GetTriggerFilter() const { return trigFilter_; } + + ///@return Returns a vector containing all of the found triggers + std::vector GetTriggerPositions() const { + return triggerPositions_; + } + + ///@return Returns the baseline subtracted waveform found inside the trace. + std::vector GetWaveform() { + return std::vector(traceSansBaseline_.begin() + + waveformRange_.first, + traceSansBaseline_.begin() + + waveformRange_.second); + } + + ///@return The bounds of the waveform in the trace + std::pair GetWaveformRange() const { + return waveformRange_; + } + + ///@return Returns the waveform with the baseline + std::vector GetWaveformWithBaseline() { + return std::vector(begin() + waveformRange_.first, + begin() + waveformRange_.second); + } + + ///@return True if the trace was saturated + bool IsSaturated() { return isSaturated_; } + + ///Sets the baseline information for the trace (average and standard + /// deviation) + ///@param[in] a : The pair containing the average and + /// standard deviation. + void SetBaseline(const std::pair &a) { baseline_ = a; } + + ///sets the energy sums vector if we are using the TriggerFilterAnalyzer + ///@param [in] a : the vector of energy sums + void SetEnergySums(const std::vector &a) { esums_ = a; } + + ///Sets the maximum information for the trace (position and baseline + /// subtracted extraploated value) + ///@param[in] a : The pair containing the position and + /// baseline subtracted extrapolated value. + void SetExtrapolatedMax(const std::pair &a) { + extrapolatedMax_ = a; + } + + ///Sets the baseline value found via a filter of the trace. This value is + /// in the "filtered" units. + void SetFilteredBaseline(const double &a) { filteredBaseline_ = a; } + + ///Sets the vector containing the energys found by performing a + /// trapezoidal filter on the trace. + ///@param[in] a : The vector containing the energies. + void SetFilteredEnergies(const std::vector &a) { + filteredEnergies_ = a; + } + + ///Sets the isSaturated_ private variable. + ///@param[in] a : Sets to true if the trace was flagged as saturated by + /// the electronics. + void SetIsSaturated(const bool &a) { isSaturated_ = a; } + + ///Sets the maximum information for the trace (position and baseline + /// subtracted value) + ///@param[in] a : The pair containing the position and + /// baseline subtracted value. + void SetMax(const std::pair &a) { max_ = a; } + + ///Sets the sub-sampling phase of the trace. + ///@param[in] a : The value of the phase that we want to set. This + /// comes from a fit or CFD analysis of the trace. + void SetPhase(const double &a) { phase_ = a; } + + ///Sets the value of the QDC that was calculated from the waveform + ///@param[in] a : The value that we are going to set + void SetQdc(const double &a) { qdc_ = a; } + + ///Sets the baseline subtracted trace. + ///@param[in] a : The vector that we are going to assign. + void SetTraceSansBaseline(const std::vector &a) { + traceSansBaseline_ = a; + } + + ///Sets the value of the tail-ratio method used for doing discrimination + /// on signals that have a varying decay constant. This is generally + /// defined as the integral of the "tail" of the waveform divided by the + /// QDC of the waveform. + ///@param[in] a : The value that we're going to set. + void SetTailRatio(const double &a) { tailRatio_ = a; } + + ///Sets the value of Tau as calculated from the waveform + ///@param[in] a : The value that we are going to assign. + void SetTau(const double &a) { tau_ = a; } + + ///sets the trigger filter if we are using the TriggerFilterAnalyzer + ///@param [in] a : the vector with the trigger filter + void SetTriggerFilter(const std::vector &a) { trigFilter_ = a; } + + ///Sets the trigger positions that were located by a trapezoidal filter + /// of the trace. + ///@param[in] a : The vector containing the trigger positions. + void SetTriggerPositions(const std::vector &a) { + triggerPositions_ = a; + } + + ///Sets the bounds for the waveform + ///@param[in] a : the range we want to set + void SetWaveformRange(const std::pair &a) { + waveformRange_ = a; + } + +private: + bool isSaturated_; ///< True if the trace was flagged as saturated. + + double phase_; ///< The sub-sampling phase of the trace. + double qdc_; ///< The qdc that was calculated from the waveform. + double tailRatio_; ///< The tail-ratio of the trace. + double tau_; ///< The tau as calculated from the waveform + double filteredBaseline_; ///< Baseline calculated from filtering the trc. + + unsigned int numTriggers_; ///< The number of triggers in the trace. + + std::pair baseline_; ///< Baseline Average and Std. Dev. + std::pair max_; ///< Max position and value sans baseline + std::pair extrapolatedMax_; ///< Max position and extrapolated value + std::pair waveformRange_; ///< Waveform Range + + std::vector filteredEnergies_; ///< Energies from filtering the trc. + std::vector traceSansBaseline_; ///< Baseline subtracted trace + std::vector trigFilter_; ///< The trigger filter for the trace + std::vector esums_; ///< The Energy sums calculated from the trace + + std::vector triggerPositions_; ///< Trigger positions in trc. +}; + +#endif // __TRACE_H_ \ No newline at end of file diff --git a/Scan/ScanLib/include/Unpacker.hpp b/Analysis/ScanLibraries/include/Unpacker.hpp similarity index 93% rename from Scan/ScanLib/include/Unpacker.hpp rename to Analysis/ScanLibraries/include/Unpacker.hpp index 0714809e0..82103ea6d 100644 --- a/Scan/ScanLib/include/Unpacker.hpp +++ b/Analysis/ScanLibraries/include/Unpacker.hpp @@ -17,6 +17,8 @@ #include #include +#include "XiaListModeDataMask.hpp" + #ifndef MAX_PIXIE_MOD #define MAX_PIXIE_MOD 12 #endif @@ -68,6 +70,12 @@ class Unpacker{ /// Set the width of events in pixie16 clock ticks. double SetEventWidth(double width_){ return (eventWidth = width_); } + + void InitializeDataMask(const std::string &firmware, + const unsigned int& frequency) { + mask_.SetFrequency(frequency); + mask_.SetFirmware(firmware); + } /// Set the address of the scan interface used for file operations. ScanInterface *SetInterface(ScanInterface *interface_){ return (interface = interface_); } @@ -98,6 +106,9 @@ class Unpacker{ void Run(){ running = true; } protected: + XiaListModeDataMask mask_; //Object providing the masks necessary to + // decode the data. + double eventWidth; /// The width of the raw event in pixie clock ticks (8 ns). bool debug_mode; /// True if debug mode is set. @@ -129,9 +140,12 @@ class Unpacker{ * \param[out] bufLen The number of words in the buffer. * \return The number of XiaDatas read from the buffer. */ - int ReadBuffer(unsigned int *buf, unsigned long &bufLen); + int ReadBuffer(unsigned int *buf); private: + ///Vector containing the list of channels decoded from + std::vector decodedList_; + unsigned int TOTALREAD; /// Maximum number of data words to read. unsigned int maxWords; /// Maximum number of data words for revision D. unsigned int numRawEvt; /// The total count of raw events read from file. diff --git a/Analysis/ScanLibraries/include/XiaData.hpp b/Analysis/ScanLibraries/include/XiaData.hpp new file mode 100644 index 000000000..006f5e007 --- /dev/null +++ b/Analysis/ScanLibraries/include/XiaData.hpp @@ -0,0 +1,268 @@ +///@file XiaData.cpp +///@brief A class that holds information from the XIA LLC. Pixie-16 List +/// Mode Data +///@authors C. R. Thornsberry and S. V. Paulauskas +#ifndef XIADATA_HPP +#define XIADATA_HPP + +#include + +/*! \brief A pixie16 channel event + * + * All data is grouped together into channels. For each pixie16 channel that + * fires the energy, time (both trigger time and event time), and trace (if + * applicable) are obtained. Additional information includes the channels + * identifier, calibrated energies, trace analysis information. + * Note that this currently stores raw values internally through pixie word types + * but returns data values through native C types. This is potentially non-portable. + */ +class XiaData { +public: + /// Default constructor. + XiaData() { Clear(); } + + ///Default Destructor. + ~XiaData() {}; + + ///@brief Equality operator that compares checks if we have the same + /// channel (i.e. the ID and Time are identical) + ///@param[in] rhs : The right hand side of the comparison + ///@return True if the two XiaData classes are equal. + bool operator==(const XiaData &rhs) const { + return GetId() == rhs.GetId() && GetTime() == rhs.GetTime(); + } + + ///@brief The conjugate of the equality operator + ///@param[in] rhs : The right hand side for the comparison + ///@return True if the two are not equal. + bool operator!=(const XiaData &rhs) const { + return !operator==(rhs); + } + + ///@brief The less than operator that compares if the time of the current + /// class is less than the time of the comparison class. + ///@param[in] rhs : The right hand side for the comparison + ///@return True if this instance arrived earlier than the right hand side. + bool operator<(const XiaData &rhs) const { + return GetTime() < rhs.GetTime(); + } + + ///@brief The conjugate of the less than operator + ///@param[in] rhs : The right hand side for the comparison + ///@return True if the right hand side arrived ealier than the left hand + /// side. + bool operator>(const XiaData &rhs) const { + return !operator<(rhs); + } + + ///@brief A method that will compare the times of two XiaData classes + /// this method can be used in conjunction with sorting methods + ///@param[in] lhs : A pointer to the left hand side of the comparison + ///@param[in] rhs : A pointer to the right hand side of the comparison + ///@return True if the time of arrival for right hand side is later than + /// that of the left hand side. + static bool CompareTime(const XiaData *lhs, const XiaData *rhs) { + return lhs->GetTime() < rhs->GetTime(); + } + + ///@brief A method that will compare the unique ID of two XiaData classes + ///@param[in] lhs : A pointer to the left hand side of the comparison + ///@param[in] rhs : A pointer to the right hand side of the comparison + ///@return Return true if left hand side has a lower ID than the right + /// hand side. + static bool CompareId(const XiaData *lhs, const XiaData *rhs) { + return (lhs->GetId() < rhs->GetId()); + } + + ///@return The status of the CFD Forced Trigger Bit + bool GetCfdForcedTriggerBit() const { return cfdForceTrig_; } + + ///@return The status of the CFD Trigger bit. + bool GetCfdTriggerSourceBit() const { return cfdTrigSource_; } + + ///@return True if we had a pileup detected on the module + bool IsPileup() const { return isPileup_; } + + ///@return True if the trace was flagged as a pileup + bool IsSaturated() const { return isSaturated_; } + + ///@return True if this channel was generated on the module + bool IsVirtualChannel() const { return isVirtualChannel_; } + + ///@return The baseline as calculated on-board the Pixie-16 modules using + /// the energy filter. This parameter is only set if the data set + /// contains the Energy Sums in the list mode data. This baseline cannot + /// be used in conjunction with trace information. + double GetBaseline() const { return baseline_; } + + ///@return The energy that was calculated on the module + double GetEnergy() const { return energy_; } + + ///@return The time for the channel including all of the CFD information + /// when available. + double GetTime() const { return time_; } + + ///@return The arrival time of the signal without any CFD information in + /// the calculation + double GetTimeSansCfd() const {return timeSansCfd_;} + + ///@return The CFD fractional time in clockticks + unsigned int GetCfdFractionalTime() const { return cfdTime_; } + + ///@return The Channel number that recorded these data + unsigned int GetChannelNumber() const { return chanNum_; } + + ///@return The crate number that had the module + unsigned int GetCrateNumber() const { return crateNum_; } + + ///@return The upper 16 bits of the event time + unsigned int GetEventTimeHigh() const { return eventTimeHigh_; } + + ///@return The lower 32 bits of the event time + unsigned int GetEventTimeLow() const { return eventTimeLow_; } + + ///@return The upper 16 bits of the external time stamp provided to the + /// module via the front panel + unsigned int GetExternalTimeHigh() const { return externalTimeHigh_; } + + ///@return The lower 32 bits of the external time stamp provided to the + /// module via the front panel + unsigned int GetExternalTimeLow() const { return externalTimeLow_; } + + ///@return The unique ID of the channel. + ///We can have a maximum of 208 channels in a crate, the first module + /// (#0) is always in the second slot of the crate, and we always have 16 + /// channels + unsigned int GetId() const { + return crateNum_ * 208 + GetModuleNumber() * 16 + chanNum_; + } + + ///@return the module number + unsigned int GetModuleNumber() const { + return slotNum_ - 2; + } + + ///@return The slot that the module was in + unsigned int GetSlotNumber() const { return slotNum_; } + + ///@return The energy sums recorded on the module + std::vector GetEnergySums() const { return eSums_; } + + ///@return the QDC recorded on the module + std::vector GetQdc() const { return qdc_; } + + ///@return The trace that was sampled on the module + std::vector GetTrace() const { return trace_; } + + ///@brief Sets the baseline recorded on the module if the energy sums + /// were recorded in the data stream + ///@param[in] a : The value to set + void SetBaseline(const double &a) { baseline_ = a; } + + ///@brief This value is set to true if the CFD was forced to trigger + ///@param[in] a : The value to set + void SetCfdForcedTriggerBit(const bool &a) { cfdForceTrig_ = a; } + + ///@brief Sets the CFD fractional time calculated on-board + ///@param[in] a : The value to set + void SetCfdFractionalTime(const unsigned int &a) { cfdTime_ = a; } + + ///@brief Sets the CFD trigger source + ///@param[in] a : The value to set + void SetCfdTriggerSourceBit(const bool &a) { cfdTrigSource_ = a; } + + ///@brief Sets the channel number + ///@param[in] a : The value to set + void SetChannelNumber(const unsigned int &a) { chanNum_ = a; } + + ///@brief Sets the crate number + ///@param[in] a : The value to set + void SetCrateNumber(const unsigned int &a) { crateNum_ = a; } + + ///@brief Sets the energy calculated on-board + ///@param[in] a : The value to set + void SetEnergy(const double &a) { energy_ = a; } + + ///@brief Sets the energy sums calculated on-board + ///@param[in] a : The value to set + void SetEnergySums(const std::vector &a) { eSums_ = a; } + + ///@brief Sets the upper 16 bits of the event time + ///@param[in] a : The value to set + void SetEventTimeHigh(const unsigned int &a) { eventTimeHigh_ = a; } + + ///@brief Sets the lower 32 bits of the event time + ///@param[in] a : The value to set + void SetEventTimeLow(const unsigned int &a) { eventTimeLow_ = a; } + + ///@brief Sets the upper 16 bits of the external event time + ///@param[in] a : The value to set + void SetExternalTimeHigh(const unsigned int &a) { externalTimeHigh_ = a; } + + ///@brief Sets the lower 32 bits of the external event time + ///@param[in] a : The value to set + void SetExternalTimeLow(const unsigned int &a) { externalTimeLow_ = a; } + + ///@brief Sets if we had a pileup found on-board + ///@param[in] a : The value to set + void SetPileup(const bool &a) { isPileup_ = a; } + + ///@brief Sets the QDCs that were calculated on-board + ///@param[in] a : The value to set + void SetQdc(const std::vector &a) { qdc_ = a; } + + ///@brief Sets the saturation flag + ///@param[in] a : True if we found a saturation on board + void SetSaturation(const bool &a) { isSaturated_ = a; } + + ///@brief Sets the slot number + ///@param[in] a : The value to set + void SetSlotNumber(const unsigned int &a) { slotNum_ = a; } + + ///@brief Sets the calculated arrival time of the signal + ///@param[in] a : The value to set + void SetTime(const double &a) { time_ = a; } + + ///@brief Sets the calculated arrival time of the signal sans the CFD + /// fractional time components. + ///@param[in] a : The value to set + void SetTimeSansCfd(const double &a) {timeSansCfd_ = a;} + + ///@brief Sets the trace recorded on board + ///@param[in] a : The value to set + void SetTrace(const std::vector &a) { trace_ = a; } + + ///@brief Sets the flag for channels generated on-board + ///@param[in] a : True if we this channel was generated on-board + void SetVirtualChannel(const bool &a) { isVirtualChannel_ = a; } + + ///@brief Clear all variables and set them to some default values. + void Clear(); + +private: + bool cfdForceTrig_; /// CFD was forced to trigger. + bool cfdTrigSource_; /// The ADC that the CFD/FPGA synced with. + bool isPileup_; /// Pile-up flag from Pixie. + bool isSaturated_; /// Saturation flag from Pixie. + bool isVirtualChannel_; /// Flagged if generated virtually in Pixie DSP. + + double energy_; /// Raw pixie energy. + double baseline_;///Baseline that was recorded with the energy sums + double time_; ///< The time of arrival using all parts of the time + double timeSansCfd_; ///< The time of arrival of the signal sans CFD time. + + unsigned int cfdTime_; /// CFD trigger time + unsigned int chanNum_; /// Channel number. + unsigned int crateNum_; ///The Crate number for the channel + unsigned int eventTimeHigh_; /// Upper 16 bits of pixie16 event time. + unsigned int eventTimeLow_; /// Lower 32 bits of pixie16 event time. + unsigned int externalTimeHigh_; ///Upper 16 bits of external time stamp + unsigned int externalTimeLow_; ///Lower 32 bits of external time stamp + unsigned int slotNum_; ///Slot number + + std::vector eSums_;///Energy sums recorded by the module + std::vector qdc_; ///QDCs recorded by the module + std::vector trace_; /// ADC trace capture. +}; + +#endif \ No newline at end of file diff --git a/Analysis/ScanLibraries/include/XiaListModeDataDecoder.hpp b/Analysis/ScanLibraries/include/XiaListModeDataDecoder.hpp new file mode 100644 index 000000000..fc269a1f0 --- /dev/null +++ b/Analysis/ScanLibraries/include/XiaListModeDataDecoder.hpp @@ -0,0 +1,80 @@ +/// @file XiaListModeDataDecoder.hpp +/// @brief Class that handles decoding list mode data from XIA Pixie-16 +/// modules. +/// @author S. V. Paulauskas +/// @date December 23, 2016 +#ifndef PIXIESUITE_XIALISTMODEDATADECODER_HPP +#define PIXIESUITE_XIALISTMODEDATADECODER_HPP + +#include + +#include "XiaData.hpp" +#include "XiaListModeDataMask.hpp" + +///Class to decode Xia List mode Data +class XiaListModeDataDecoder { +public: + ///Default constructor + XiaListModeDataDecoder() {}; + + ///Default destructor + ~XiaListModeDataDecoder() {}; + + ///Main decoding method + ///@param[in] buf : Pointer to the beginning of the data buffer. + ///@param[in] mask : The mask set that we need to decode the data + ///@return A vector containing all of the decoded XiaData events. + std::vector DecodeBuffer(unsigned int *buf, + const XiaListModeDataMask &mask); + + ///Method to calculate the arrival time of the signal in samples + ///@param[in] mask : The data mask containing the necessary information + /// to calculate the time. + ///@param[in] data : The data that we will use to calculate the time + ///@return A pair of doubles where the first element is the time + /// calculated just using the trapezoidal filter (no CFD) and the second + /// element is the time calculated using all available CFD information. + /// If the CFD information is unavailable these two elements are identical. + static std::pair CalculateTimeInSamples( + const XiaListModeDataMask &mask, const XiaData &data); + + ///Method to calculate the arrival time of the signal in nanoseconds + ///@param[in] mask : The data mask containing the necessary information + /// to calculate the time. + ///@param[in] data : The data that we will use to calculate the time + ///@return The calculated time in nanoseconds + static double CalculateTimeInNs( + const XiaListModeDataMask &mask, const XiaData &data); +private: + ///Method to decode word zero from the header. + ///@param[in] word : The word that we need to decode + ///@param[in] data : The XiaData object that we are going to fill. + ///@return The pair of the header length and event length for use in + /// subsequent processing. + std::pair DecodeWordZero( + const unsigned int &word, XiaData &data, + const XiaListModeDataMask &mask); + + ///Method to decode word two from the header. + ///@param[in] word : The word that we need to decode + ///@param[in] data : The XiaData object that we are going to fill. + ///@param[in] mask : The data mask to decode the data + void DecodeWordTwo(const unsigned int &word, XiaData &data, + const XiaListModeDataMask &mask); + + ///Method to decode word three from the header. + ///@param[in] word : The word that we need to decode + ///@param[in] data : The XiaData object that we are going to fill. + ///@param[in] mask : The data mask to decode the data + ///@return The trace length + unsigned int DecodeWordThree(const unsigned int &word, XiaData &data, + const XiaListModeDataMask &mask); + + ///Method to decode word three from the header. + ///@param[in] word : The word that we need to decode + ///@param[in] data : The XiaData object that we are going to fill. + void DecodeTrace(unsigned int *buf, XiaData &data, + const unsigned int &traceLength); +}; + +#endif //PIXIESUITE_XIALISTMODEDATADECODER_HPP diff --git a/Analysis/ScanLibraries/include/XiaListModeDataEncoder.hpp b/Analysis/ScanLibraries/include/XiaListModeDataEncoder.hpp new file mode 100644 index 000000000..d1d82359a --- /dev/null +++ b/Analysis/ScanLibraries/include/XiaListModeDataEncoder.hpp @@ -0,0 +1,78 @@ +/// @file XiaListModeDataEncoder.hpp +/// @brief Class that handles encoding Pixie-16 list mode data from a XiaData +/// class +/// @author S. V. Paulauskas +/// @date December 30, 2016 +#ifndef PIXIESUITE_XIALISTMODEDATAENCODER_HPP +#define PIXIESUITE_XIALISTMODEDATAENCODER_HPP + +#include + +#include "XiaData.hpp" +//For the FIRMWARE enum +#include "XiaListModeDataMask.hpp" + +class XiaListModeDataEncoder { +public: + ///Default constructor + XiaListModeDataEncoder() {}; + + ///Default destructor + ~XiaListModeDataEncoder() {}; + + ///Method that will create a Pixie List Mode Data Event from an XiaData + /// object. + ///@param[in] data : The data that we want to encode + ///@param[in] firmware : The firmware version to encode into + ///@param[in] frequency : The sampling frequency in MHz or MS/s + ///@return A vector containing the encoded data. + std::vector EncodeXiaData(const XiaData &data, + const DataProcessing::FIRMWARE &firmware, + const unsigned int &frequency); + +private: + ///Encodes the first word of the data buffer. + ///@param[in] data : The data to encode + ///@param[in] mask : The object with the data masks + ///@return The encoded data word. + unsigned int EncodeWordZero(const XiaData &data, + const XiaListModeDataMask &mask); + + ///Encodes the second word of the data buffer. + ///@param[in] data : The data to encode + ///@param[in] mask : The object with the data masks + ///@return The encoded data word. + unsigned int EncodeWordOne(const XiaData &data, + const XiaListModeDataMask &mask); + + ///Encodes the third word of the data buffer. + ///@param[in] data : The data to encode + ///@param[in] mask : The object with the data masks + ///@return The encoded data word. + unsigned int EncodeWordTwo(const XiaData &data, + const XiaListModeDataMask &mask); + + ///Encodes the fourth word of the data buffer. + ///@param[in] data : The data to encode + ///@param[in] mask : The object with the data masks + ///@return The encoded data word. + unsigned int EncodeWordThree(const XiaData &data, + const XiaListModeDataMask &mask); + + ///Encodes the Trace + ///@param[in] data : The data to encode + ///@param[in] mask : The object with the data masks + ///@return The encoded data word. + std::vector EncodeTrace(const std::vector &trc, + const std::pair &mask); + + ///Encodes the Esums + ///@param[in] data : The data to encode + ///@param[in] mask : The object with the data masks + ///@return The encoded data word. + std::vector EncodeEsums(const XiaData &data, + const XiaListModeDataMask &mask); +}; + +#endif //PIXIESUITE_XIALISTMODEDATAENCODER_HPP diff --git a/Analysis/ScanLibraries/include/XiaListModeDataMask.hpp b/Analysis/ScanLibraries/include/XiaListModeDataMask.hpp new file mode 100644 index 000000000..99240464c --- /dev/null +++ b/Analysis/ScanLibraries/include/XiaListModeDataMask.hpp @@ -0,0 +1,170 @@ +/// @file XiaListModeDataMask.hpp +/// @brief Class that provides the data masks for XIA list mode data +/// @author S. V. Paulauskas +/// @date December 29, 2016 +#ifndef PIXIESUITE_XIALISTMODEDATAMASK_HPP +#define PIXIESUITE_XIALISTMODEDATAMASK_HPP + +#include +#include + +#include "HelperEnumerations.hpp" + +///A class that provides the necessary data masks and bit shifts to decode the +/// XIA Pixie-16 List Mode Data headers. To decode the data we apply the mask +/// to the 32-bit header words, then shift the result by the specified amount +/// to remove the lease significant bits. We do not include a method for the +/// Event Time Low header word (Word 1) since this value takes the entire +/// 32-bit word. The values of the bit shifts are taken from documentation +/// provided by XIA LLC. +class XiaListModeDataMask { +public: + ///Default constructor + XiaListModeDataMask() { + frequency_ = 0; + firmware_ = DataProcessing::UNKNOWN; + } + + ///Constructor accepting a FIRMWARE enum as an argument + ///@param[in] firmware : The value we want to set for the firmware + ///@param[in] freq : The value in MS/s or MHz that we want to assign to the + /// frequency. + XiaListModeDataMask(const DataProcessing::FIRMWARE &firmware, + const unsigned int &freq) { + firmware_ = firmware; + frequency_ = freq; + } + + ///Constructor accepting a string with the firmware type and the frequency + ///@param[in] firmware : The value we want to set for the firmware + ///@param[in] freq : The value in MS/s or MHz that we want to assign to the + /// frequency. + XiaListModeDataMask(const std::string &firmware, + const unsigned int &freq) { + firmware_ = ConvertStringToFirmware(firmware); + frequency_ = freq; + } + + ///Default Destructor + ~XiaListModeDataMask() {} + + ///Getter for the Mask and Shift of the Channel Number. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetChannelNumberMask() const { + return std::make_pair(0x0000000F, 0); + } + + ///Getter for the Mask and Shift of the Slot Id. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetSlotIdMask() const { + return std::make_pair(0x000000F0, 4); + } + + ///Getter for the Mask and Shift of the Crate ID. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetCrateIdMask() const { + return std::make_pair(0x00000F00, 8); + } + + ///Getter for the Mask and Shift of the Header Length. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetHeaderLengthMask() const { + return std::make_pair(0x0001F000, 12); + } + + ///Getter for the Mask and Shift of the Event Length. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetEventLengthMask() const; + + ///Getter for the Mask and Shift of the Finish Code. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetFinishCodeMask() const { + return std::make_pair(0x80000000, 31); + } + + ///Getter for the Mask and Shift of the Event Time High. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetEventTimeHighMask() const { + return std::make_pair(0x0000FFFF, 0); + } + + ///Getter for the Mask and Shift of the Event Time High. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetCfdFractionalTimeMask() const; + + ///Getter for the Mask and Shift of the Cfd Forced Trigger Bit mask. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetCfdForcedTriggerBitMask() const; + + ///Getter for the Mask and Shift of the CFD Trigger Source Bit. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetCfdTriggerSourceMask() const; + + //Getter for the CFD size + ///@return The decimal size of the CFD, i.e. 13-bit = 8192. It returns a + /// double since we're generally using this size in calculations of the + /// arrival time of the pulse. + double GetCfdSize() const; + + ///Getter for the Mask and Shift of the Energy. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetEventEnergyMask() const; + + ///Getter for the Mask and Shift of the Trace-out-of-range Flag. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetTraceOutOfRangeFlagMask() const; + + ///Getter for the Mask and Shift of the Trace Length. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetTraceLengthMask() const; + + ///Getter for the Mask and shift for the trace words. + ///@return The pair of the mask and bit shift to use to decode the data. + std::pair GetTraceMask() const { + return std::make_pair(0x0000FFFF, 16); + }; + + ///Getter for the value of the FIRMWARE so that we can test that things + /// are working as expected. + ///@return The current value of the internal firmware_ variable. + DataProcessing::FIRMWARE GetFirmware() const { return firmware_; } + + ///Getter for the value of the frequency that we're using. + ///@return The current value of the internal frequency_ variable + unsigned int GetFrequency() const { return frequency_; } + + ///Sets the firmware version + ///@param[in] firmware : The firmware type that we would like to set. + void SetFirmware(const DataProcessing::FIRMWARE &firmware) { + firmware_ = firmware; + } + + ///Sets the firmware version + ///@param[in] type : The string that we are going to convert to the + /// more useful FIRMWARE enum. + void SetFirmware(const std::string &type) { + firmware_ = ConvertStringToFirmware(type); + }; + + ///Sets the frequency of the module that we are working with. + ///@param[in] freq : The frequency of the module in MS/s or MHz that we + /// are working with. + void SetFrequency(const unsigned int &freq) { frequency_ = freq; } + + ///Converts a string to a firmware version this is used to set the + /// firmware using SetFirmware(string) method. + ///@param[in] type : A string of the firmware version that we would like. + /// It can be prepended with the "R" or not. + ///@return The firmware ENUM for the firmware type. + DataProcessing::FIRMWARE ConvertStringToFirmware(const std::string &type); + +private: + ///The firmware version that we are using. + DataProcessing::FIRMWARE firmware_; + ///The frequency of the module that we want to decode. + unsigned int frequency_; + + std::string BadMaskErrorMessage(const std::string &func) const; +}; + +#endif //PIXIESUITE_XIALISTMODEDATAMASK_HPP diff --git a/Analysis/ScanLibraries/source/CMakeLists.txt b/Analysis/ScanLibraries/source/CMakeLists.txt new file mode 100644 index 000000000..1f8288118 --- /dev/null +++ b/Analysis/ScanLibraries/source/CMakeLists.txt @@ -0,0 +1,28 @@ +#Set the scan sources that we will make a lib out of +set(PaassScanSources ScanInterface.cpp Unpacker.cpp + XiaData.cpp XiaListModeDataMask.cpp XiaListModeDataDecoder.cpp + XiaListModeDataEncoder.cpp) + +if(PAASS_USE_ROOT) + list(APPEND PaassScanSources RootScanner.cpp) +endif(PAASS_USE_ROOT) + +#Add the sources to the library +add_library(PaassScanObjects OBJECT ${PaassScanSources}) + +if(PAASS_BUILD_SHARED_LIBS) + message(STATUS "Building Scan Shared Objects") + add_library(PaassScan SHARED $) + target_link_libraries(PaassScan PaassCoreStatic ${CMAKE_THREAD_LIBS_INIT}) + if (${CURSES_FOUND}) + target_link_libraries(PaassScan ${CURSES_LIBRARIES}) + endif(${CURSES_FOUND}) + install(TARGETS PaassScan DESTINATION lib) +endif(PAASS_BUILD_SHARED_LIBS) + +#Create PixieScan static library and add ncurses if we have it +add_library(PaassScanStatic STATIC $) +target_link_libraries(PaassScanStatic PaassCoreStatic ${CMAKE_THREAD_LIBS_INIT}) +if (${CURSES_FOUND}) + target_link_libraries(PaassScanStatic ${CURSES_LIBRARIES}) +endif(${CURSES_FOUND}) diff --git a/Analysis/ScanLibraries/source/RootScanner.cpp b/Analysis/ScanLibraries/source/RootScanner.cpp new file mode 100644 index 000000000..2be64fdee --- /dev/null +++ b/Analysis/ScanLibraries/source/RootScanner.cpp @@ -0,0 +1,189 @@ +#include "RootScanner.hpp" + +#include + +#include "TSystem.h" +#include "TList.h" +#include "TObject.h" +#include "TAxis.h" +#include "TH1.h" +#include "TH2.h" +#include "TGraph.h" + +RootScanner::RootScanner() : + ScanInterface() +{ + new TApplication("scanner", 0, NULL); + + canvas_ = new TCanvas("canvas", ""); +} +RootScanner::~RootScanner() { + canvas_->Close(); + delete canvas_; +} + +/** IdleTask is called whenever a scan is not busy doing things. This method + * may be used to update things which need to be updated every so often + * (e.g. a root TCanvas). + * \return Nothing. + */ +void RootScanner::IdleTask() { + gSystem->ProcessEvents(); + usleep(100000); +} + +void RootScanner::ResetZoom(TVirtualPad *pad /*= gPad*/) { + AxisInfo* padZoomInfo = &zoomInfo_[pad]; + for (int i=0;ilimitMin[i] = std::numeric_limits::max(); + padZoomInfo->limitMax[i] = std::numeric_limits::min(); + padZoomInfo->rangeUserMin[i] = std::numeric_limits::max(); + padZoomInfo->rangeUserMax[i] = std::numeric_limits::min(); + } + padZoomInfo->reset = true; +} + +//Update the zoom levels on a pad. +void RootScanner::UpdateZoom(TVirtualPad *pad /*= gPad*/) { + //Get zoom info for this pad. + auto itr = zoomInfo_.find(pad); + if (itr == zoomInfo_.end()) ResetZoom(pad); + AxisInfo* padZoomInfo = &zoomInfo_[pad]; + + //If zoom has been reset we continue, otherwise we get current axis limits. + if (padZoomInfo->reset) padZoomInfo->reset = false; + else { + //Get the user zoom settings. + padZoomInfo->rangeUserMin[0] = pad->GetUxmin(); + padZoomInfo->rangeUserMax[0] = pad->GetUxmax(); + padZoomInfo->rangeUserMin[1] = pad->GetUymin(); + padZoomInfo->rangeUserMax[1] = pad->GetUymax(); +// padZoomInfo->rangeUserMin[2] = pad->GetUzmin(); +// padZoomInfo->rangeUserMax[2] = pad->GetUzmax(); + } + + //Determine if the user had zoomed or unzoomed by comparing the current axis + // limits to those taken from the canvas. + bool userZoom[numAxes_]; + for (int i=0; irangeUserMin[i] > padZoomInfo->limitMin[i] || + padZoomInfo->rangeUserMax[i] < padZoomInfo->limitMax[i]); + } + + //Get the list of items on the pad. + TList *list = gPad->GetListOfPrimitives(); + + bool limitChange = false; + + //Loop over the objects in the list to determine pad limits. + for( TObject *obj = list->First(); obj; obj = list->After(obj)) { + TAxis *xAxis, *yAxis, *zAxis = NULL; //Pointers to the axes. + + //Check if the object is a histogram + if ( TH1* hist = dynamic_cast(obj) ) { + xAxis = hist->GetXaxis(); + yAxis = hist->GetYaxis(); + + //Check if hist is 2D + if (dynamic_cast(hist) ) { + zAxis = hist->GetZaxis(); + } + else{ + if (hist->GetBinContent(hist->GetMaximumBin()) * 1.1 > padZoomInfo->limitMax[1]) { + padZoomInfo->limitMax[1] = 1.1 * hist->GetBinContent(hist->GetMaximumBin()); + } + } + } + //Check if the object is a graph + else if ( TGraph* graph = dynamic_cast(obj) ) { + xAxis = graph->GetXaxis(); + yAxis = graph->GetYaxis(); + } + //Not an object we care about so we continue. + else continue; + + //If the axis min / max are outside current stored values then we update + // the values. + if (xAxis->GetXmin() < padZoomInfo->limitMin[0]) { + padZoomInfo->limitMin[0] = xAxis->GetXmin(); + limitChange = true; + } + if (xAxis->GetXmax() > padZoomInfo->limitMax[0]) { + padZoomInfo->limitMax[0] = xAxis->GetXmax(); + limitChange = true; + } + if (yAxis->GetXmin() < padZoomInfo->limitMin[1]) { + padZoomInfo->limitMin[1] = yAxis->GetXmin(); + limitChange = true; + } + if (yAxis->GetXmax() > padZoomInfo->limitMax[1]) { + padZoomInfo->limitMax[1] = yAxis->GetXmax(); + limitChange = true; + } + if (zAxis) { + if (zAxis->GetXmin() < padZoomInfo->limitMin[1]) { + padZoomInfo->limitMin[1] = zAxis->GetXmin(); + limitChange = true; + } + if (yAxis->GetXmax() > padZoomInfo->limitMax[1]) { + padZoomInfo->limitMax[1] = zAxis->GetXmax(); + limitChange = true; + } + + } + + } + + //If the user didn't zoom we store the current axis limits in the userZoom + // values. + for (int axis = 0; axis < 3; axis++) { + if (!userZoom[axis]) { + padZoomInfo->rangeUserMin[axis] = padZoomInfo->limitMin[axis]; + padZoomInfo->rangeUserMax[axis] = padZoomInfo->limitMax[axis]; + } + } + + //Loop over the objects again and set the proper limits for each item. + for( TObject *obj = list->First(); obj; obj = list->After(obj)) { + TAxis *xAxis, *yAxis; //Pointers to the axes. + //Check if the object is a histogram + if ( TH1* hist = dynamic_cast(obj) ) { + xAxis = hist->GetXaxis(); + yAxis = hist->GetYaxis(); + + if (limitChange) { + //Set the axes limits + xAxis->SetLimits(padZoomInfo->limitMin[0], padZoomInfo->limitMax[0]); + yAxis->SetLimits(padZoomInfo->limitMin[1], padZoomInfo->limitMax[1]); + + //Check if hist is 2D + if (dynamic_cast(hist) ) { + TAxis *zAxis = hist->GetZaxis(); + zAxis->SetLimits(padZoomInfo->limitMin[2], padZoomInfo->limitMax[2]); + } + //We assume if not 2D then its 1D. + else { + //Set the histogram maximum + hist->SetMaximum(padZoomInfo->limitMax[1]); + } + } + + } + //Check if the object is a graph + else if ( TGraph* graph = dynamic_cast(obj) ) { + xAxis = graph->GetXaxis(); + yAxis = graph->GetYaxis(); + } + else continue; + + //Set the range of the axis to the determined userZoom values. + xAxis->SetRangeUser(padZoomInfo->rangeUserMin[0], padZoomInfo->rangeUserMax[0]); + yAxis->SetRangeUser(padZoomInfo->rangeUserMin[1], padZoomInfo->rangeUserMax[1]); + } + + if (limitChange) { + pad->Modified(); + } + +} + diff --git a/Scan/ScanLib/source/ScanInterface.cpp b/Analysis/ScanLibraries/source/ScanInterface.cpp similarity index 91% rename from Scan/ScanLib/source/ScanInterface.cpp rename to Analysis/ScanLibraries/source/ScanInterface.cpp index 94bd47e7b..b5ca46517 100644 --- a/Scan/ScanLib/source/ScanInterface.cpp +++ b/Analysis/ScanLibraries/source/ScanInterface.cpp @@ -12,6 +12,7 @@ */ #include #include +#include #include #include @@ -333,6 +334,17 @@ void ScanInterface::SyntaxStr(char *name_){ std::cout << " usage: " << name_ << " [options]\n"; } +void ScanInterface::SetOutputInformation(const std::string &a) { + unsigned long found = a.find_last_of("/"); + if(found == std::string::npos) { + outputFilename_ = a; + outputPath_ = "./"; + } else { + outputFilename_ = a.substr(found+1, a.size()); + outputPath_ = a.substr(0, found+1); + } +} + /** Initialize the Unpacker object. * Does nothing useful by default. * \param[in] prefix_ String to append to the beginning of system output. @@ -386,6 +398,11 @@ ScanInterface::ScanInterface(Unpacker *core_/*=NULL*/){ scan_init = false; file_open = false; + //Initialize the setup and output file names and path + outputFilename_ = ""; + outputPath_ = ""; + setup_filename = ""; + kill_all = false; run_ctrl_exit = false; @@ -396,22 +413,28 @@ ScanInterface::ScanInterface(Unpacker *core_/*=NULL*/){ if(core_){ core = core_; } else{ core = NULL; } - // Push back all of the arguments. Annoying, but we only need to do this once. - baseOpts.push_back(optionExt("batch", no_argument, NULL, 'b', "", "Run in batch mode (i.e. with no command line)")); - baseOpts.push_back(optionExt("config", required_argument, NULL, 'c', - "", "Specify path to setup to use for scan")); - baseOpts.push_back(optionExt("counts", no_argument, NULL, 0, "", "Write all recorded channel counts to a file")); - baseOpts.push_back(optionExt("debug", no_argument, NULL, 0, "", "Enable readout debug mode")); - baseOpts.push_back(optionExt("dry-run", no_argument, NULL, 0, "", "Extract spills from file, but do no processing")); - baseOpts.push_back(optionExt("fast-fwd", required_argument, NULL, 0, "", "Skip ahead to a specified word in the file (start of file at zero)")); - baseOpts.push_back(optionExt("help", no_argument, NULL, 'h', "", "Display this dialogue")); - baseOpts.push_back(optionExt("input", required_argument, NULL, 'i', "", "Specifies the input file to analyze")); - baseOpts.push_back(optionExt("output", required_argument, NULL, 'o', "", "Specifies the name of the output file. Default is \"out\"")); - baseOpts.push_back(optionExt("quiet", no_argument, NULL, 'q', "", "Toggle off verbosity flag")); - baseOpts.push_back(optionExt("shm", no_argument, NULL, 's', "", "Enable shared memory readout")); - baseOpts.push_back(optionExt("version", no_argument, NULL, 'v', "", "Display version information")); - - optstr = "bc:hi:o:qsv"; + //Setup all the arguments that are known to the program. + baseOpts = { + optionExt("batch", no_argument, NULL, 'b', "", "Run in batch mode (i.e. with no command line)"), + optionExt("config", required_argument, NULL, 'c', "", "Specify path to setup to use for scan"), + optionExt("counts", no_argument, NULL, 0, "", "Write all recorded channel counts to a file"), + optionExt("debug", no_argument, NULL, 0, "", "Enable readout debug mode"), + optionExt("dry-run", no_argument, NULL, 0, "", "Extract spills from file, but do no processing"), + optionExt("fast-fwd", required_argument, NULL, 0, "", "Skip ahead to a specified word in the file (start of file at zero)"), + optionExt("firmware", required_argument, NULL, 'f', "", + "Sets the firmware revision for decoding the data. " + "See the wiki or HelperEnumerations.hpp " + "for more information."), + optionExt("frequency", required_argument, NULL, 0, "", "Specifies the sampling frequency used to collect the data."), + optionExt("help", no_argument, NULL, 'h', "", "Display this dialogue"), + optionExt("input", required_argument, NULL, 'i', "", "Specifies the input file to analyze"), + optionExt("output", required_argument, NULL, 'o', "", "Specifies the name of the output file. Default is \"out\""), + optionExt("quiet", no_argument, NULL, 'q', "", "Toggle off verbosity flag"), + optionExt("shm", no_argument, NULL, 's', "", "Enable shared memory readout"), + optionExt("version", no_argument, NULL, 'v', "", "Display version information") + }; + + optstr = "bc:f:hi:o:qsv"; progName = std::string(PROG_NAME); msgHeader = progName + ": "; @@ -461,7 +484,7 @@ void ScanInterface::RunControl(){ bool full_spill = false; while(true){ - if(kill_all == true){ + if(kill_all == true){ break; } else if(!is_running){ @@ -832,6 +855,8 @@ bool ScanInterface::Setup(int argc, char *argv[]){ dry_run_mode = false; shm_mode = false; num_spills_recvd = 0; + unsigned int samplingFrequency = 0; + std::string firmware = ""; std::string input_filename = ""; // Add derived class options to the option list. @@ -872,6 +897,10 @@ bool ScanInterface::Setup(int argc, char *argv[]){ else if(strcmp("fast-fwd", longOpts[idx].name) == 0) { file_start_offset = atoll(optarg); } + else if(strcmp("frequency", longOpts[idx].name) == 0) + samplingFrequency = (unsigned int)std::stoi(optarg); + else if(strcmp("firmware", longOpts[idx].name) == 0) + firmware = optarg; else{ for(std::vector::iterator iter = userOpts.begin(); iter != userOpts.end(); iter++){ if(strcmp(iter->name, longOpts[idx].name) == 0){ @@ -894,6 +923,9 @@ bool ScanInterface::Setup(int argc, char *argv[]){ case 'c' : setup_filename = optarg; break; + case 'f' : + firmware = optarg; + break; case 'h' : help(argv[0]); return false; @@ -901,7 +933,7 @@ bool ScanInterface::Setup(int argc, char *argv[]){ input_filename = optarg; break; case 'o' : - output_filename = optarg; + SetOutputInformation(optarg); break; case 'q' : is_verbose = false; @@ -938,6 +970,19 @@ bool ScanInterface::Setup(int argc, char *argv[]){ // Link this object to the Unpacker for cross-calls. core->SetInterface(this); + //Initialize the data mask for decoding the data + ///@TODO We need to be able to handle mixed systems, which is not + /// implemented yet. + if(samplingFrequency == 0 || firmware == "") { + if (samplingFrequency == 0) + throw std::invalid_argument( + "ScanInterface::Setup - The frequency has not been set."); + if(firmware == "") + throw std::invalid_argument("ScanInterface::Setup - The firmware " + "has not been set."); + } else + core->InitializeDataMask(firmware, samplingFrequency); + if(debug_mode) core->SetDebugMode(); @@ -1090,6 +1135,8 @@ bool ScanInterface::Close(){ * \param[out] prefix The input filename path without the file extension. * \return The file extension string. */ +///@TODO This method needs cleaned up signficantly. This should only take +/// about 4 lines of code if using string::find_last_of() std::string get_extension(std::string filename_, std::string &prefix){ size_t count = 0; size_t last_index = 0; diff --git a/Scan/ScanLib/source/Unpacker.cpp b/Analysis/ScanLibraries/source/Unpacker.cpp similarity index 57% rename from Scan/ScanLib/source/Unpacker.cpp rename to Analysis/ScanLibraries/source/Unpacker.cpp index 7668425fa..080f78130 100644 --- a/Scan/ScanLib/source/Unpacker.cpp +++ b/Analysis/ScanLibraries/source/Unpacker.cpp @@ -1,41 +1,40 @@ /** \file Unpacker.cpp * \brief A class to handle the unpacking of UTK/ORNL style pixie16 data spills. * - * This class is intended to be used as a replacement of pixiestd.cpp from Stan - * Paulauskas's pixie_scan. The majority of function names and arguments are + * This class is intended to be used as a replacement of PixieStd.cpp from + * pixie_scan. The majority of function names and arguments are * preserved as much as possible while allowing for more standardized unpacking * of pixie16 data. - * CRT * * \author C. R. Thornsberry * \date Feb. 12th, 2016 */ -#include -#include -#include -#include -#include -#include #include +#include +#include #include +#include + #include "Unpacker.hpp" #include "XiaData.hpp" +#include "XiaListModeDataDecoder.hpp" + +using namespace std; -void clearDeque(std::deque &list){ +void clearDeque(deque &list){ while(!list.empty()){ delete list.front(); list.pop_front(); } } -/** Scan the event list and sort it by timestamp. - * \return Nothing. - */ +///Scan the event list and sort it by timestamp. +/// @return Nothing. void Unpacker::TimeSort(){ - for(std::vector >::iterator iter = eventList.begin(); iter != eventList.end(); iter++){ - sort(iter->begin(), iter->end(), &XiaData::compareTime); - } + for(vector >::iterator iter = eventList.begin(); + iter != eventList.end(); iter++) + sort(iter->begin(), iter->end(), &XiaData::CompareTime); } /** Scan the time sorted event list and package the events into a raw @@ -65,32 +64,34 @@ bool Unpacker::BuildRawEvent(){ realStopTime = eventStartTime; unsigned int mod, chan; - std::string type, subtype, tag; + string type, subtype, tag; XiaData *current_event = NULL; - // Loop over all time-sorted modules. - for(std::vector >::iterator iter = eventList.begin(); iter != eventList.end(); iter++){ + // Loop over all time-sorted modules. + for(vector >::iterator iter = eventList.begin(); iter != eventList.end(); iter++){ if(iter->empty()) continue; // Loop over the list of channels that fired in this buffer while(!iter->empty()){ current_event = iter->front(); - mod = current_event->modNum; - chan = current_event->chanNum; + mod = current_event->GetModuleNumber(); + chan = current_event->GetChannelNumber(); if(mod > MAX_PIXIE_MOD || chan > MAX_PIXIE_CHAN){ // Skip this channel - std::cout << "BuildRawEvent: Encountered non-physical Pixie ID (mod = " << mod << ", chan = " << chan << ")\n"; + cout << "BuildRawEvent: Encountered non-physical Pixie ID (mod = " << mod << ", chan = " << chan << ")\n"; delete current_event; iter->pop_front(); continue; } - double currtime = current_event->time; + double currtime = current_event->GetTime(); // Check for backwards time-skip. This is un-handled currently and needs fixed CRT!!! if(currtime < eventStartTime) - std::cout << "BuildRawEvent: Detected backwards time-skip from start=" << eventStartTime << " to " << current_event->time << "???\n"; + cout << "BuildRawEvent: Detected backwards time-skip from " + "start=" << eventStartTime << " to " + << current_event->GetTime() << "???\n"; // If the time difference between the current and previous event is // larger than the event width, finalize the current event, otherwise @@ -129,16 +130,16 @@ bool Unpacker::BuildRawEvent(){ * \return True if the XiaData's module number is valid and false otherwise. */ bool Unpacker::AddEvent(XiaData *event_){ - if(event_->modNum > MAX_PIXIE_MOD){ return false; } + if(event_->GetModuleNumber() > MAX_PIXIE_MOD){ return false; } // Check for the need to add a new deque to the event list. - if(event_->modNum+1 > (unsigned int)eventList.size()){ - while (eventList.size() < event_->modNum + 1) { + if(event_->GetModuleNumber()+1 > (unsigned int)eventList.size()){ + while (eventList.size() < event_->GetModuleNumber() + 1) { eventList.push_back(std::deque()); } } - eventList.at(event_->modNum).push_back(event_); + eventList.at(event_->GetModuleNumber()).push_back(event_); return true; } @@ -173,8 +174,8 @@ bool Unpacker::GetFirstTime(double &time){ for(std::vector >::iterator iter = eventList.begin(); iter != eventList.end(); iter++){ if(iter->empty()) continue; - if(iter->front()->time < time) - time = iter->front()->time; + if(iter->front()->GetTime() < time) + time = iter->front()->GetTime(); } return true; @@ -199,162 +200,25 @@ void Unpacker::ProcessRawEvent(ScanInterface *addr_/*=NULL*/){ ClearRawEvent(); } -/** Called form ReadSpill. Scan the current spill and construct a list of - * events which fired by obtaining the module, channel, trace, etc. of the - * timestamped event. This method will construct the event list for - * later processing. - * \param[in] buf Pointer to an array of unsigned ints containing raw buffer data. - * \param[out] bufLen The number of words in the buffer. - * \return The number of XiaDatas read from the buffer. - */ -int Unpacker::ReadBuffer(unsigned int *buf, unsigned long &bufLen){ - // multiplier for high bits of 48-bit time - static const double HIGH_MULT = pow(2., 32.); - - unsigned int modNum; - unsigned long numEvents = 0; - unsigned int *bufStart = buf; - - // Determine the number of words in the buffer - bufLen = *buf++; - - // Read the module number - modNum = *buf++; - - XiaData *lastVirtualChannel = NULL; - - if(bufLen > 0){ // Check if the buffer has data - if(bufLen == 2){ // this is an empty channel - return 0; - } - while( buf < bufStart + bufLen ){ - XiaData *currentEvt = new XiaData(); - - // decoding event data... see pixie16app.c - // buf points to the start of channel data - unsigned int chanNum = (buf[0] & 0x0000000F); - unsigned int slotNum = (buf[0] & 0x000000F0) >> 4; - unsigned int crateNum = (buf[0] & 0x00000F00) >> 8; - unsigned int headerLength = (buf[0] & 0x0001F000) >> 12; - unsigned int eventLength = (buf[0] & 0x1FFE0000) >> 17; - - currentEvt->virtualChannel = ((buf[0] & 0x20000000) != 0); - currentEvt->saturatedBit = ((buf[0] & 0x40000000) != 0); - currentEvt->pileupBit = ((buf[0] & 0x80000000) != 0); - - // Rev. D header lengths not clearly defined in pixie16app_defs - //! magic numbers here for now - if(headerLength == 1){ - // this is a manual statistics block inserted by the poll program - /*stats.DoStatisticsBlock(&buf[1], modNum); - buf += eventLength; - numEvents = -10;*/ - continue; - } - if(headerLength != 4 && headerLength != 8 && headerLength != 12 && headerLength != 16){ - std::cout << "ReadBuffer: Unexpected header length: " << headerLength << std::endl; - std::cout << "ReadBuffer: Buffer " << modNum << " of length " << bufLen << std::endl; - std::cout << "ReadBuffer: CHAN:SLOT:CRATE " << chanNum << ":" << slotNum << ":" << crateNum << std::endl; - // advance to next event and continue - // buf += EventLength; - // continue; - - // skip the rest of this buffer - return numEvents; - } - - unsigned int lowTime = buf[1]; - unsigned int highTime = buf[2] & 0x0000FFFF; - unsigned int cfdTime = (buf[2] & 0xFFFF0000) >> 16; - unsigned int energy = buf[3] & 0x0000FFFF; - unsigned int traceLength = (buf[3] & 0xFFFF0000) >> 16; - - if(headerLength == 8 || headerLength == 16){ - // Skip the onboard partial sums for now - // trailing, leading, gap, baseline - } - - if(headerLength >= 12){ - int offset = headerLength - 8; - for (int i=0; i < currentEvt->numQdcs; i++){ - currentEvt->qdcValue[i] = buf[offset + i]; - } - } - - // One last check - if( traceLength / 2 + headerLength != eventLength ){ - std::cout << "ReadBuffer: Bad event length (" << eventLength << ") does not correspond with length of header ("; - std::cout << headerLength << ") and length of trace (" << traceLength << ")" << std::endl; - buf += eventLength; - continue; - } - - // Handle multiple crates - modNum += 100 * crateNum; - - currentEvt->chanNum = chanNum; - currentEvt->modNum = modNum; - /*if(currentEvt->virtualChannel){ - DetectorLibrary* modChan = DetectorLibrary::get(); - - currentEvt->modNum += modChan->GetPhysicalModules(); - if(modChan->at(modNum, chanNum).HasTag("construct_trace")){ - lastVirtualChannel = currentEvt; - } - }*/ - - channel_counts[modNum][chanNum]++; - - currentEvt->energy = energy; - if(currentEvt->saturatedBit){ currentEvt->energy = 16383; } - - currentEvt->trigTime = lowTime; - currentEvt->cfdTime = cfdTime; - currentEvt->eventTimeHi = highTime; - currentEvt->eventTimeLo = lowTime; - currentEvt->time = highTime * HIGH_MULT + lowTime; - - buf += headerLength; - // Check if trace data follows the channel header - if( traceLength > 0 ){ - // sbuf points to the beginning of trace data - unsigned short *sbuf = (unsigned short *)buf; - - currentEvt->reserve(traceLength); - - /*if(currentEvt->saturatedBit) - currentEvt->trace.SetValue("saturation", 1);*/ - - if( lastVirtualChannel != NULL && lastVirtualChannel->adcTrace.empty() ){ - lastVirtualChannel->assign(traceLength, 0); - } - // Read the trace data (2-bytes per sample, i.e. 2 samples per word) - for(unsigned int k = 0; k < traceLength; k ++){ - currentEvt->push_back(sbuf[k]); - - if(lastVirtualChannel != NULL){ - lastVirtualChannel->adcTrace[k] += sbuf[k]; - } - } - buf += traceLength / 2; - } - - AddEvent(currentEvt); - - numEvents++; - } - } - else{ // if buffer has data - std::cout << "ReadBuffer: ERROR IN ReadBuffData, LIST UNKNOWN" << std::endl; - return -100; - } - - return numEvents; +///Called form ReadSpill. Scan the current spill and construct a list of +///events which fired by obtaining the module, channel, trace, etc. of the +///timestamped event. This method will construct the event list for +///later processing. +///@param[in] buf : Pointer to an array of unsigned ints containing raw +/// buffer data. +///@return The number of XiaDatas read from the buffer. +int Unpacker::ReadBuffer(unsigned int *buf){ + static XiaListModeDataDecoder decoder; + decodedList_ = decoder.DecodeBuffer(buf, mask_); + for (vector::iterator it = decodedList_.begin(); + it != decodedList_.end(); it++) + AddEvent(*it); + return (int)decodedList_.size(); } Unpacker::Unpacker() : eventWidth(62), // ~ 500 ns in 8 ns pixie clock ticks. - debug_mode(false), + debug_mode(false), running(true), interface(NULL), TOTALREAD(1000000), // Maximum number of data words to read. @@ -386,17 +250,13 @@ Unpacker::~Unpacker(){ * \param[in] is_verbose Toggle the verbosity flag on/off. * \return True if the spill was read successfully and false otherwise. */ -bool Unpacker::ReadSpill(unsigned int *data, unsigned int nWords, bool is_verbose/*=true*/){ +bool Unpacker::ReadSpill(unsigned int *data, unsigned int nWords, + bool is_verbose/*=true*/){ const unsigned int maxVsn = 14; // No more than 14 pixie modules per crate unsigned int nWords_read = 0; - - //static clock_t clockBegin; // Initialization time - //time_t tmsBegin; int retval = 0; // return value from various functions - - unsigned long bufLen; - + // Various event counters unsigned long numEvents = 0; static int counter = 0; // the number of times this function is called @@ -423,14 +283,14 @@ bool Unpacker::ReadSpill(unsigned int *data, unsigned int nWords, bool is_verbos // Check sanity of record length and vsn if(lenRec > maxWords || (vsn > maxVsn && vsn != 9999 && vsn != 1000)){ if(is_verbose){ - std::cout << "ReadSpill: SANITY CHECK FAILED: lenRec = " << lenRec << ", vsn = " << vsn << ", read " << nWords_read << " of " << nWords << std::endl; + cout << "ReadSpill: SANITY CHECK FAILED: lenRec = " << lenRec << ", vsn = " << vsn << ", read " << nWords_read << " of " << nWords << endl; } return false; } // If the record length is 6, this is an empty channel. // Skip this vsn and continue with the next - //! Revision specific, so move to ReadBuffData + ///@TODO Revision specific, so move to ReadBuffData if(lenRec==6){ nWords_read += lenRec; lastVsn=vsn; @@ -442,7 +302,7 @@ bool Unpacker::ReadSpill(unsigned int *data, unsigned int nWords, bool is_verbos if(vsn < maxVsn){ if(lastVsn != 0xFFFFFFFF && vsn != lastVsn+1){ if(is_verbose){ - std::cout << "ReadSpill: MISSING BUFFER " << lastVsn+1 << ", lastVsn = " << lastVsn << ", vsn = " << vsn << ", lenrec = " << lenRec << std::endl; + cout << "ReadSpill: MISSING BUFFER " << lastVsn+1 << ", lastVsn = " << lastVsn << ", vsn = " << vsn << ", lenrec = " << lenRec << endl; } ClearEventList(); fullSpill=false; // WHY WAS THIS TRUE!?!? CRT @@ -450,15 +310,15 @@ bool Unpacker::ReadSpill(unsigned int *data, unsigned int nWords, bool is_verbos // Read the buffer. After read, the vector eventList will //contain pointers to all channels that fired in this buffer - retval = ReadBuffer(&data[nWords_read], bufLen); + retval = ReadBuffer(&data[nWords_read]); // If the return value is less than the error code, //reading the buffer failed for some reason. //Print error message and reset variables if necessary if(retval <= -100){ - if(is_verbose){ std::cout << "ReadSpill: READOUT PROBLEM " << retval << " in event " << counter << std::endl; } + if(is_verbose){ cout << "ReadSpill: READOUT PROBLEM " << retval << " in event " << counter << endl; } if(retval == -100){ - if(is_verbose){ std::cout << "ReadSpill: Remove list " << lastVsn << " " << vsn << std::endl; } + if(is_verbose){ cout << "ReadSpill: Remove list " << lastVsn << " " << vsn << endl; } ClearEventList(); } return false; @@ -478,7 +338,7 @@ bool Unpacker::ReadSpill(unsigned int *data, unsigned int nWords, bool is_verbos if(is_verbose){ /*struct tm * timeinfo; timeinfo = localtime (&theTime); - std::cout << "ReadSpill: Read wall clock time of " << asctime(timeinfo);*/ + cout << "ReadSpill: Read wall clock time of " << asctime(timeinfo);*/ } nWords_read += lenRec; continue; @@ -490,13 +350,13 @@ bool Unpacker::ReadSpill(unsigned int *data, unsigned int nWords, bool is_verbos else{ // Bail out if we have lost our place, // (bad vsn) and process events - std::cout << "ReadSpill: UNEXPECTED VSN " << vsn << std::endl; + cout << "ReadSpill: UNEXPECTED VSN " << vsn << endl; break; } } // while still have words if(nWords > TOTALREAD || nWords_read > TOTALREAD){ - std::cout << "ReadSpill: Values of nn - " << nWords << " nk - "<< nWords_read << " TOTALREAD - " << TOTALREAD << std::endl; + cout << "ReadSpill: Values of nn - " << nWords << " nk - "<< nWords_read << " TOTALREAD - " << TOTALREAD << endl; return false; } @@ -510,7 +370,7 @@ bool Unpacker::ReadSpill(unsigned int *data, unsigned int nWords, bool is_verbos // Check the number of read words if(is_verbose && nWords_read != nWords){ - std::cout << "ReadSpill: Received spill of " << nWords << " words, but read " << nWords_read << " words\n"; + cout << "ReadSpill: Received spill of " << nWords << " words, but read " << nWords_read << " words\n"; } // If there are events to process, continue @@ -540,17 +400,17 @@ bool Unpacker::ReadSpill(unsigned int *data, unsigned int nWords, bool is_verbos // Every once in a while (when evcount is a multiple of 1000) // print the time elapsed doing the analysis if((evCount % 1000 == 0 || evCount == 1) && theTime != 0){ - std::cout << std::endl << "ReadSpill: Data read up to poll status time " << ctime(&theTime); + cout << endl << "ReadSpill: Data read up to poll status time " << ctime(&theTime); } } else { - if(is_verbose){ std::cout << "ReadSpill: Spill split between buffers" << std::endl; } + if(is_verbose){ cout << "ReadSpill: Spill split between buffers" << endl; } ClearEventList(); // This tosses out all events read into the deque so far return false; } } else if(retval != -10){ - if(is_verbose){ std::cout << "ReadSpill: bad buffer, numEvents = " << numEvents << std::endl; } + if(is_verbose){ cout << "ReadSpill: bad buffer, numEvents = " << numEvents << endl; } ClearEventList(); // This tosses out all events read into the deque so far return false; } @@ -566,7 +426,7 @@ void Unpacker::Write(){ if(count_output.good()){ for(unsigned int i = 0; i <= MAX_PIXIE_MOD; i++){ for(unsigned int j = 0; j <= MAX_PIXIE_CHAN; j++){ - count_output << i << "\t" << j << "\t" << channel_counts[i][j] << std::endl; + count_output << i << "\t" << j << "\t" << channel_counts[i][j] << endl; } } count_output.close(); diff --git a/Analysis/ScanLibraries/source/XiaData.cpp b/Analysis/ScanLibraries/source/XiaData.cpp new file mode 100644 index 000000000..90527e606 --- /dev/null +++ b/Analysis/ScanLibraries/source/XiaData.cpp @@ -0,0 +1,23 @@ +///@file XiaData.cpp +///@brief A class that holds information from the XIA LLC. Pixie-16 List +/// Mode Data +///@authors C. R. Thornsberry and S. V. Paulauskas +#include "XiaData.hpp" + +///Clears all of the variables. The vectors are all cleared using the clear() +/// method. This method is called when the class is first initalizied so that +/// it has some default values for the software to use in the event that they +/// are needed. +void XiaData::Clear(){ + cfdForceTrig_ = cfdTrigSource_ = isPileup_ = isSaturated_ = false; + isVirtualChannel_ = false; + + energy_ = baseline_ = 0.0; + + chanNum_ = crateNum_ = slotNum_ = cfdTime_ = 0; + eventTimeHigh_ = eventTimeLow_ = externalTimeLow_ = externalTimeHigh_ = 0; + + eSums_.clear(); + qdc_.clear(); + trace_.clear(); +} \ No newline at end of file diff --git a/Analysis/ScanLibraries/source/XiaListModeDataDecoder.cpp b/Analysis/ScanLibraries/source/XiaListModeDataDecoder.cpp new file mode 100644 index 000000000..9de752e1a --- /dev/null +++ b/Analysis/ScanLibraries/source/XiaListModeDataDecoder.cpp @@ -0,0 +1,280 @@ +/// @file XiaListModeDataDecoder.hpp +/// @brief Class that handles decoding list mode data from XIA Pixie-16 +/// modules. +/// @author S. V. Paulauskas +/// @date December 23, 2016 +#include +#include +#include + +#include + +#include "HelperEnumerations.hpp" +#include "XiaListModeDataDecoder.hpp" + +using namespace std; +using namespace DataProcessing; + +vector XiaListModeDataDecoder::DecodeBuffer( + unsigned int *buf, const XiaListModeDataMask &mask) { + + unsigned int *bufStart = buf; + ///@NOTE : These two pieces here are the Pixie Module Data Header. They + /// tell us the number of words read from the module (bufLen) and the VSN + /// of the module (module number). + unsigned int bufLen = *buf++; + unsigned int modNum = *buf++; + + //A buffer length of zero is an issue, we'll throw a length error. + if (bufLen == 0) + throw length_error("Unpacker::ReadBuffer - The buffer length was " + "sized 0. This is a huge issue."); + + //For empty buffers we just return an empty vector. + static const unsigned int emptyBufferLength = 2; + if (bufLen == emptyBufferLength) + return vector(); + + stringstream msg; + vector events; + static unsigned int numSkippedBuffers = 0; + + while (buf < bufStart + bufLen) { + XiaData *data = new XiaData(); + bool hasExternalTimestamp = false; + bool hasQdc = false; + bool hasEnergySums = false; + + pair lengths = + DecodeWordZero(buf[0], *data, mask); + unsigned int headerLength = lengths.first; + unsigned int eventLength = lengths.second; + + data->SetEventTimeLow(buf[1]); + DecodeWordTwo(buf[2], *data, mask); + unsigned int traceLength = DecodeWordThree(buf[3], *data, mask); + + //We check the header length here to set the appropriate flags for + // processing the rest of the header words. If we encounter a header + // length that we do not know we will throw an error as this + // generally indicates an issue with the data file or processing at a + // higher level. + switch (headerLength) { + case STATS_BLOCK : // Manual statistics block inserted by poll + // this is a manual statistics block inserted by the poll program + /*stats.DoStatisticsBlock(&buf[1], modNum); + buf += eventLength; + numEvents = -10;*/ + continue; + case HEADER : + break; + case HEADER_W_ETS : + hasExternalTimestamp = true; + break; + case HEADER_W_QDC : + hasQdc = true; + break; + case HEADER_W_ESUM : + hasEnergySums = true; + break; + case HEADER_W_ESUM_ETS : + hasExternalTimestamp = hasEnergySums = true; + break; + case HEADER_W_ESUM_QDC : + hasEnergySums = hasQdc = true; + break; + case HEADER_W_ESUM_QDC_ETS : + hasEnergySums = hasExternalTimestamp = hasQdc = true; + break; + case HEADER_W_QDC_ETS : + hasQdc = hasExternalTimestamp = true; + break; + default: + numSkippedBuffers++; + cerr << "XiaListModeDataDecoder::ReadBuffer : We encountered " + "an unrecognized header length (" << headerLength + << "). " << endl + << "Skipped " << numSkippedBuffers + << " buffers in the file." << endl + << "Unexpected header length: " << headerLength << endl + << "ReadBuffer: Buffer " << modNum << " of length " + << bufLen << endl + << "ReadBuffer: CRATE:SLOT(MOD):CHAN " + << data->GetCrateNumber() << ":" + << data->GetSlotNumber() << "(" << modNum << "):" + << data->GetChannelNumber() << endl; + return vector(); + } + + if (hasExternalTimestamp) { + //Do nothing for now + } + + if (hasEnergySums) { + // Skip the onboard partial sums for now + // trailing, leading, gap, baseline + } + + if (hasQdc) { + static const unsigned int numQdcs = 8; + vector tmp; + unsigned int offset = headerLength - numQdcs; + for (unsigned int i = 0; i < numQdcs; i++) { + tmp.push_back(buf[offset + i]); + } + data->SetQdc(tmp); + } + + ///@TODO Figure out where to put this... + //channel_counts[modNum][chanNum]++; + + ///@TODO This needs to be revised to take into account the bit + /// resolution of the modules. I've currently set it to the maximum + /// bit resolution of any module (16-bit). + if (data->IsSaturated()) + data->SetEnergy(65536); + + //We set the time according to the revision and firmware. + pair times = CalculateTimeInSamples(mask, *data); + data->SetTimeSansCfd(times.first); + data->SetTime(times.second); + + // One last check to ensure event length matches what we think it + // should be. + if (traceLength / 2 + headerLength != eventLength) { + numSkippedBuffers++; + cerr << "XiaListModeDataDecoder::ReadBuffer : Event" + "length (" << eventLength << ") does not correspond to " + "header length (" << headerLength + << ") and trace length (" + << traceLength / 2 << "). Skipped a total of " + << numSkippedBuffers << " buffers in this file." << endl; + return vector(); + } else //Advance the buffer past the header and to the trace + buf += headerLength; + + if (traceLength > 0) { + DecodeTrace(buf, *data, traceLength); + buf += traceLength / 2; + } + events.push_back(data); + }// while(buf < bufStart + bufLen) + return events; +} + +std::pair XiaListModeDataDecoder::DecodeWordZero( + const unsigned int &word, XiaData &data, + const XiaListModeDataMask &mask) { + + data.SetChannelNumber(word & mask.GetChannelNumberMask().first); + data.SetSlotNumber((word & mask.GetSlotIdMask().first) + >> mask.GetSlotIdMask().second); + data.SetCrateNumber((word & mask.GetCrateIdMask().first) + >> mask.GetCrateIdMask().second); + data.SetPileup((word & mask.GetFinishCodeMask().first) != 0); + + //We have to check if we have one of these three firmwares since they + // have the Trace-Out-of-Range flag in this word. + switch (mask.GetFirmware()) { + case R17562: + case R20466: + case R27361: + data.SetSaturation((bool) + ((word & + mask.GetTraceOutOfRangeFlagMask().first) + >> mask.GetTraceOutOfRangeFlagMask().second)); + break; + default: + break; + } + + return make_pair((word & mask.GetHeaderLengthMask().first) + >> mask.GetHeaderLengthMask().second, + (word & mask.GetEventLengthMask().first) + >> mask.GetEventLengthMask().second); +} + +void XiaListModeDataDecoder::DecodeWordTwo( + const unsigned int &word, XiaData &data, + const XiaListModeDataMask &mask) { + data.SetEventTimeHigh(word & mask.GetEventTimeHighMask().first); + data.SetCfdFractionalTime((word & mask.GetCfdFractionalTimeMask().first) + >> mask.GetCfdFractionalTimeMask().second); + data.SetCfdForcedTriggerBit( + (bool) ((word & mask.GetCfdForcedTriggerBitMask().first) + >> mask.GetCfdForcedTriggerBitMask().second)); + data.SetCfdTriggerSourceBit( + (bool) (word & mask.GetCfdTriggerSourceMask().first) + >> mask.GetCfdTriggerSourceMask().second); +} + +unsigned int XiaListModeDataDecoder::DecodeWordThree( + const unsigned int &word, XiaData &data, + const XiaListModeDataMask &mask) { + data.SetEnergy(word & mask.GetEventEnergyMask().first); + + //Reverse the logic that we used in DecodeWordZero, since if we do not + // have these three firmwares we need to check this word for the + // Trace-Out-of-Range flag. + switch (mask.GetFirmware()) { + case R17562: + case R20466: + case R27361: + break; + default: + data.SetSaturation((bool) + ((word & + mask.GetTraceOutOfRangeFlagMask().first) + >> mask.GetTraceOutOfRangeFlagMask().second)); + break; + } + + return ((word & mask.GetTraceLengthMask().first) + >> mask.GetTraceLengthMask().second); +} + +void XiaListModeDataDecoder::DecodeTrace(unsigned int *buf, XiaData &data, + const unsigned int &traceLength) { + vector tmp; + // sbuf points to the beginning of trace data + unsigned short *sbuf = (unsigned short *) buf; + + // Read the trace data (2-bytes per sample, i.e. 2 samples per word) + for (unsigned int k = 0; k < traceLength; k++) + tmp.push_back((unsigned int &&) sbuf[k]); + + data.SetTrace(tmp); +} + +pair XiaListModeDataDecoder::CalculateTimeInSamples( + const XiaListModeDataMask &mask, const XiaData &data) { + double filterTime = + data.GetEventTimeLow() + data.GetEventTimeHigh() * pow(2., 32); + + if (data.GetCfdFractionalTime() == 0 || data.GetCfdForcedTriggerBit()) + return make_pair(filterTime, filterTime); + + double cfdTime = 0, multiplier = 1; + if (mask.GetFrequency() == 100) + cfdTime = data.GetCfdFractionalTime() / mask.GetCfdSize(); + + if (mask.GetFrequency() == 250) { + multiplier = 2; + cfdTime = data.GetCfdFractionalTime() / mask.GetCfdSize() - + data.GetCfdTriggerSourceBit(); + } + + if (mask.GetFrequency() == 500) { + multiplier = 10; + cfdTime = data.GetCfdFractionalTime() / mask.GetCfdSize() + + data.GetCfdTriggerSourceBit() - 1; + } + + return make_pair(filterTime, filterTime * multiplier + cfdTime); +} + +double XiaListModeDataDecoder::CalculateTimeInNs( + const XiaListModeDataMask &mask, const XiaData &data) { + double conversionToNs = 1. / (mask.GetFrequency() * 1.e6); + return CalculateTimeInSamples(mask, data).second * conversionToNs; +} \ No newline at end of file diff --git a/Analysis/ScanLibraries/source/XiaListModeDataEncoder.cpp b/Analysis/ScanLibraries/source/XiaListModeDataEncoder.cpp new file mode 100644 index 000000000..bb402ddc8 --- /dev/null +++ b/Analysis/ScanLibraries/source/XiaListModeDataEncoder.cpp @@ -0,0 +1,154 @@ +/// @file XiaListModeDataEncoder.hpp +/// @brief Class that handles encoding Pixie-16 list mode data from a XiaData +/// class +/// @author S. V. Paulauskas +/// @date December 30, 2016 +#include +#include +#include + +#include + +#include "HelperEnumerations.hpp" +#include "XiaListModeDataEncoder.hpp" + +using namespace std; +using namespace DataProcessing; + +std::vector XiaListModeDataEncoder::EncodeXiaData( + const XiaData &data, + const FIRMWARE &firmware, + const unsigned int &frequency) { + if (data == XiaData()) + throw invalid_argument("XiaListModeDataEncoder::EncodeXiaData - We " + "received an empty XiaData structure."); + if (firmware == UNKNOWN) + throw invalid_argument("XiaListModeDataEncoder::EncodeXiaData - We " + "cannot encode against an UNKNOWN " + "firmware."); + if (frequency != 100 && frequency != 250 && frequency != 500) + throw invalid_argument("XiaListModeDataEncoder::EncodeXiaData - We " + "cannot encode against an unknown " + "frequency."); + vector header; + XiaListModeDataMask mask(firmware, frequency); + + header.push_back(EncodeWordZero(data, mask)); + header.push_back(EncodeWordOne(data,mask)); + header.push_back(EncodeWordTwo(data,mask)); + header.push_back(EncodeWordThree(data,mask)); + + //The following calls are required in this order due to the structure of + // the XIA list mode data format. + if(data.GetEnergySums().size() != 0) { + vector tmp = EncodeEsums(data, mask); + header.insert(header.end(), tmp.begin(), tmp.end()); + } + + ///Each QDC value takes up a single 32-bit word. No need to do anything + /// special here. + if(data.GetQdc().size() != 0) { + for(unsigned int i = 0; i < data.GetQdc().size(); i++) + header.push_back((unsigned int &&)data.GetQdc().at(i)); + } + + if(data.GetExternalTimeLow() != 0) { + //The External timestamps work essentially the same as the internal time + // stamps in terms of the structure. The major difference here is that + // the upper bits of the high word are all zero. No special + // processing needed in this case. + header.push_back(data.GetExternalTimeLow()); + header.push_back(data.GetExternalTimeHigh()); + } + + ///Trace comes last since it comes after the header. + if(data.GetTrace().size() != 0) { + vector tmp = EncodeTrace(data.GetTrace(), + mask.GetTraceMask()); + header.insert(header.end(), tmp.begin(), tmp.end()); + } + + return header; +} + +unsigned int XiaListModeDataEncoder::EncodeWordZero( + const XiaData &data, const XiaListModeDataMask &mask) { + //These magic numbers are dependent upon the XIA List Mode Data Structure. + // For more information about them please consult the relevant + // documentation. + unsigned int headerLength = 4; + if (data.GetExternalTimeLow() != 0) + headerLength += 2; + if (data.GetEnergySums().size() != 0) + headerLength += 4; + if (data.GetQdc().size() != 0) + headerLength += 8; + unsigned int eventLength = + (unsigned int)ceil(data.GetTrace().size() * 0.5) + headerLength; + + unsigned int word = 0; + word |= data.GetChannelNumber() & mask.GetChannelNumberMask().first; + word |= (data.GetSlotNumber() << mask.GetSlotIdMask().second) & + mask.GetSlotIdMask().first; + word |= (data.GetCrateNumber() << mask.GetCrateIdMask().second) & + mask.GetCrateIdMask().first; + word |= (headerLength << mask.GetHeaderLengthMask().second) & + mask.GetHeaderLengthMask().first; + word |= (eventLength << mask.GetEventLengthMask().second) & + mask.GetEventLengthMask().first; + word |= (data.IsPileup() << mask.GetFinishCodeMask().second) & + mask.GetFinishCodeMask().first; + return word; +} + +unsigned int XiaListModeDataEncoder::EncodeWordOne( + const XiaData &data, const XiaListModeDataMask &mask) { + return data.GetEventTimeLow(); +} + +unsigned int XiaListModeDataEncoder::EncodeWordTwo( + const XiaData &data, const XiaListModeDataMask &mask){ + unsigned int word = 0; + word |= data.GetEventTimeHigh() & mask.GetEventTimeHighMask().first; + word |= (data.GetCfdFractionalTime() << mask.GetCfdFractionalTimeMask().second) & + mask.GetCfdFractionalTimeMask().first; + word |= (data.GetCfdForcedTriggerBit() << mask.GetCfdForcedTriggerBitMask() + .second) & mask.GetCfdForcedTriggerBitMask().first; + word |= (data.GetCfdTriggerSourceBit() << mask.GetCfdTriggerSourceMask().second) & + mask.GetCfdTriggerSourceMask().first; + return word; +} + +unsigned int XiaListModeDataEncoder::EncodeWordThree( + const XiaData &data, const XiaListModeDataMask &mask){ + unsigned int word = 0; + word |= (unsigned int) data.GetEnergy() & mask.GetEventEnergyMask().first; + word |= (data.IsSaturated() << mask.GetTraceOutOfRangeFlagMask() + .second) & mask.GetTraceOutOfRangeFlagMask().first; + word |= (data.GetTrace().size() << mask.GetTraceLengthMask().second) & + mask.GetTraceLengthMask().first; + return word; +} + +vector XiaListModeDataEncoder::EncodeTrace( + const std::vector &trc, const std::pair &mask){ + vector tmp; + for(vector::const_iterator it = trc.begin(); + it != trc.end(); it+=2) { + vector::const_iterator next = it + 1; + unsigned int word = 0; + word |= *it; + if(next != trc.end()) + word |= *next << mask.second; + tmp.push_back(word); + } + return tmp; +} + +///@TODO This needs to be updated so that it actually formats the baseline in +/// one of the IEEE standard formats properly before we return. +vector XiaListModeDataEncoder::EncodeEsums( + const XiaData &data, const XiaListModeDataMask &mask){ + return data.GetEnergySums(); +} \ No newline at end of file diff --git a/Analysis/ScanLibraries/source/XiaListModeDataMask.cpp b/Analysis/ScanLibraries/source/XiaListModeDataMask.cpp new file mode 100644 index 000000000..309b22254 --- /dev/null +++ b/Analysis/ScanLibraries/source/XiaListModeDataMask.cpp @@ -0,0 +1,350 @@ +/// @file XiaListModeDataMask.cpp +/// @brief Class that provides the data masks for XIA list mode data +/// @author S. V. Paulauskas +/// @date December 29, 2016 +#include +#include +#include + +#include "XiaListModeDataMask.hpp" + +using namespace std; +using namespace DataProcessing; + +FIRMWARE XiaListModeDataMask::ConvertStringToFirmware(const std::string &type) { + FIRMWARE firmware = UNKNOWN; + unsigned int firmwareNumber = 0; + stringstream msg; + + //First convert the string into a number + if (type.find("R") == 0 || type.find("r") == 0) { + string tmp(type.begin() + 1, type.end()); + firmwareNumber = (unsigned int) atoi(tmp.c_str()); + } else + firmwareNumber = (unsigned int) atoi(type.c_str()); + + if (firmwareNumber >= 17562 && firmwareNumber < 20466) + firmware = R17562; + else if (firmwareNumber >= 20466 && firmwareNumber < 27361) + firmware = R20466; + else if (firmwareNumber >= 27361 && firmwareNumber < 29432) + firmware = R27361; + else if (firmwareNumber >= 29432 && firmwareNumber < 30474) + firmware = R29432; + else if (firmwareNumber >= 30474 && firmwareNumber < 30980) + firmware = R30474; + else if (firmwareNumber >= 30980 && firmwareNumber < 30981) + firmware = R30980; + else if (firmwareNumber >= 30981 && firmwareNumber < 34688) + firmware = R30981; + else if (firmwareNumber == + 34688) //compare exactly here since nothing higher + firmware = R34688; + else { + msg << "XiaListModeDataMask::CovnertStringToFirmware : " + << "Unrecognized firmware option - " << type << endl; + throw invalid_argument(msg.str()); + } + return firmware; +} + +///The CFD Fractional Time always starts on bit 16 of Word 2. The range of +/// this value changes. +pair +XiaListModeDataMask::GetCfdFractionalTimeMask() const { + if (firmware_ == UNKNOWN || frequency_ == 0) + throw invalid_argument(BadMaskErrorMessage + ("GetCfdFractionalTimeMask")); + unsigned int mask = 0; + if (frequency_ == 100) { + switch (firmware_) { + case R17562: + case R29432: + mask = 0xFFFF0000; + break; + case R30474: + case R30980: + case R30981: + case R34688: + mask = 0x7FFF0000; + break; + default: + break; + } + } else if (frequency_ == 250) { + switch (firmware_) { + case R20466: + mask = 0xFFFF0000; + break; + case R27361: + case R29432: + mask = 0x7FFF0000; + break; + case R30474: + case R30980: + case R30981: + case R34688: + mask = 0x3FFF0000; + break; + default: + break; + } + } else if (frequency_ == 500) { + switch (firmware_) { + case R29432: + case R30474: + case R30980: + case R30981: + case R34688: + mask = 0x1FFF0000; + break; + default: + break; + } + } + return make_pair(mask, 16); +} + +std::pair XiaListModeDataMask::GetEventLengthMask() +const { + if(firmware_ == UNKNOWN) + throw invalid_argument(BadMaskErrorMessage("GetEventLengthMask")); + unsigned int mask = 0; + unsigned int bit = 0; + switch(firmware_) { + case R17562: + case R20466: + case R27361: + mask = 0x3FFE0000; + bit = 17; + break; + case R29432: + case R30474: + case R30980: + case R30981: + case R34688: + mask = 0x7FFE0000; + bit = 17; + break; + default: + break; + } + return make_pair(mask, bit); +} + +pair +XiaListModeDataMask::GetCfdForcedTriggerBitMask() const { + if (firmware_ == UNKNOWN || frequency_ == 0) + throw invalid_argument(BadMaskErrorMessage + ("GetCfdForcedTriggerBitMask")); + unsigned int mask = 0; + unsigned int bit = 0; + if (frequency_ == 100) { + switch (firmware_) { + case R30474: + case R30980: + case R30981: + case R34688: + mask = 0x80000000; + bit = 31; + break; + default: + break; + } + } else if (frequency_ == 250) { + switch (firmware_) { + case R30474: + case R30980: + case R30981: + case R34688: + mask = 0x80000000; + bit = 31; + break; + default: + break; + } + } + return make_pair(mask, bit); +} + +pair +XiaListModeDataMask::GetCfdTriggerSourceMask() const { + if (firmware_ == UNKNOWN || frequency_ == 0) + throw invalid_argument(BadMaskErrorMessage + ("GetCfdTriggerSourceMask")); + unsigned int mask = 0; + unsigned int bit = 0; + if (frequency_ == 250) { + switch (firmware_) { + case R27361: + case R29432: + mask = 0x80000000; + bit = 31; + break; + case R30474: + case R30980: + case R30981: + case R34688: + mask = 0x40000000; + bit = 30; + break; + default: + break; + } + } else if (frequency_ == 500) { + switch (firmware_) { + case R29432: + case R30474: + case R30980: + case R30981: + case R34688: + mask = 0xE0000000; + bit = 29; + break; + default: + break; + } + } + return make_pair(mask, bit); +} + +/// The energy always starts out on Bit 0 of Word 3 so we do not need to +/// define a variable for the bit. +pair XiaListModeDataMask::GetEventEnergyMask() +const { + if (firmware_ == UNKNOWN || frequency_ == 0) + throw invalid_argument(BadMaskErrorMessage + ("GetEventEnergyMask")); + unsigned int mask = 0; + switch (firmware_) { + case R29432: + case R30474: + case R30980: + case R30981: + mask = 0x00007FFF; + break; + case R17562: + case R20466: + case R27361: + case R34688: + mask = 0x0000FFFF; + break; + default: + break; + } + return make_pair(mask, 0); +} + +//The Trace-out-of-range flag moves around on us. For most revisions it on +// bit 15 of Word 3, but for the most recent firmware its been moved to bit 31. +pair +XiaListModeDataMask::GetTraceOutOfRangeFlagMask() const { + if (firmware_ == UNKNOWN || frequency_ == 0) + throw invalid_argument(BadMaskErrorMessage + ("GetTraceOutOfRangeFlagMask")); + + unsigned int mask = 0; + unsigned int bit = 0; + switch (firmware_) { + case R17562: + case R20466: + case R27361: + mask = 0x40000000; + bit = 30; + break; + case R29432: + case R30474: + case R30980: + case R30981: + mask = 0x00008000; + bit = 15; + break; + case R34688: + mask = 0x80000000; + bit = 31; + break; + default: + break; + } + return make_pair(mask, bit); +} + +//Trace Length always starts on bit 16 of Word 3. +pair XiaListModeDataMask::GetTraceLengthMask() +const { + if (firmware_ == UNKNOWN || frequency_ == 0) + throw invalid_argument(BadMaskErrorMessage + ("GetTraceLengthMask")); + unsigned int mask = 0; + switch (firmware_) { + case R17562: + case R20466: + case R27361: + case R29432: + case R30474: + case R30980: + case R30981: + mask = 0xFFFF0000; + break; + case R34688: + mask = 0x7FFF0000; + break; + default: + break; + } + return make_pair(mask, 16); +} + +string XiaListModeDataMask::BadMaskErrorMessage(const std::string &func) const { + stringstream msg; + msg << "XiaListModeDataMask::" << func << " : " + << "Could not obtain a mask for firmware code " << firmware_ + << " and frequency " << frequency_ << ". Check your settings."; + return msg.str(); +} + +double XiaListModeDataMask::GetCfdSize() const { + if (firmware_ == UNKNOWN || frequency_ == 0) + throw invalid_argument(BadMaskErrorMessage + ("GetCfdSize")); + if (frequency_ == 500) + return 8192.; + + double val = 0; + if (frequency_ == 100) { + switch (firmware_) { + case R17562: + case R29432: + val = 65536; + break; + case R30474: + case R30980: + case R30981: + case R34688: + val = 32768; + break; + default: + break; + } + } else if (frequency_ == 250) { + switch (firmware_) { + case R20466: + val = 65536; + break; + case R27361: + case R29432: + val = 32768; + break; + case R30980: + case R30981: + case R34688: + case R30474: + val = 16384; + break; + default: + break; + } + } + + return val; +} diff --git a/Analysis/ScanLibraries/tests/CMakeLists.txt b/Analysis/ScanLibraries/tests/CMakeLists.txt new file mode 100644 index 000000000..e04d6b979 --- /dev/null +++ b/Analysis/ScanLibraries/tests/CMakeLists.txt @@ -0,0 +1,35 @@ +################################################################################ +add_executable(unittest-XiaListModeDataDecoder + unittest-XiaListModeDataDecoder.cpp + ../source/XiaData.cpp + ../source/XiaListModeDataDecoder.cpp + ../source/XiaListModeDataMask.cpp) +target_link_libraries(unittest-XiaListModeDataDecoder UnitTest++ ${LIBS}) +install(TARGETS unittest-XiaListModeDataDecoder DESTINATION bin/unittests) + +################################################################################ +add_executable(unittest-XiaListModeDataEncoder + unittest-XiaListModeDataEncoder.cpp + ../source/XiaData.cpp + ../source/XiaListModeDataEncoder.cpp + ../source/XiaListModeDataMask.cpp) +target_link_libraries(unittest-XiaListModeDataEncoder UnitTest++ ${LIBS}) +install(TARGETS unittest-XiaListModeDataEncoder DESTINATION bin/unittests) + +################################################################################ +add_executable(unittest-XiaListModeDataMask + unittest-XiaListModeDataMask.cpp + ../source/XiaData.cpp + ../source/XiaListModeDataMask.cpp) +target_link_libraries(unittest-XiaListModeDataMask UnitTest++ ${LIBS}) +install(TARGETS unittest-XiaListModeDataMask DESTINATION bin/unittests) + +################################################################################ +add_executable(unittest-XiaData unittest-XiaData.cpp ../source/XiaData.cpp) +target_link_libraries(unittest-XiaData UnitTest++ ${LIBS}) +install(TARGETS unittest-XiaData DESTINATION bin/unittests) + +################################################################################ +add_executable(unittest-Trace unittest-Trace.cpp) +target_link_libraries(unittest-Trace UnitTest++ ${LIBS}) +install(TARGETS unittest-Trace DESTINATION bin/unittests) \ No newline at end of file diff --git a/Analysis/ScanLibraries/tests/unittest-Trace.cpp b/Analysis/ScanLibraries/tests/unittest-Trace.cpp new file mode 100644 index 000000000..a107205c4 --- /dev/null +++ b/Analysis/ScanLibraries/tests/unittest-Trace.cpp @@ -0,0 +1,67 @@ +///@file unittest-Trace.cpp +///@brief A program that will execute unit tests on Trace +///@author S. V. Paulauskas +///@date February 3, 2017 +#include + +#include + +#include + +#include "UnitTestSampleData.hpp" +#include "Trace.hpp" + +using namespace std; +using namespace unittest_trace_variables; +using namespace unittest_decoded_data; + +TEST_FIXTURE(Trace, TestingGettersAndSetters){ + double double_input = 100.; + + SetBaseline(baseline_pair); + CHECK_EQUAL(baseline_pair.first, GetBaselineInfo().first); + CHECK_EQUAL(baseline_pair.second, GetBaselineInfo().second); + + SetMax(max_pair); + CHECK_EQUAL(max_pair.first, GetMaxInfo().first); + CHECK_EQUAL(max_pair.second, GetMaxInfo().second); + + SetWaveformRange(waveform_range); + CHECK_EQUAL(waveform_range.first, GetWaveformRange().first); + CHECK_EQUAL(waveform_range.second, GetWaveformRange().second); + + SetTraceSansBaseline(trace_sans_baseline); + CHECK_ARRAY_EQUAL(trace_sans_baseline, GetTraceSansBaseline(), + trace_sans_baseline.size()); + + CHECK_ARRAY_EQUAL(waveform, GetWaveform(), waveform.size()); + + SetTriggerFilter(trace_sans_baseline); + CHECK_ARRAY_EQUAL(trace_sans_baseline, GetTriggerFilter(), + trace_sans_baseline.size()); + + SetEnergySums(waveform); + CHECK_ARRAY_EQUAL(waveform, GetEnergySums(), waveform.size()); + + SetQdc(double_input); + CHECK_EQUAL(double_input, GetQdc()); + + SetExtrapolatedMax(extrapolated_maximum_pair); + CHECK_EQUAL(extrapolated_maximum_pair.first, + GetExtrapolatedMaxInfo().first); + CHECK_EQUAL(extrapolated_maximum_pair.second, + GetExtrapolatedMaxInfo().second); + + SetIsSaturated(true); + CHECK(IsSaturated()); + + SetPhase(double_input); + CHECK_EQUAL(double_input, GetPhase()); + + SetTau(double_input); + CHECK_EQUAL(double_input, GetTau()); +} + +int main(int argv, char *argc[]) { + return (UnitTest::RunAllTests()); +} \ No newline at end of file diff --git a/Analysis/ScanLibraries/tests/unittest-XiaData.cpp b/Analysis/ScanLibraries/tests/unittest-XiaData.cpp new file mode 100644 index 000000000..55053acc0 --- /dev/null +++ b/Analysis/ScanLibraries/tests/unittest-XiaData.cpp @@ -0,0 +1,168 @@ +///@file unittest-XiaData.cpp +///@brief A program that will execute unit tests on XiaData +///@author S. V. Paulauskas +///@date December 5, 2016 +#include + +#include + +#include + +#include "UnitTestSampleData.hpp" +#include "XiaData.hpp" + +using namespace std; +using namespace unittest_trace_variables; +using namespace unittest_decoded_data; + +XiaData lhs, rhs; + +TEST_FIXTURE(XiaData, Test_GetBaseline) { + SetBaseline(baseline); + CHECK_EQUAL(baseline, GetBaseline()); +} + +TEST_FIXTURE(XiaData, Test_GetId) { + SetSlotNumber(slotId); + SetChannelNumber(channelNumber); + SetCrateNumber(crateId); + CHECK_EQUAL(crateId * 208 + GetModuleNumber() * 16 + + channelNumber, GetId()); +} + +TEST_FIXTURE(XiaData, Test_GetSetCfdForcedTrig) { + SetCfdForcedTriggerBit(cfd_forced_trigger); + CHECK(GetCfdForcedTriggerBit()); +} + +TEST_FIXTURE(XiaData, Test_GetSetCfdFractionalTime) { + SetCfdFractionalTime(cfd_fractional_time); + CHECK_EQUAL(cfd_fractional_time, GetCfdFractionalTime()); +} + +TEST_FIXTURE(XiaData, Test_GetSetCfdTriggerSourceBit) { + SetCfdTriggerSourceBit(cfd_source_trigger_bit); + CHECK(GetCfdTriggerSourceBit()); +} + +TEST_FIXTURE(XiaData, Test_GetSetChannelNumber) { + SetChannelNumber(channelNumber); + CHECK_EQUAL(channelNumber, GetChannelNumber()); +} + +TEST_FIXTURE(XiaData, Test_GetSetCrateNumber) { + SetCrateNumber(crateId); + CHECK_EQUAL(crateId, GetCrateNumber()); +} + +TEST_FIXTURE(XiaData, Test_GetSetEnergy) { + SetEnergy(energy); + CHECK_EQUAL(energy, GetEnergy()); +} + +TEST_FIXTURE(XiaData, Test_GetSetEnergySums) { + SetEnergySums(energy_sums); + CHECK_ARRAY_EQUAL(energy_sums, GetEnergySums(), + energy_sums.size()); +} + +TEST_FIXTURE(XiaData, Test_GetSetEventTimeHigh) { + SetEventTimeHigh(ts_high); + CHECK_EQUAL(ts_high, GetEventTimeHigh()); +} + +TEST_FIXTURE(XiaData, Test_GetSetEventTimeLow) { + SetEventTimeLow(ts_low); + CHECK_EQUAL(ts_low, GetEventTimeLow()); +} + +TEST_FIXTURE(XiaData, Test_GetSetExternalTimeHigh) { + SetExternalTimeHigh(ex_ts_high); + CHECK_EQUAL(ex_ts_high, GetExternalTimeHigh()); +} + +TEST_FIXTURE(XiaData, Test_GetSetExternalTimeLow) { + SetExternalTimeLow(ex_ts_low); + CHECK_EQUAL(ex_ts_low, GetExternalTimeLow()); +} + +TEST_FIXTURE(XiaData, Test_GetSetPileup) { + SetPileup(pileup_bit); + CHECK(IsPileup()); +} + +TEST_FIXTURE(XiaData, Test_GetSetQdc) { + SetQdc(qdc); + CHECK_ARRAY_EQUAL(qdc, GetQdc(), qdc.size()); +} + +TEST_FIXTURE(XiaData, Test_GetSetSaturation) { + SetSaturation(trace_out_of_range); + CHECK(IsSaturated()); +} + +TEST_FIXTURE(XiaData, Test_GetSetSlotNumber) { + SetSlotNumber(slotId); + CHECK_EQUAL(slotId, GetSlotNumber()); +} + +TEST_FIXTURE(XiaData, Test_GetSetTrace) { + SetTrace(trace); + CHECK_ARRAY_EQUAL(trace, GetTrace(), trace.size()); +} + +TEST_FIXTURE(XiaData, Test_GetSetVirtualChannel) { + SetVirtualChannel(virtual_channel); + CHECK(IsVirtualChannel()); +} + +TEST_FIXTURE(XiaData, Test_GetTime) { + SetTime(ts); + CHECK_EQUAL(ts, GetTime()); +} + +///This will test that the Time for the rhs is greater than the lhs +TEST(Test_CompareTime){ + lhs.Clear(); rhs.Clear(); + + lhs.SetTime(ts); + rhs.SetTime(ts+10); + + CHECK(lhs.CompareTime(&lhs, &rhs)); +} + +//This will test that the ID for the rhs is greater than the lhs +TEST(Test_CompareId) { + lhs.Clear(); rhs.Clear(); + lhs.SetChannelNumber(channelNumber); + lhs.SetSlotNumber(slotId); + lhs.SetCrateNumber(crateId); + + rhs.SetChannelNumber(channelNumber); + rhs.SetSlotNumber(slotId+2); + rhs.SetCrateNumber(crateId); + + CHECK(lhs.CompareId(&lhs, &rhs)); +} + +TEST(Test_Equality) { + lhs.Clear(); rhs.Clear(); + lhs.SetChannelNumber(channelNumber); + lhs.SetSlotNumber(slotId); + lhs.SetCrateNumber(crateId); + rhs = lhs; + CHECK(lhs == rhs); +} + +TEST(Test_LessThanOperator) { + lhs.Clear(); rhs.Clear(); + lhs.SetTime(ts); + rhs = lhs; + rhs.SetTime(ts+10); + CHECK(lhs < rhs); +} + +int main(int argv, char *argc[]) { + return (UnitTest::RunAllTests()); +} + diff --git a/Analysis/ScanLibraries/tests/unittest-XiaListModeDataDecoder.cpp b/Analysis/ScanLibraries/tests/unittest-XiaListModeDataDecoder.cpp new file mode 100644 index 000000000..50d3dbdf8 --- /dev/null +++ b/Analysis/ScanLibraries/tests/unittest-XiaListModeDataDecoder.cpp @@ -0,0 +1,76 @@ +///@file unittest-XiaListModeDataDecoder.cpp +///@brief Unit tests for the XiaListModeDataDecoder class +///@author S. V. Paulauskas +///@author December 25, 2016 +#include + +#include + +#include "HelperEnumerations.hpp" +#include "UnitTestSampleData.hpp" +#include "XiaListModeDataDecoder.hpp" + +using namespace std; +using namespace DataProcessing; +using namespace unittest_encoded_data; +using namespace unittest_decoded_data; + +///@TODO These need to be expanded so that we cover all of the nine different +/// firmware and frequency combinations. +static const XiaListModeDataMask mask(R30474, 250); +using namespace unittest_encoded_data::R30474_250; + +//Test the error handling in the class +TEST_FIXTURE(XiaListModeDataDecoder, TestBufferLengthChecks) { + //Check that we throw a length error when the buffer length is zero + CHECK_THROW(DecodeBuffer(&empty_buffer[0], mask), length_error); + //Check that we return an empty vector when we have an empty module + CHECK_EQUAL(empty_buffer[1], + DecodeBuffer(&empty_module_buffer[0], mask).size()); +} +///Test if we can decode a simple 4 word header that includes the Pixie +/// Module Data Header. +TEST_FIXTURE(XiaListModeDataDecoder, TestHeaderDecoding) { + //Check that we get an empty vector when the header has an impossible size. + CHECK_EQUAL((unsigned int)0, + DecodeBuffer(&header_w_bad_headerlen[0], mask).size()); + + //Check that we can decode a simple 4-word header. + XiaData result_data = *(DecodeBuffer(&header[0], mask).front()); + + CHECK_EQUAL(slotId, result_data.GetSlotNumber()); + CHECK_EQUAL(channelNumber, result_data.GetChannelNumber()); + CHECK_EQUAL(energy, result_data.GetEnergy()); + CHECK_EQUAL(ts_high, result_data.GetEventTimeHigh()); + CHECK_EQUAL(ts_low, result_data.GetEventTimeLow()); + CHECK_EQUAL(ts, result_data.GetTime()); +} + +//Test if we can decode a trace properly +TEST_FIXTURE(XiaListModeDataDecoder, TestTraceDecoding) { + //Testing that we return an empty event list. + CHECK_EQUAL((unsigned int)0, + DecodeBuffer(&header_w_bad_eventlen[0], mask).size()); + + XiaData result = *(DecodeBuffer(&header_N_trace[0], mask).front()); + CHECK_ARRAY_EQUAL(unittest_trace_variables::trace, result.GetTrace(), + unittest_trace_variables::trace.size()); +} + +//Test if we can decode the qdc properly +TEST_FIXTURE(XiaListModeDataDecoder, TestQdcDecoding) { + XiaData result = *(DecodeBuffer(&header_N_qdc[0], mask).front()); + CHECK_ARRAY_EQUAL(qdc, result.GetQdc(), qdc.size()); +} + +//Test that we can get the right timestamp if we involve the CFD. +TEST_FIXTURE(XiaListModeDataDecoder, TestCfdTimeCalculation) { + XiaData result = *(DecodeBuffer(&header_N_Cfd[0], mask).front()); + CHECK_EQUAL(cfd_fractional_time, result.GetCfdFractionalTime()); + CHECK_CLOSE(ts_w_cfd, result.GetTime(), 1e-5); +} + + +int main(int argv, char *argc[]) { + return (UnitTest::RunAllTests()); +} \ No newline at end of file diff --git a/Analysis/ScanLibraries/tests/unittest-XiaListModeDataEncoder.cpp b/Analysis/ScanLibraries/tests/unittest-XiaListModeDataEncoder.cpp new file mode 100644 index 000000000..e85e64dbe --- /dev/null +++ b/Analysis/ScanLibraries/tests/unittest-XiaListModeDataEncoder.cpp @@ -0,0 +1,65 @@ +///@file unittest-XiaListModeDataEncoder.cpp +///@brief Unit tests for the XiaListModeDataDecoder class +///@author S. V. Paulauskas +///@author December 25, 2016 +#include + +#include + +#include "HelperEnumerations.hpp" +#include "UnitTestSampleData.hpp" +#include "XiaListModeDataEncoder.hpp" + +using namespace std; +using namespace DataProcessing; +using namespace unittest_encoded_data; +using namespace unittest_decoded_data; +using namespace unittest_encoded_data::R30474_250; + +TEST_FIXTURE(XiaListModeDataEncoder, TestEncodingThrows) { + //Check that we throw a range error when we pass an empty XiaData class + CHECK_THROW(EncodeXiaData(XiaData(), R30474, 250), invalid_argument); + + //Check that we catch an invalid_argument when we pass a bogus firmware + CHECK_THROW(EncodeXiaData(XiaData(), UNKNOWN, 250), invalid_argument); + + //Check that we catch an invalid_argument when we pass a bogus frequency + CHECK_THROW(EncodeXiaData(XiaData(), R30474, 2500), invalid_argument); +} + +///Test if we can encode some headers with different information. +///@TODO Add headers for Esums and external TS. +TEST_FIXTURE(XiaListModeDataEncoder, TestSimpleHeaderEncoding) { + XiaData data; + data.SetEnergy(energy); + data.SetSlotNumber(slotId); + data.SetChannelNumber(channelNumber); + data.SetCrateNumber(crateId); + data.SetEventTimeLow(ts_low); + data.SetEventTimeHigh(ts_high); + + //Check that we can handle just a simple 4 word header + CHECK_ARRAY_EQUAL(header_vec, EncodeXiaData(data, R30474, 250), + header_vec.size()); + + //Check that we can handle a header with a trace + data.SetTrace(unittest_trace_variables::trace); + CHECK_ARRAY_EQUAL(header_vec_w_trc, EncodeXiaData(data, R30474, 250), + header_vec_w_trc.size()); + + //Check that we can handle a QDC + data.SetQdc(qdc); + data.SetTrace(vector()); + CHECK_ARRAY_EQUAL(header_vec_w_qdc, EncodeXiaData(data, R30474, 250), + header_vec_w_qdc.size()); + + //Check that we can handle a QDC and a Trace + data.SetQdc(qdc); + data.SetTrace(unittest_trace_variables::trace); + CHECK_ARRAY_EQUAL(header_vec_w_qdc_n_trc, EncodeXiaData(data, R30474, 250), + header_vec_w_qdc_n_trc.size()); +} + +int main(int argv, char *argc[]) { + return (UnitTest::RunAllTests()); +} \ No newline at end of file diff --git a/Analysis/ScanLibraries/tests/unittest-XiaListModeDataMask.cpp b/Analysis/ScanLibraries/tests/unittest-XiaListModeDataMask.cpp new file mode 100644 index 000000000..04ae447b5 --- /dev/null +++ b/Analysis/ScanLibraries/tests/unittest-XiaListModeDataMask.cpp @@ -0,0 +1,238 @@ +///@file unittest-XiaListModeDataMask.cpp +///@brief Unit testing of the XiaListModeDataMask class +///@author S. V. Paulauskas +///@date December 29, 2016 +#include +#include + +#include + +#include "HelperEnumerations.hpp" +#include "XiaListModeDataMask.hpp" + +using namespace std; +using namespace DataProcessing; + +//Test that we can convert all the firmware names to the right values. +TEST_FIXTURE(XiaListModeDataMask, TestConvertStringToFirmware) { + //Check the exact names. + CHECK_EQUAL(R17562, ConvertStringToFirmware("R17562")); + CHECK_EQUAL(R17562, ConvertStringToFirmware("17562")); + + CHECK_EQUAL(R20466, ConvertStringToFirmware("R20466")); + CHECK_EQUAL(R20466, ConvertStringToFirmware("20466")); + + CHECK_EQUAL(R27361, ConvertStringToFirmware("R27361")); + CHECK_EQUAL(R27361, ConvertStringToFirmware("27361")); + + CHECK_EQUAL(R29432, ConvertStringToFirmware("R29432")); + CHECK_EQUAL(R29432, ConvertStringToFirmware("29432")); + + CHECK_EQUAL(R30474, ConvertStringToFirmware("R30474")); + CHECK_EQUAL(R30474, ConvertStringToFirmware("30474")); + + CHECK_EQUAL(R30980, ConvertStringToFirmware("R30980")); + CHECK_EQUAL(R30980, ConvertStringToFirmware("30980")); + + CHECK_EQUAL(R30981, ConvertStringToFirmware("R30981")); + CHECK_EQUAL(R30981, ConvertStringToFirmware("30981")); + + CHECK_EQUAL(R34688, ConvertStringToFirmware("R34688")); + CHECK_EQUAL(R34688, ConvertStringToFirmware("34688")); + + //Check values in between numbers + CHECK_EQUAL(R17562, ConvertStringToFirmware("19000")); + CHECK_EQUAL(R20466, ConvertStringToFirmware("23000")); + CHECK_EQUAL(R27361, ConvertStringToFirmware("28000")); + CHECK_EQUAL(R29432, ConvertStringToFirmware("29700")); + CHECK_EQUAL(R30474, ConvertStringToFirmware("30670")); + CHECK_EQUAL(R30981, ConvertStringToFirmware("32000")); + + //Two cases for absolute failure of the method is when we have a firmware + // version that is higher than the highest known one, and a version + // smaller than the smallest known version. + CHECK_THROW(ConvertStringToFirmware("45000"), invalid_argument); + CHECK_THROW(ConvertStringToFirmware("12"), invalid_argument); +} + +TEST_FIXTURE(XiaListModeDataMask, TestXiaListModeDataMask) { + //We do not need to test more than on version of these since they are + // identical across all firmware versions. + SetFirmware(R30474); + CHECK_EQUAL((unsigned int) 0x0000000F, GetChannelNumberMask().first); + CHECK_EQUAL((unsigned int) 0, GetChannelNumberMask().second); + + CHECK_EQUAL((unsigned int) 0x000000F0, GetSlotIdMask().first); + CHECK_EQUAL((unsigned int) 4, GetSlotIdMask().second); + + CHECK_EQUAL((unsigned int) 0x00000F00, GetCrateIdMask().first); + CHECK_EQUAL((unsigned int) 8, GetCrateIdMask().second); + + CHECK_EQUAL((unsigned int) 0x0001F000, GetHeaderLengthMask().first); + CHECK_EQUAL((unsigned int) 12, GetHeaderLengthMask().second); + + CHECK_EQUAL((unsigned int) 0x80000000, GetFinishCodeMask().first); + CHECK_EQUAL((unsigned int) 31, GetFinishCodeMask().second); + + CHECK_EQUAL((unsigned int) 0x0000FFFF, GetEventTimeHighMask().first); + CHECK_EQUAL((unsigned int) 0, GetEventTimeHighMask().second); +} + +///Tests for the 100 MHz versions +TEST_FIXTURE(XiaListModeDataMask, Test_100MSps_Word2) { + SetFrequency(100); + + SetFirmware(R29432); + CHECK_EQUAL((unsigned int) 0xFFFF0000, GetCfdFractionalTimeMask().first); + CHECK_EQUAL((unsigned int) 16, GetCfdFractionalTimeMask().second); + + CHECK_EQUAL((unsigned int) 0, GetCfdForcedTriggerBitMask().first); + CHECK_EQUAL((unsigned int) 0, GetCfdForcedTriggerBitMask().second); + + CHECK_EQUAL((unsigned int) 0, GetCfdTriggerSourceMask().first); + CHECK_EQUAL((unsigned int) 0, GetCfdTriggerSourceMask().second); + + vector firm = {R30474, R30980, R30981, R34688}; + for (vector::iterator it = firm.begin(); it != firm.end(); it++) { + SetFirmware(*it); + CHECK_EQUAL((unsigned int) 0x7FFF0000, + GetCfdFractionalTimeMask().first); + CHECK_EQUAL((unsigned int) 16, GetCfdFractionalTimeMask().second); + + CHECK_EQUAL((unsigned int) 0x80000000, + GetCfdForcedTriggerBitMask().first); + CHECK_EQUAL((unsigned int) 31, GetCfdForcedTriggerBitMask().second); + + CHECK_EQUAL((unsigned int) 0, GetCfdTriggerSourceMask().first); + CHECK_EQUAL((unsigned int) 0, GetCfdTriggerSourceMask().second); + } +} + +///Tests for the 250 MHz versions +TEST_FIXTURE(XiaListModeDataMask, Test_250Msps_Word2) { + SetFrequency(250); + + SetFirmware(R29432); + CHECK_EQUAL((unsigned int) 0x7FFF0000, GetCfdFractionalTimeMask().first); + CHECK_EQUAL((unsigned int) 16, GetCfdFractionalTimeMask().second); + + CHECK_EQUAL((unsigned int) 0, GetCfdForcedTriggerBitMask().first); + CHECK_EQUAL((unsigned int) 0, GetCfdForcedTriggerBitMask().second); + + CHECK_EQUAL((unsigned int) 0x80000000, GetCfdTriggerSourceMask().first); + CHECK_EQUAL((unsigned int) 31, GetCfdTriggerSourceMask().second); + + vector firm = {R30474, R30980, R30981, R34688}; + for (vector::iterator it = firm.begin(); it != firm.end(); it++) { + SetFirmware(*it); + CHECK_EQUAL((unsigned int) 0x3FFF0000, + GetCfdFractionalTimeMask().first); + CHECK_EQUAL((unsigned int) 16, GetCfdFractionalTimeMask().second); + + CHECK_EQUAL((unsigned int) 0x80000000, + GetCfdForcedTriggerBitMask().first); + CHECK_EQUAL((unsigned int) 31, GetCfdForcedTriggerBitMask().second); + + CHECK_EQUAL((unsigned int) 0x40000000, GetCfdTriggerSourceMask().first); + CHECK_EQUAL((unsigned int) 30, GetCfdTriggerSourceMask().second); + } +} + +///Tests for the 500 MHz versions +TEST_FIXTURE(XiaListModeDataMask, Test_500MSps_Word2) { + SetFrequency(500); + + vector firm = {R29432, R30474, R30980, R30981, R34688}; + + for (vector::iterator it = firm.begin(); it != firm.end(); it++) { + SetFirmware(*it); + CHECK_EQUAL((unsigned int) 0x1FFF0000, + GetCfdFractionalTimeMask().first); + CHECK_EQUAL((unsigned int) 16, GetCfdFractionalTimeMask().second); + + CHECK_EQUAL((unsigned int) 0, GetCfdForcedTriggerBitMask().first); + CHECK_EQUAL((unsigned int) 0, GetCfdForcedTriggerBitMask().second); + + CHECK_EQUAL((unsigned int) 0xE0000000, GetCfdTriggerSourceMask().first); + CHECK_EQUAL((unsigned int) 29, GetCfdTriggerSourceMask().second); + } +} + +TEST_FIXTURE(XiaListModeDataMask, Test_R29432_To_R30981_Word3) { + vector freq = {100, 250, 500}; + + vector firm = {R29432, R30474, R30980, R30981}; + + for (vector::iterator it = firm.begin(); it != firm.end(); it++) { + SetFirmware(*it); + for (vector::iterator it1 = freq.begin(); + it1 != freq.end(); it1++) { + SetFrequency(*it1); + CHECK_EQUAL((unsigned int) 0x00007FFF, GetEventEnergyMask().first); + CHECK_EQUAL((unsigned int) 0, GetEventEnergyMask().second); + + CHECK_EQUAL((unsigned int) 0x00008000, + GetTraceOutOfRangeFlagMask().first); + CHECK_EQUAL((unsigned int) 15, GetTraceOutOfRangeFlagMask().second); + + CHECK_EQUAL((unsigned int) 0xFFFF0000, GetTraceLengthMask().first); + CHECK_EQUAL((unsigned int) 16, GetTraceLengthMask().second); + } + } +} + +TEST_FIXTURE(XiaListModeDataMask, Test_R34688_Word3) { + SetFirmware(R34688); + vector freq = {100, 250, 500}; + for (vector::iterator it = freq.begin(); + it != freq.end(); it++) { + SetFrequency(*it); + CHECK_EQUAL((unsigned int) 0x0000FFFF, GetEventEnergyMask().first); + CHECK_EQUAL((unsigned int) 0, GetEventEnergyMask().second); + + CHECK_EQUAL((unsigned int) 0x80000000, + GetTraceOutOfRangeFlagMask().first); + CHECK_EQUAL((unsigned int) 31, GetTraceOutOfRangeFlagMask().second); + + CHECK_EQUAL((unsigned int) 0x7FFF0000, GetTraceLengthMask().first); + CHECK_EQUAL((unsigned int) 16, GetTraceLengthMask().second); + } +} + +TEST_FIXTURE(XiaListModeDataMask, Test_Cfd_Size_Mask) { + //This firmware has a unique CFD size. + SetFirmware(R29432); + SetFrequency(100); + CHECK_EQUAL(65536, GetCfdSize()); + //This firmware has a different format for the 250 MS/s + SetFrequency(250); + CHECK_EQUAL(32768, GetCfdSize()); + + + //All of the 500 MS/s modules have the same sized CFD. + SetFrequency(500); + vector firm = {R29432, R30474, R30980, R30981, R34688}; + for (vector::iterator it = firm.begin(); it != firm.end(); it++) { + SetFirmware(*it); + CHECK_EQUAL(8192, GetCfdSize()); + } + + //The 100 MHz and 250 MS/s revisions have the same structure for the + // following four firmwares + firm = {R30474, R30980, R30981, R34688}; + SetFrequency(100); + for (vector::iterator it = firm.begin(); it != firm.end(); it++) { + SetFirmware(*it); + CHECK_EQUAL(32768, GetCfdSize()); + } + + SetFrequency(250); + for (vector::iterator it = firm.begin(); it != firm.end(); it++) { + SetFirmware(*it); + CHECK_EQUAL(16384, GetCfdSize()); + } +} + +int main(int argv, char *argc[]) { + return (UnitTest::RunAllTests()); +} \ No newline at end of file diff --git a/Scan/ScanLib/CMakeLists.txt b/Analysis/Scanor/CMakeLists.txt similarity index 71% rename from Scan/ScanLib/CMakeLists.txt rename to Analysis/Scanor/CMakeLists.txt index 33d664731..6dc9de94a 100644 --- a/Scan/ScanLib/CMakeLists.txt +++ b/Analysis/Scanor/CMakeLists.txt @@ -1,6 +1,6 @@ #Install include directories to support the shared library. -if(BUILD_SHARED_LIBS) +if(PAASS_BUILD_SHARED_LIBS) install(DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX}) -endif(BUILD_SHARED_LIBS) +endif(PAASS_BUILD_SHARED_LIBS) add_subdirectory(source) diff --git a/Scan/scanor/include/GetArguments.hpp b/Analysis/Scanor/include/GetArguments.hpp similarity index 100% rename from Scan/scanor/include/GetArguments.hpp rename to Analysis/Scanor/include/GetArguments.hpp diff --git a/Scan/scanor/include/Scanor.hpp b/Analysis/Scanor/include/Scanor.hpp similarity index 100% rename from Scan/scanor/include/Scanor.hpp rename to Analysis/Scanor/include/Scanor.hpp diff --git a/Scan/scanor/include/ScanorInterface.hpp b/Analysis/Scanor/include/ScanorInterface.hpp similarity index 100% rename from Scan/scanor/include/ScanorInterface.hpp rename to Analysis/Scanor/include/ScanorInterface.hpp diff --git a/Scan/scanor/source/CMakeLists.txt b/Analysis/Scanor/source/CMakeLists.txt similarity index 91% rename from Scan/scanor/source/CMakeLists.txt rename to Analysis/Scanor/source/CMakeLists.txt index e96a0d8e8..e31bfe516 100644 --- a/Scan/scanor/source/CMakeLists.txt +++ b/Analysis/Scanor/source/CMakeLists.txt @@ -17,7 +17,7 @@ set(SCANOR_SOURCES add_library(ScanorObjects OBJECT ${SCANOR_SOURCES}) -if(BUILD_SHARED_LIBS) +if(PAASS_BUILD_SHARED_LIBS) add_library(ScanHHIRF SHARED $) install(TARGETS ScanHHIRF DESTINATION lib) -endif(BUILD_SHARED_LIBS) +endif(PAASS_BUILD_SHARED_LIBS) diff --git a/Scan/scanor/source/GetArguments.cpp b/Analysis/Scanor/source/GetArguments.cpp similarity index 100% rename from Scan/scanor/source/GetArguments.cpp rename to Analysis/Scanor/source/GetArguments.cpp diff --git a/Scan/scanor/source/Scanor.cpp b/Analysis/Scanor/source/Scanor.cpp similarity index 100% rename from Scan/scanor/source/Scanor.cpp rename to Analysis/Scanor/source/Scanor.cpp diff --git a/Scan/scanor/source/ScanorInterface.cpp b/Analysis/Scanor/source/ScanorInterface.cpp similarity index 100% rename from Scan/scanor/source/ScanorInterface.cpp rename to Analysis/Scanor/source/ScanorInterface.cpp diff --git a/Scan/scanor/source/messlog.f b/Analysis/Scanor/source/messlog.f similarity index 100% rename from Scan/scanor/source/messlog.f rename to Analysis/Scanor/source/messlog.f diff --git a/Scan/scanor/source/mildatim.f b/Analysis/Scanor/source/mildatim.f similarity index 100% rename from Scan/scanor/source/mildatim.f rename to Analysis/Scanor/source/mildatim.f diff --git a/Scan/scanor/source/scanof.f b/Analysis/Scanor/source/scanof.f similarity index 100% rename from Scan/scanor/source/scanof.f rename to Analysis/Scanor/source/scanof.f diff --git a/Scan/scanor/source/scanor.f b/Analysis/Scanor/source/scanor.f similarity index 100% rename from Scan/scanor/source/scanor.f rename to Analysis/Scanor/source/scanor.f diff --git a/Scan/scanor/source/scanorux.f b/Analysis/Scanor/source/scanorux.f similarity index 100% rename from Scan/scanor/source/scanorux.f rename to Analysis/Scanor/source/scanorux.f diff --git a/Scan/scanor/source/set2cc.f b/Analysis/Scanor/source/set2cc.f similarity index 100% rename from Scan/scanor/source/set2cc.f rename to Analysis/Scanor/source/set2cc.f diff --git a/Analysis/Utilities/CMakeLists.txt b/Analysis/Utilities/CMakeLists.txt new file mode 100644 index 000000000..b75c966ca --- /dev/null +++ b/Analysis/Utilities/CMakeLists.txt @@ -0,0 +1,10 @@ +if(PAASS_USE_ROOT) + add_subdirectory(Scope) +endif(PAASS_USE_ROOT) + +if(PAASS_BUILD_SKELETON) + add_subdirectory(Skeleton) +endif(PAASS_BUILD_SKELETON) + +add_subdirectory(HeadReader) +add_subdirectory(TraceFilterer) \ No newline at end of file diff --git a/Analysis/Utilities/HeadReader/CMakeLists.txt b/Analysis/Utilities/HeadReader/CMakeLists.txt new file mode 100644 index 000000000..2b2b548bf --- /dev/null +++ b/Analysis/Utilities/HeadReader/CMakeLists.txt @@ -0,0 +1,2 @@ +include_directories(include) +add_subdirectory(source) \ No newline at end of file diff --git a/Analysis/Utilities/HeadReader/source/CMakeLists.txt b/Analysis/Utilities/HeadReader/source/CMakeLists.txt new file mode 100644 index 000000000..c22593ad9 --- /dev/null +++ b/Analysis/Utilities/HeadReader/source/CMakeLists.txt @@ -0,0 +1,4 @@ +# Install headReader executable. +add_executable(headReader headReader.cpp) +target_link_libraries(headReader PaassScanStatic) +install (TARGETS headReader DESTINATION bin) \ No newline at end of file diff --git a/Scan/util/source/headReader.cpp b/Analysis/Utilities/HeadReader/source/headReader.cpp similarity index 100% rename from Scan/util/source/headReader.cpp rename to Analysis/Utilities/HeadReader/source/headReader.cpp diff --git a/Analysis/Utilities/Scope/CMakeLists.txt b/Analysis/Utilities/Scope/CMakeLists.txt new file mode 100644 index 000000000..2b2b548bf --- /dev/null +++ b/Analysis/Utilities/Scope/CMakeLists.txt @@ -0,0 +1,2 @@ +include_directories(include) +add_subdirectory(source) \ No newline at end of file diff --git a/Scan/util/include/scope.hpp b/Analysis/Utilities/Scope/include/scope.hpp similarity index 89% rename from Scan/util/include/scope.hpp rename to Analysis/Utilities/Scope/include/scope.hpp index d4144a79e..630510fb5 100644 --- a/Scan/util/include/scope.hpp +++ b/Analysis/Utilities/Scope/include/scope.hpp @@ -7,18 +7,17 @@ #include #include -// PixieCore libraries #include "Unpacker.hpp" -#include "ScanInterface.hpp" -class ChannelEvent; -class TApplication; -class TCanvas; +#include "RootScanner.hpp" + +class ProcessedXiaData; class TGraph; class TH2F; class TF1; class TLine; class TProfile; +class VandleTimingFunction; /////////////////////////////////////////////////////////////////////////////// // class scopeUnpacker @@ -72,7 +71,7 @@ class scopeUnpacker : public Unpacker { // class scopeScanner /////////////////////////////////////////////////////////////////////////////// -class scopeScanner : public ScanInterface { +class scopeScanner : public RootScanner { public: /// Default constructor. scopeScanner(int mod = 0, int chan = 0); @@ -137,14 +136,6 @@ class scopeScanner : public ScanInterface { */ virtual void SyntaxStr(char *name_); - /** IdleTask is called whenever a scan is running in shared - * memory mode, and a spill has yet to be received. This method may - * be used to update things which need to be updated every so often - * (e.g. a root TCanvas) when working with a low data rate. - * \return Nothing. - */ - virtual void IdleTask(); - /** Initialize the map file, the config file, the processor handler, * and add all of the required processors. * \param[in] prefix_ String to append to the beginning of system output. @@ -217,21 +208,21 @@ class scopeScanner : public ScanInterface { bool performCfd_; std::vector x_vals; - std::deque chanEvents_; /// chanEvents_; ///) + target_link_libraries(scope ${HRIBF_LIBRARIES}) +else() + add_executable(scope scope.cpp) +endif(PAASS_USE_HRIBF) + +target_link_libraries(scope ResourceStatic PaassScanStatic ${ROOT_LIBRARIES}) +install(TARGETS scope DESTINATION bin) \ No newline at end of file diff --git a/Scan/util/source/scope.cpp b/Analysis/Utilities/Scope/source/scope.cpp similarity index 50% rename from Scan/util/source/scope.cpp rename to Analysis/Utilities/Scope/source/scope.cpp index a6207a858..5e443f61a 100644 --- a/Scan/util/source/scope.cpp +++ b/Analysis/Utilities/Scope/source/scope.cpp @@ -1,11 +1,13 @@ -#include #include +#include // PixieCore libraries -#include "XiaData.hpp" +#include "ProcessedXiaData.hpp" +#include "HelperFunctions.hpp" // Local files #include "scope.hpp" +#include "VandleTimingFunction.hpp" #ifdef USE_HRIBF #include "GetArguments.hpp" @@ -14,11 +16,9 @@ #endif // Root files -#include "TApplication.h" #include "TSystem.h" #include "TStyle.h" #include "TMath.h" -#include "TCanvas.h" #include "TGraph.h" #include "TH2F.h" #include "TAxis.h" @@ -34,33 +34,9 @@ #endif #define ADC_TIME_STEP 4 // ns -#define SLEEP_WAIT 1E4 // When not in shared memory mode, length of time to wait after gSystem->ProcessEvents is called (in us). - -/**The Paulauskas function is described in NIM A 737 (22), with a slight - * adaptation. We use a step function such that f(x < phase) = baseline. - * In addition, we also we formulate gamma such that the gamma in the paper is - * gamma_prime = 1 / pow(gamma, 0.25). - * - * The parameters are: - * p[0] = baseline - * p[1] = amplitude - * p[2] = phase - * p[3] = beta - * p[4] = gamma - * - * \param[in] x X value. - * \param[in] p Paramater values. - * - * \return the value of the function for the specified x value and parameters. - */ -double PaulauskasFitFunc(double *x, double *p) { - //Compute the time difference between x and the phase corrected for clock ticks. - float diff = (x[0] - p[2])/ADC_TIME_STEP; - //If the difference is less than zero we return the baseline. - if (diff < 0 ) return p[0]; - //Return the computed function. - return p[0] + p[1] * exp(-diff * p[3]) * (1 - exp(-pow(diff * p[4],4))); -} + +using namespace std; +using namespace TraceFunctions; /////////////////////////////////////////////////////////////////////////////// // class scopeUnpacker @@ -88,18 +64,20 @@ void scopeUnpacker::ProcessRawEvent(ScanInterface *addr_/*=NULL*/){ if(!running) break; - //Get the first event int he FIFO. + //Get the first event in the FIFO. current_event = rawEvent.front(); rawEvent.pop_front(); - // Safety catches for null event or empty adcTrace. - if(!current_event || current_event->adcTrace.empty()){ + // Safety catches for null event or empty ->GetTrace(). + if(!current_event || current_event->GetTrace().empty()){ continue; } // Pass this event to the correct processor - int maximum = *std::max_element(current_event->adcTrace.begin(),current_event->adcTrace.end()); - if(current_event->modNum == mod_ && current_event->chanNum == chan_){ + double maximum = + FindMaximum(current_event->GetTrace(), + current_event->GetTrace().size()).second; + if(current_event->GetModuleNumber() == mod_ && current_event->GetChannelNumber() == chan_){ //Check threhsold. if (maximum < threshLow_) { delete current_event; @@ -123,7 +101,7 @@ void scopeUnpacker::ProcessRawEvent(ScanInterface *addr_/*=NULL*/){ /////////////////////////////////////////////////////////////////////////////// /// Default constructor. -scopeScanner::scopeScanner(int mod /*= 0*/, int chan/*=0*/) : ScanInterface() { +scopeScanner::scopeScanner(int mod /*= 0*/, int chan/*=0*/) : RootScanner() { need_graph_update = false; resetGraph_ = false; acqRun_ = true; @@ -143,20 +121,17 @@ scopeScanner::scopeScanner(int mod /*= 0*/, int chan/*=0*/) : ScanInterface() { num_displayed = 0; time(&last_trace); - // Variables for root graphics - rootapp = new TApplication("scope", 0, NULL); - gSystem->Load("libTree"); - - canvas = new TCanvas("scope_canvas", "scopeScanner"); - graph = new TGraph(); - cfdGraph = new TGraph(); + hist = new TH2F("hist","",256,0,1,256,0,1); + cfdLine = new TLine(); - cfdLine->SetLineColor(2); - - hist = new TH2F("hist","",256,0,1,256,0,1); + cfdLine->SetLineColor(kRed); + cfdPol3 = new TF1("cfdPol3", "pol3"); + cfdPol3->SetLineColor(kGreen+1); + cfdPol2 = new TF1("cfdPol2", "pol2"); + cfdPol2->SetLineColor(kMagenta+1); - SetupFunc(); + SetupFunc(); gStyle->SetPalette(51); @@ -169,42 +144,45 @@ scopeScanner::scopeScanner(int mod /*= 0*/, int chan/*=0*/) : ScanInterface() { /// Destructor. scopeScanner::~scopeScanner(){ - canvas->Close(); - delete canvas; delete graph; - delete cfdGraph; delete cfdLine; + delete cfdPol3; + delete cfdPol2; delete hist; - delete paulauskasFunc; + delete fittingFunction_; + delete vandleTimingFunction_; } TF1 *scopeScanner::SetupFunc() { - paulauskasFunc = new TF1("paulauskas",PaulauskasFitFunc,0,1,5); - paulauskasFunc->SetParNames("voffset","amplitude","phase","beta","gamma"); - - return paulauskasFunc; + vandleTimingFunction_ = new VandleTimingFunction(); + fittingFunction_ = + new TF1("func", vandleTimingFunction_, 0., 1.e6, 5); + fittingFunction_->SetParNames("phase", "amplitude","beta","gamma", + "baseline"); + return fittingFunction_; } void scopeScanner::ResetGraph(unsigned int size) { delete graph; - delete cfdGraph; graph = new TGraph(size); graph->SetMarkerStyle(kFullDotSmall); - cfdGraph = new TGraph(size); - cfdGraph->SetLineColor(4); - if(size != x_vals.size()){ - std::cout << msgHeader << "Changing trace length from " << x_vals.size()*ADC_TIME_STEP << " to " << size*ADC_TIME_STEP << " ns.\n"; + cout << msgHeader << "Changing trace length from " + << x_vals.size() << " to " << size + << " ns.\n"; x_vals.resize(size); for(size_t index = 0; index < x_vals.size(); index++) - x_vals[index] = ADC_TIME_STEP * index; + x_vals[index] = index; } - hist->SetBins(x_vals.size(), x_vals.front(), x_vals.back() + ADC_TIME_STEP, 1, 0, 1); - std::stringstream stream; - stream << "M" << ((scopeUnpacker*)core)->GetMod() << "C" << ((scopeUnpacker*)core)->GetChan(); + hist->SetBins(x_vals.size(), x_vals.front(), x_vals.back(), + 1, 0, 1); + + stringstream stream; + stream << "M" << ((scopeUnpacker*)core)->GetMod() << "C" + << ((scopeUnpacker*)core)->GetChan(); graph->SetTitle(stream.str().c_str()); hist->SetTitle(stream.str().c_str()); @@ -212,119 +190,105 @@ void scopeScanner::ResetGraph(unsigned int size) { } void scopeScanner::Plot(){ + static float histAxis[2][2]; + if(chanEvents_.size() < numAvgWaveforms_) return; - ///The limits of the vertical axis - static float axisVals[2][2]; //The max and min values of the graph, first index is the axis, second is the min / max - static float userZoomVals[2][2]; - static bool userZoom[2]; + unsigned int traceSize = chanEvents_.front()->GetTrace().size(); - //Get the user zoom settings. - userZoomVals[0][0] = canvas->GetUxmin(); - userZoomVals[0][1] = canvas->GetUxmax(); - userZoomVals[1][0] = canvas->GetUymin(); - userZoomVals[1][1] = canvas->GetUymax(); - - if(chanEvents_.front()->size != x_vals.size()){ // The length of the trace has changed. + if(traceSize != x_vals.size()) resetGraph_ = true; - } + if (resetGraph_) { - ResetGraph(chanEvents_.front()->size); + ResetGraph(traceSize); + ResetZoom(); for (int i=0;i<2;i++) { - axisVals[i][0] = 1E9; - axisVals[i][1] = -1E9; - userZoomVals[i][0] = 1E9; - userZoomVals[i][1] = -1E9; - userZoom[i] = false; - } - } - - //Determine if the user had zoomed or unzoomed. - for (int i=0; i<2; i++) { - userZoom[i] = (userZoomVals[i][0] != axisVals[i][0] || userZoomVals[i][1] != axisVals[i][1]); + histAxis[i][0] = 1E9; + histAxis[i][1] = -1E9; + } } - //For a waveform pulse we use a graph. if (numAvgWaveforms_ == 1) { int index = 0; - for (size_t i = 0; i < chanEvents_.front()->size; ++i) { - graph->SetPoint(index, x_vals[i], chanEvents_.front()->event->adcTrace.at(i)); - index++; - } + for (size_t i = 0; i < traceSize; ++i, index++) + graph->SetPoint(index, x_vals[i], chanEvents_.front()->GetTrace().at(i)); - //Get and set the updated graph limits. - if (graph->GetXaxis()->GetXmin() < axisVals[0][0]) axisVals[0][0] = graph->GetXaxis()->GetXmin(); - if (graph->GetXaxis()->GetXmax() > axisVals[0][1]) axisVals[0][1] = graph->GetXaxis()->GetXmax(); - graph->GetXaxis()->SetLimits(axisVals[0][0], axisVals[0][1]); - - if (graph->GetYaxis()->GetXmin() < axisVals[1][0]) axisVals[1][0] = graph->GetYaxis()->GetXmin(); - if (graph->GetYaxis()->GetXmax() > axisVals[1][1]) axisVals[1][1] = graph->GetYaxis()->GetXmax(); - graph->GetYaxis()->SetLimits(axisVals[1][0], axisVals[1][1]); - - //Set the users zoom window. - for (int i = 0; i < 2; i++) { - if (!userZoom[i]) { - for (int j = 0; j < 2; j++) userZoomVals[i][j] = axisVals[i][j]; - } - } - graph->GetXaxis()->SetRangeUser(userZoomVals[0][0], userZoomVals[0][1]); - graph->GetYaxis()->SetRangeUser(userZoomVals[1][0], userZoomVals[1][1]); + UpdateZoom(); graph->Draw("AP0"); - float lowVal = (chanEvents_.front()->max_index - fitLow_) * ADC_TIME_STEP; - float highVal = (chanEvents_.front()->max_index + fitHigh_) * ADC_TIME_STEP; + float lowVal = (chanEvents_.front()->GetTrace().GetMaxInfo().first - + fitLow_); + float highVal = (chanEvents_.front()->GetTrace().GetMaxInfo().first + + fitHigh_); + ///@TODO Renable the CFD with the proper functionality. + /* if(performCfd_){ + ProcessedXiaData *evt = chanEvents_.front(); + // Find the zero-crossing of the cfd waveform. - float cfdCrossing = chanEvents_.front()->AnalyzeCFD(cfdF_, cfdD_, cfdL_); + float cfdCrossing = evt->AnalyzeCFD(cfdF_); - // Draw the cfd waveform. - for(size_t cfdIndex = 0; cfdIndex < chanEvents_.front()->size; cfdIndex++) - cfdGraph->SetPoint((int)cfdIndex, x_vals[cfdIndex], chanEvents_.front()->cfdvals[cfdIndex] + chanEvents_.front()->baseline); + // Draw the cfd crossing line. cfdLine->DrawLine(cfdCrossing*ADC_TIME_STEP, userZoomVals[1][0], cfdCrossing*ADC_TIME_STEP, userZoomVals[1][1]); - cfdGraph->Draw("LSAME"); + + // Draw the 3rd order polynomial. + cfdPol3->SetParameter(0, evt->cfdPar[0]); + cfdPol3->SetParameter(1, evt->cfdPar[1]/ADC_TIME_STEP); + cfdPol3->SetParameter(2, evt->cfdPar[2]/pow(ADC_TIME_STEP, 2.0)); + cfdPol3->SetParameter(3, evt->cfdPar[3]/pow(ADC_TIME_STEP, 3.0)); + // Find the pulse maximum by fitting with a third order polynomial. + if(evt->event->adcTrace[evt->max_index-1] >= evt->event->adcTrace[evt->max_index+1]) // Favor the left side of the pulse. + cfdPol3->SetRange((evt->max_index - 2)*ADC_TIME_STEP, (evt->max_index + 1)*ADC_TIME_STEP); + else // Favor the right side of the pulse. + cfdPol3->SetRange((evt->max_index - 1)*ADC_TIME_STEP, (evt->max_index + 2)*ADC_TIME_STEP); + cfdPol3->Draw("SAME"); + + // Draw the 2nd order polynomial. + cfdPol2->SetParameter(0, evt->cfdPar[4]); + cfdPol2->SetParameter(1, evt->cfdPar[5]/ADC_TIME_STEP); + cfdPol2->SetParameter(2, evt->cfdPar[6]/pow(ADC_TIME_STEP, 2.0)); + cfdPol2->SetRange((evt->cfdIndex - 1)*ADC_TIME_STEP, (evt->cfdIndex + 1)*ADC_TIME_STEP); + cfdPol2->Draw("SAME"); } + */ if(performFit_){ - paulauskasFunc->SetRange(lowVal, highVal); - paulauskasFunc->SetParameters(chanEvents_.front()->baseline, 0.5 * chanEvents_.front()->qdc, lowVal, 0.5, 0.1); - paulauskasFunc->FixParameter(0, chanEvents_.front()->baseline); - graph->Fit(paulauskasFunc,"QMER"); - } - } - else { //For multiple events with make a 2D histogram and plot the profile on top. + fittingFunction_->SetParameters(lowVal, + 0.5 * chanEvents_.front()->GetTrace().GetQdc(), 0.3, 0.1); + fittingFunction_->FixParameter( + 4, chanEvents_.front()->GetTrace().GetBaselineInfo().first); + graph->Fit(fittingFunction_, "WRQ", "", lowVal, highVal); + } + } else { + //For multiple events with make a 2D histogram and plot the profile on top. //Determine the maximum and minimum values of the events. for (unsigned int i = 0; i < numAvgWaveforms_; i++) { - ChannelEvent* evt = chanEvents_.at(i); - float evtMin = *std::min_element(evt->event->adcTrace.begin(), evt->event->adcTrace.end()); - float evtMax = *std::max_element(evt->event->adcTrace.begin(), evt->event->adcTrace.end()); + ProcessedXiaData* evt = chanEvents_.at(i); + float evtMin = *min_element(evt->GetTrace().begin(), evt->GetTrace().end()); + float evtMax = *max_element(evt->GetTrace().begin(), evt->GetTrace().end()); evtMin -= fabs(0.1 * evtMax); evtMax += fabs(0.1 * evtMax); - if (evtMin < axisVals[1][0]) axisVals[1][0] = evtMin; - if (evtMax > axisVals[1][1]) axisVals[1][1] = evtMax; - } - - //Set the users zoom window. - for (int i=0; i<2; i++) { - if (!userZoom[i]) { - for (int j=0; j<2; j++) - userZoomVals[i][j] = axisVals[i][j]; - } + if (evtMin < histAxis[1][0]) histAxis[1][0] = evtMin; + if (evtMax > histAxis[1][1]) histAxis[1][1] = evtMax; } //Reset the histogram hist->Reset(); //Rebin the histogram - hist->SetBins(x_vals.size(), x_vals.front(), x_vals.back() + ADC_TIME_STEP, axisVals[1][1] - axisVals[1][0], axisVals[1][0], axisVals[1][1]); + hist->SetBins(x_vals.size(), x_vals.front(), + x_vals.back(), + histAxis[1][1] - histAxis[1][0], + histAxis[1][0], histAxis[1][1]); //Fill the histogram for (unsigned int i = 0; i < numAvgWaveforms_; i++) { - ChannelEvent* evt = chanEvents_.at(i); - for (size_t i=0; i < evt->size; ++i) { - hist->Fill(x_vals[i], evt->event->adcTrace[i]); + ProcessedXiaData* evt = chanEvents_.at(i); + for (size_t i=0; i < evt->GetTrace().size(); ++i) { + hist->Fill(x_vals[i], evt->GetTrace()[i]); } } @@ -332,24 +296,24 @@ void scopeScanner::Plot(){ prof->SetLineColor(kRed); prof->SetMarkerColor(kRed); - float lowVal = prof->GetBinCenter(prof->GetMaximumBin() - fitLow_); - float highVal = prof->GetBinCenter(prof->GetMaximumBin() + fitHigh_); + double lowVal = prof->GetBinCenter(prof->GetMaximumBin() - fitLow_); + double highVal = prof->GetBinCenter(prof->GetMaximumBin() + fitHigh_); if(performFit_){ - paulauskasFunc->SetRange(lowVal, highVal); - paulauskasFunc->SetParameters(chanEvents_.front()->baseline, 0.5 * chanEvents_.front()->qdc, lowVal, 0.5, 0.2); - paulauskasFunc->FixParameter(0, chanEvents_.front()->baseline); - prof->Fit(paulauskasFunc,"QMER"); + fittingFunction_->SetParameters(lowVal, + 0.5 * chanEvents_.front()->GetTrace().GetQdc(), 0.3, 0.1); + fittingFunction_->FixParameter( + 4, chanEvents_.front()->GetTrace().GetBaselineInfo().first); + hist->Fit(fittingFunction_, "WRQ", "", lowVal, highVal); } hist->SetStats(false); hist->Draw("COLZ"); prof->Draw("SAMES"); - hist->GetXaxis()->SetRangeUser(userZoomVals[0][0], userZoomVals[0][1]); - hist->GetYaxis()->SetRangeUser(userZoomVals[1][0], userZoomVals[1][1]); + UpdateZoom(); - canvas->Update(); + GetCanvas()->Update(); TPaveStats* stats = (TPaveStats*) prof->GetListOfFunctions()->FindObject("stats"); if (stats) { stats->SetX1NDC(0.55); @@ -364,7 +328,7 @@ void scopeScanner::Plot(){ } // Update the canvas. - canvas->Update(); + GetCanvas()->Update(); // Save the TGraph to a file. if (saveFile_ != "") { @@ -382,11 +346,12 @@ void scopeScanner::Plot(){ * \param[in] prefix_ String to append to the beginning of system output. * \return True upon successfully initializing and false otherwise. */ -bool scopeScanner::Initialize(std::string prefix_){ +bool scopeScanner::Initialize(string prefix_){ if(init){ return false; } // Print a small welcome message. - std::cout << " Displaying traces for mod = " << ((scopeUnpacker*)core)->GetMod() << ", chan = " << ((scopeUnpacker*)core)->GetChan() << ".\n"; + cout << " Displaying traces for mod = " << ((scopeUnpacker*)core)->GetMod() + << ", chan = " << ((scopeUnpacker*)core)->GetChan() << ".\n"; return (init = true); } @@ -395,16 +360,16 @@ bool scopeScanner::Initialize(std::string prefix_){ * \param[in] code_ The notification code passed from ScanInterface methods. * \return Nothing. */ -void scopeScanner::Notify(const std::string &code_/*=""*/){ +void scopeScanner::Notify(const string &code_/*=""*/){ if(code_ == "START_SCAN"){ ClearEvents(); acqRun_ = true; } else if(code_ == "STOP_SCAN"){ acqRun_ = false; } - else if(code_ == "SCAN_COMPLETE"){ std::cout << msgHeader << "Scan complete.\n"; } - else if(code_ == "LOAD_FILE"){ std::cout << msgHeader << "File loaded.\n"; } + else if(code_ == "SCAN_COMPLETE"){ cout << msgHeader << "Scan complete.\n"; } + else if(code_ == "LOAD_FILE"){ cout << msgHeader << "File loaded.\n"; } else if(code_ == "REWIND_FILE"){ } - else{ std::cout << msgHeader << "Unknown notification code '" << code_ << "'!\n"; } + else{ cout << msgHeader << "Unknown notification code '" << code_ << "'!\n"; } } /** Return a pointer to the Unpacker object to use for data unpacking. @@ -424,14 +389,25 @@ Unpacker *scopeScanner::GetCore(){ bool scopeScanner::AddEvent(XiaData *event_){ if(!event_){ return false; } - //Get the first event int the FIFO. - ChannelEvent *channel_event = new ChannelEvent(event_); + //Get the firs + // t event int the FIFO. + ProcessedXiaData *channel_event = new ProcessedXiaData(*event_); //Process the waveform. - //channel_event->FindLeadingEdge(); - channel_event->CorrectBaseline(); - channel_event->FindQDC(); - + ///@TODO This needs cleaned up quite a bit to make it more generalized + /// and remove some of the magic numbers. + channel_event->GetTrace().SetBaseline(CalculateBaseline + (channel_event->GetTrace(), + make_pair(0, 10))); + + channel_event->GetTrace().SetMax(FindMaximum + (channel_event->GetTrace(), + channel_event->GetTrace().size())); + + channel_event->GetTrace().SetQdc(CalculateQdc + (channel_event->GetTrace(), + make_pair(5,15))); + //Push the channel event into the deque. chanEvents_.push_back(channel_event); @@ -487,19 +463,19 @@ void scopeScanner::ClearEvents(){ * \param[in] prefix_ String to append at the start of any output. Not used by default. * \return Nothing. */ -void scopeScanner::CmdHelp(const std::string &prefix_/*=""*/){ - std::cout << " set - Set the module and channel of signal of interest (default = 0, 0).\n"; - std::cout << " stop - Stop the acquistion.\n"; - std::cout << " run - Run the acquistion.\n"; - std::cout << " single - Perform a single capture.\n"; - std::cout << " thresh [high] - Set the plotting window for trace maximum.\n"; - std::cout << " fit - Turn on fitting of waveform. Set to \"off\" to disable.\n"; - std::cout << " cfd [F=0.5] [D=1] [L=1] - Turn on cfd analysis of waveform. Set [F] to \"off\" to disable.\n"; - std::cout << " avg - Set the number of waveforms to average.\n"; - std::cout << " save - Save the next trace to the specified file name..\n"; - std::cout << " delay [time] - Set the delay between drawing traces (in seconds, default = 1 s).\n"; - std::cout << " log - Toggle log/linear mode on the y-axis.\n"; - std::cout << " clear - Clear all stored traces and start over.\n"; +void scopeScanner::CmdHelp(const string &prefix_/*=""*/){ + cout << " set - Set the module and channel of signal of interest (default = 0, 0).\n"; + cout << " stop - Stop the acquistion.\n"; + cout << " run - Run the acquistion.\n"; + cout << " single - Perform a single capture.\n"; + cout << " thresh [high] - Set the plotting window for trace maximum.\n"; + cout << " fit - Turn on fitting of waveform. Set to \"off\" to disable.\n"; + cout << " cfd [F=0.5] [D=1] [L=1] - Turn on cfd analysis of waveform. Set [F] to \"off\" to disable.\n"; + cout << " avg - Set the number of waveforms to average.\n"; + cout << " save - Save the next trace to the specified file name..\n"; + cout << " delay [time] - Set the delay between drawing traces (in seconds, default = 1 s).\n"; + cout << " log - Toggle log/linear mode on the y-axis.\n"; + cout << " clear - Clear all stored traces and start over.\n"; } /** ArgHelp is used to allow a derived class to add a command line option @@ -518,7 +494,7 @@ void scopeScanner::ArgHelp(){ * \return Nothing. */ void scopeScanner::SyntaxStr(char *name_){ - std::cout << " usage: " << std::string(name_) << " [options]\n"; + cout << " usage: " << string(name_) << " [options]\n"; } /** ExtraArguments is used to send command line arguments to classes derived @@ -529,9 +505,9 @@ void scopeScanner::SyntaxStr(char *name_){ */ void scopeScanner::ExtraArguments(){ if(userOpts.at(0).active) - std::cout << msgHeader << "Set module to (" << ((scopeUnpacker*)core)->SetMod(atoi(userOpts.at(0).argument.c_str())) << ").\n"; + cout << msgHeader << "Set module to (" << ((scopeUnpacker*)core)->SetMod(atoi(userOpts.at(0).argument.c_str())) << ").\n"; if(userOpts.at(1).active) - std::cout << msgHeader << "Set channel to (" << ((scopeUnpacker*)core)->SetChan(atoi(userOpts.at(1).argument.c_str())) << ").\n"; + cout << msgHeader << "Set channel to (" << ((scopeUnpacker*)core)->SetChan(atoi(userOpts.at(1).argument.c_str())) << ").\n"; } /** ExtraCommands is used to send command strings to classes derived @@ -541,7 +517,7 @@ void scopeScanner::ExtraArguments(){ * \param[out] arg_ Vector or arguments to the user command. * \return True if the command was recognized and false otherwise. */ -bool scopeScanner::ExtraCommands(const std::string &cmd_, std::vector &args_){ +bool scopeScanner::ExtraCommands(const string &cmd_, vector &args_){ if(cmd_ == "set"){ // Toggle debug mode if(args_.size() == 2){ // Clear all events from the event deque. @@ -554,8 +530,8 @@ bool scopeScanner::ExtraCommands(const std::string &cmd_, std::vector \n"; + cout << msgHeader << "Invalid number of parameters to 'set'\n"; + cout << msgHeader << " -SYNTAX- set \n"; } } else if(cmd_ == "single") { @@ -571,30 +547,30 @@ bool scopeScanner::ExtraCommands(const std::string &cmd_, std::vectorSetThreshHigh(atoi(args_.at(1).c_str())); } else { - std::cout << msgHeader << "Invalid number of parameters to 'thresh'\n"; - std::cout << msgHeader << " -SYNTAX- thresh [upperThresh]\n"; + cout << msgHeader << "Invalid number of parameters to 'thresh'\n"; + cout << msgHeader << " -SYNTAX- thresh [upperThresh]\n"; } } else if (cmd_ == "fit") { if (args_.size() >= 1 && args_.at(0) == "off") { // Turn root fitting off. if(performFit_){ - std::cout << msgHeader << "Disabling root fitting.\n"; - delete graph->GetListOfFunctions()->FindObject(paulauskasFunc->GetName()); - canvas->Update(); + cout << msgHeader << "Disabling root fitting.\n"; + delete graph->GetListOfFunctions()->FindObject(fittingFunction_->GetName()); + GetCanvas()->Update(); performFit_ = false; } - else{ std::cout << msgHeader << "Fitting is not enabled.\n"; } + else{ cout << msgHeader << "Fitting is not enabled.\n"; } } else if (args_.size() == 2) { // Turn root fitting on. fitLow_ = atoi(args_.at(0).c_str()); fitHigh_ = atoi(args_.at(1).c_str()); - std::cout << msgHeader << "Setting root fitting range to [" << fitLow_ << ", " << fitHigh_ << "].\n"; + cout << msgHeader << "Setting root fitting range to [" << fitLow_ << ", " << fitHigh_ << "].\n"; performFit_ = true; } else { - std::cout << msgHeader << "Invalid number of parameters to 'fit'\n"; - std::cout << msgHeader << " -SYNTAX- fit \n"; - std::cout << msgHeader << " -SYNTAX- fit off\n"; + cout << msgHeader << "Invalid number of parameters to 'fit'\n"; + cout << msgHeader << " -SYNTAX- fit \n"; + cout << msgHeader << " -SYNTAX- fit off\n"; } } else if (cmd_ == "cfd") { @@ -605,10 +581,10 @@ bool scopeScanner::ExtraCommands(const std::string &cmd_, std::vector\n"; + cout << msgHeader << "Invalid number of parameters to 'avg'\n"; + cout << msgHeader << " -SYNTAX- avg \n"; } } else if(cmd_ == "save") { @@ -643,54 +619,44 @@ bool scopeScanner::ExtraCommands(const std::string &cmd_, std::vector\n"; + cout << msgHeader << "Invalid number of parameters to 'save'\n"; + cout << msgHeader << " -SYNTAX- save \n"; } } else if(cmd_ == "delay"){ if(args_.size() == 1){ delay_ = atoi(args_.at(0).c_str()); } else{ - std::cout << msgHeader << "Invalid number of parameters to 'delay'\n"; - std::cout << msgHeader << " -SYNTAX- delay