1
0
Fork 0
mirror of https://github.com/gwm17/Specter.git synced 2024-11-22 18:28:52 -05:00

Compare commits

...

6 Commits

26 changed files with 165 additions and 104 deletions

View File

@ -34,6 +34,7 @@ SpecProject is an example showing what a typical implementation might look like,
Note: As Specter is still very early in development, the default build configuration is Debug. If you are actually going to use it, you will want to make sure that you switch to building in Release configuration otherwise performance may be poor. Note: As Specter is still very early in development, the default build configuration is Debug. If you are actually going to use it, you will want to make sure that you switch to building in Release configuration otherwise performance may be poor.
## Suppported data sources ## Suppported data sources
By default Specter only supports CAEN CoMPASS data sources. Specter supports CAEN data sources (binary data files fully supported, online is a work in progress) and Daqromancy data sources (untested as of yet)
### Status as of Aug 2022 ### Status as of Aug 2022
Unfortunately, at the time of writing, CoMPASS does not fully support the use of external data analysis. CoMPASS versions 1.3-2.0 have a data socket, which can not be accessed outside of the localhost, meaning that use with Specter generally requires an additional server broadcasting the data to the machine running Specter, which is non-trivial. CoMPASS began supporting a public data socket as of version 2.1, however, it is now by default a time-sorted data socket. The time-sorting operation is so inefficient that any significant data rate on the aquisition basically results in a soft crash of CoMPASS. Currently there is no option to turn off the time sorting on the data socket. CAEN has been alerted to this issue, so hopefully this will be rectified shortly. Unfortunately, at the time of writing, CoMPASS does not fully support the use of external data analysis. CoMPASS versions 1.3-2.0 have a data socket, which can not be accessed outside of the localhost, meaning that use with Specter generally requires an additional server broadcasting the data to the machine running Specter, which is non-trivial. CoMPASS began supporting a public data socket as of version 2.1, however, it is now by default a time-sorted data socket. The time-sorting operation is so inefficient that any significant data rate on the aquisition basically results in a soft crash of CoMPASS. Currently there is no option to turn off the time sorting on the data socket. CAEN has been alerted to this issue, so hopefully this will be rectified shortly.

View File

@ -18,18 +18,18 @@ MassMap::MassMap()
std::ifstream massfile("Assets/amdc2016_mass.txt"); std::ifstream massfile("Assets/amdc2016_mass.txt");
if (massfile.is_open()) if (massfile.is_open())
{ {
std::string junk, A, element; std::string junk, element;
int Z; uint32_t Z, A, key;
double atomicMassBig, atomicMassSmall, isotopicMass; double atomicMassBig, atomicMassSmall, isotopicMass;
getline(massfile, junk); getline(massfile, junk);
getline(massfile, junk); getline(massfile, junk);
while (massfile >> junk) while (massfile >> junk)
{ {
massfile >> Z >> A >> element >> atomicMassBig >> atomicMassSmall; massfile >> Z >> A >> element >> atomicMassBig >> atomicMassSmall;
isotopicMass = (atomicMassBig + atomicMassSmall * 1e-6 - Z * electron_mass) * u_to_mev; isotopicMass = (atomicMassBig + atomicMassSmall * 1e-6 - Z * s_eMass) * s_u2MeV;
std::string key = "(" + std::to_string(Z) + "," + A + ")"; key = GenerateID(Z, A);
massTable[key] = isotopicMass; massTable[key] = isotopicMass;
elementTable[Z] = element; elementTable[key] = std::to_string(A) + element;
} }
} }
else else
@ -41,10 +41,10 @@ MassMap::MassMap()
MassMap::~MassMap() {} MassMap::~MassMap() {}
//Returns nuclear mass in MeV //Returns nuclear mass in MeV
double MassMap::FindMass(int Z, int A) double MassMap::FindMass(uint32_t Z, uint32_t A)
{ {
SPEC_PROFILE_FUNCTION(); SPEC_PROFILE_FUNCTION();
std::string key = "(" + std::to_string(Z) + "," + std::to_string(A) + ")"; uint32_t key = GenerateID(Z, A);
auto data = massTable.find(key); auto data = massTable.find(key);
if (data == massTable.end()) if (data == massTable.end())
{ {
@ -55,15 +55,17 @@ double MassMap::FindMass(int Z, int A)
} }
//returns element symbol //returns element symbol
std::string MassMap::FindSymbol(int Z, int A) std::string MassMap::FindSymbol(uint32_t Z, uint32_t A)
{ {
SPEC_PROFILE_FUNCTION(); SPEC_PROFILE_FUNCTION();
auto data = elementTable.find(Z); static std::string nullResult = "";
uint32_t key = GenerateID(Z, A);
auto data = elementTable.find(key);
if (data == elementTable.end()) if (data == elementTable.end())
{ {
SPEC_ERROR("Invalid nucleus at MassMap::FindSymbol()! Z: {0} A: {1}", Z, A); SPEC_ERROR("Invalid nucleus at MassMap::FindSymbol()! Z: {0} A: {1}", Z, A);
return ""; return nullResult;
} }
std::string fullsymbol = std::to_string(A) + data->second; return data->second;
return fullsymbol;
} }

View File

@ -16,16 +16,21 @@ class MassMap
public: public:
MassMap(); MassMap();
~MassMap(); ~MassMap();
double FindMass(int Z, int A); double FindMass(uint32_t Z, uint32_t A);
std::string FindSymbol(int Z, int A); std::string FindSymbol(uint32_t Z, uint32_t A);
private: private:
std::unordered_map<std::string, double> massTable; //As demonstrated elsewhere, using Szudzik pairing function to make unique id for two unsigned ints
std::unordered_map<int, std::string> elementTable; uint32_t GenerateID(uint32_t Z, uint32_t A)
{
return Z >= A ? (Z * Z + Z + A) : (A * A + Z);
}
std::unordered_map<uint32_t, double> massTable;
std::unordered_map<uint32_t, std::string> elementTable;
//constants //constants
static constexpr double u_to_mev = 931.4940954; static constexpr double s_u2MeV = 931.4940954;
static constexpr double electron_mass = 0.000548579909; static constexpr double s_eMass = 0.000548579909;
}; };
#endif #endif

View File

@ -15,10 +15,12 @@ namespace Specter {
beamIntegrator("beamIntegrator") beamIntegrator("beamIntegrator")
{ {
SPEC_PROFILE_FUNCTION(); SPEC_PROFILE_FUNCTION();
//Bind some parameters
manager->BindParameter(delayFLTime); manager->BindParameter(delayFLTime);
manager->BindParameter(delayFRTime); manager->BindParameter(delayFRTime);
manager->BindParameter(delayBLTime); manager->BindParameter(delayBLTime);
manager->BindParameter(delayBRTime); manager->BindParameter(delayBRTime);
//Bind parameters with some default histograms. Saves us the effort of making them in the UI. //Bind parameters with some default histograms. Saves us the effort of making them in the UI.
manager->BindParameter(x1, 600, -300.0, 300.0); manager->BindParameter(x1, 600, -300.0, 300.0);
manager->BindParameter(x2, 600, -300.0, 300.0); manager->BindParameter(x2, 600, -300.0, 300.0);
@ -29,12 +31,24 @@ namespace Specter {
manager->BindParameter(cathode, 4096, 0.0, 4096); manager->BindParameter(cathode, 4096, 0.0, 4096);
manager->BindParameter(xavg_sabreCoinc, 600, -300.0, 300.0); manager->BindParameter(xavg_sabreCoinc, 600, -300.0, 300.0);
std::vector<std::string> sabre_list; //Example of injecting experiment specific info. I know that SABRE is only used in 16 channel digitizers,
for (int i = 0; i < 127; i++) //so I can make a simple incremental id for use with the UI so that the parameter names are human readable. We map this "simple" id
//to the more robust and universal Specter board/channel UUID using a std::unordered_map
//This is kind of a really simple example of injecting a channel map; more complicated and advanced versions could involve reading
//a text file to get detector ID information, detector channel number, etc.
std::vector<std::string> sabre_list; //list of names will allow us to create a summary histogram.
uint32_t uuid;
for (uint32_t board = 0; board < 8; board++)
{ {
sabre_list.push_back("sabre_" + std::to_string(i)); for (uint32_t channel = 0; channel < 16; channel++)
sabre.emplace_back(sabre_list[i]); {
manager->BindParameter(sabre[i]); //spdlog comes prepackaged with the fmt library, so make good use of it!
sabre_list.push_back(fmt::format("sabre_{}", board*16 + channel));
uuid = Utilities::GetBoardChannelUUID(board, channel);
sabre[uuid] = Parameter(sabre_list.back());
manager->BindParameter(sabre[uuid]);
}
} }
//If you want to make a histogram default available, you can add one like this. //If you want to make a histogram default available, you can add one like this.
@ -52,70 +66,78 @@ namespace Specter {
//Do some physics! //Do some physics!
void SPSAnalysisStage::AnalyzePhysicsEvent(const SpecEvent& event) void SPSAnalysisStage::AnalyzePhysicsEvent(const SpecEvent& event)
{ {
SPEC_PROFILE_FUNCTION(); SPEC_PROFILE_FUNCTION();
//You might want some flags for coincidence cases
//Use statics to avoid allocating extra memory each call (these pipeline functions are called a lot!)
static bool sabreFlag;
//Most analysis stages will start kinda like this. Take the raw event data and //Most analysis stages will start kinda like this. Take the raw event data and
//put it into NavParameters using the hit id. Switches are perfect for this. Can also //put it into Specter::Parameters using the hit id. Switches are perfect for this. Can also
//create mapping classes to use text-file-based id association (commonly called channel maps). //create mapping classes to use text-file-based id association (commonly called channel maps).
bool sabreFlag = false; sabreFlag = false;
for(auto& hit : event) for(auto& hit : event)
{ {
if (hit.id < 127) //Check the SABRE map first; use iterators from std::unordered_map.
//See std::unordered_map docs for more info if this sort of idiom is unfamiliar
auto iter = sabre.find(hit.id);
if (iter != sabre.end())
{ {
sabreFlag = true; sabreFlag = true;
if (hit.longEnergy > sabre[hit.id].GetValue()) if (hit.longEnergy > iter->second.GetValue())
sabre[hit.id].SetValue(hit.longEnergy); iter->second.SetValue(hit.longEnergy);
continue;
} }
switch (hit.id) switch (hit.id)
{ {
case 129: case s_scintLeftID:
{ {
if (hit.longEnergy > scintLeft.GetValue()) if (hit.longEnergy > scintLeft.GetValue())
scintLeft.SetValue(hit.longEnergy); scintLeft.SetValue(hit.longEnergy);
break; break;
} }
case 133: case s_beamIntID:
{ {
beamIntegrator.Increment(); beamIntegrator.Increment();
break; break;
} }
case 135: case s_cathodeID:
{ {
if (hit.longEnergy > cathode.GetValue()) if (hit.longEnergy > cathode.GetValue())
cathode.SetValue(hit.longEnergy); cathode.SetValue(hit.longEnergy);
break; break;
} }
case 136: case s_delayFrontLeftID:
{ {
if (!delayFLTime.IsValid()) if (!delayFLTime.IsValid())
delayFLTime.SetValue(hit.timestamp / 1.0e3); delayFLTime.SetValue(hit.timestamp / 1.0e3);
break; break;
} }
case 137: case s_delayFrontRightID:
{ {
if (!delayFRTime.IsValid()) if (!delayFRTime.IsValid())
delayFRTime.SetValue(hit.timestamp / 1.0e3); delayFRTime.SetValue(hit.timestamp / 1.0e3);
break; break;
} }
case 138: case s_delayBackLeftID:
{ {
if (!delayBLTime.IsValid()) if (!delayBLTime.IsValid())
delayBLTime.SetValue(hit.timestamp / 1.0e3); delayBLTime.SetValue(hit.timestamp / 1.0e3);
break; break;
} }
case 139: case s_delayBackRightID:
{ {
if (!delayBRTime.IsValid()) if (!delayBRTime.IsValid())
delayBRTime.SetValue(hit.timestamp / 1.0e3); delayBRTime.SetValue(hit.timestamp / 1.0e3);
break; break;
} }
case 141: case s_anodeFrontID:
{ {
if (hit.longEnergy > anodeFront.GetValue()) if (hit.longEnergy > anodeFront.GetValue())
anodeFront.SetValue(hit.longEnergy); anodeFront.SetValue(hit.longEnergy);
break; break;
} }
case 143: case s_anodeBackID:
{ {
if (hit.longEnergy > anodeBack.GetValue()) if (hit.longEnergy > anodeBack.GetValue())
anodeBack.SetValue(hit.longEnergy); anodeBack.SetValue(hit.longEnergy);

View File

@ -31,7 +31,9 @@ namespace Specter {
Parameter cathode; Parameter cathode;
Parameter xavg_sabreCoinc; Parameter xavg_sabreCoinc;
std::vector<Parameter> sabre; //For collections of parameters, prefer std::unordered_map or std::map over vector
//Better with use of UUIDs for board-channel pairs
std::unordered_map<uint32_t, Parameter> sabre;
//Create a few variables //Create a few variables
Variable x1_weight; Variable x1_weight;
@ -39,6 +41,19 @@ namespace Specter {
//Create a scaler //Create a scaler
Scaler beamIntegrator; Scaler beamIntegrator;
//Define some board-channel ID's that we'll use. The static keyword means that we only need to calculate them once,
//constexpr allows us to use them in a switch (and makes them compile time evaluated)
//Note that to make them static constexpr we use literals (i.e. hardcoded) in the arguments
static constexpr uint32_t s_scintLeftID = Utilities::GetBoardChannelUUID(8, 0);
static constexpr uint32_t s_beamIntID = Utilities::GetBoardChannelUUID(8, 5);
static constexpr uint32_t s_cathodeID = Utilities::GetBoardChannelUUID(8, 7);
static constexpr uint32_t s_delayFrontLeftID = Utilities::GetBoardChannelUUID(8, 8);
static constexpr uint32_t s_delayFrontRightID = Utilities::GetBoardChannelUUID(8, 9);
static constexpr uint32_t s_delayBackLeftID = Utilities::GetBoardChannelUUID(8, 10);
static constexpr uint32_t s_delayBackRightID = Utilities::GetBoardChannelUUID(8, 11);
static constexpr uint32_t s_anodeFrontID = Utilities::GetBoardChannelUUID(8, 13);
static constexpr uint32_t s_anodeBackID = Utilities::GetBoardChannelUUID(8, 15);
}; };
} }

View File

@ -2,7 +2,7 @@ add_subdirectory(vendor/glad)
add_subdirectory(vendor/glfw) add_subdirectory(vendor/glfw)
add_subdirectory(vendor/imgui) add_subdirectory(vendor/imgui)
set(YAML_CPP_BUILD_TOOLS Off) set(YAML_CPP_BUILD_TOOLS Off CACHE BOOL "Enable parse tools")
add_subdirectory(vendor/yaml-cpp) add_subdirectory(vendor/yaml-cpp)
set(GRIMOIRE_ASIO_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/vendor/asio/asio/include) set(GRIMOIRE_ASIO_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/vendor/asio/asio/include)

View File

@ -103,6 +103,8 @@ target_sources(Specter PRIVATE
Specter/Physics/Daqromancy/DYFileSource.cpp Specter/Physics/Daqromancy/DYFileSource.cpp
Specter/Physics/Daqromancy/DYOnlineSource.h Specter/Physics/Daqromancy/DYOnlineSource.h
Specter/Physics/Daqromancy/DYOnlineSource.cpp Specter/Physics/Daqromancy/DYOnlineSource.cpp
Specter/Utils/Functions.h
Specter/Utils/Functions.cpp
) )
#ImPlot sources #ImPlot sources

View File

@ -34,5 +34,6 @@
#include "Specter/Events/Event.h" #include "Specter/Events/Event.h"
#include "Specter/Utils/TestServerLayer.h" #include "Specter/Utils/TestServerLayer.h"
#include "Specter/Utils/Instrumentor.h" #include "Specter/Utils/Instrumentor.h"
#include "Specter/Utils/Functions.h"
#endif #endif

View File

@ -44,7 +44,6 @@ namespace Specter {
m_chosenPort = "51489"; m_chosenPort = "51489";
m_chosenWindow = 3000000; m_chosenWindow = 3000000;
m_bitflags = 0; m_bitflags = 0;
m_channels_per_board = 16;
ImGui::OpenPopup(ICON_FA_LINK " Attach Source"); ImGui::OpenPopup(ICON_FA_LINK " Attach Source");
} }
if (ImGui::BeginPopupModal(ICON_FA_LINK " Attach Source")) if (ImGui::BeginPopupModal(ICON_FA_LINK " Attach Source"))
@ -79,7 +78,6 @@ namespace Specter {
{ {
m_bitflags = m_bitflags ^ CompassHeaders::EnergyCalibrated; m_bitflags = m_bitflags ^ CompassHeaders::EnergyCalibrated;
} }
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
} }
else if (m_chosenType == DataSource::SourceType::CompassOffline) else if (m_chosenType == DataSource::SourceType::CompassOffline)
{ {
@ -92,13 +90,11 @@ namespace Specter {
auto temp = m_fileDialog.RenderFileDialog(); auto temp = m_fileDialog.RenderFileDialog();
if (!temp.first.empty() && temp.second == FileDialog::Type::OpenDir) if (!temp.first.empty() && temp.second == FileDialog::Type::OpenDir)
m_chosenLocation = temp.first; m_chosenLocation = temp.first;
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
} }
else if (m_chosenType == DataSource::SourceType::DaqromancyOnline) else if (m_chosenType == DataSource::SourceType::DaqromancyOnline)
{ {
ImGui::InputText("Hostname", &m_chosenLocation); ImGui::InputText("Hostname", &m_chosenLocation);
ImGui::InputText("Port", &m_chosenPort); ImGui::InputText("Port", &m_chosenPort);
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
} }
else if (m_chosenType == DataSource::SourceType::DaqromancyOffline) else if (m_chosenType == DataSource::SourceType::DaqromancyOffline)
{ {
@ -111,7 +107,6 @@ namespace Specter {
auto temp = m_fileDialog.RenderFileDialog(); auto temp = m_fileDialog.RenderFileDialog();
if (!temp.first.empty() && temp.second == FileDialog::Type::OpenDir) if (!temp.first.empty() && temp.second == FileDialog::Type::OpenDir)
m_chosenLocation = temp.first; m_chosenLocation = temp.first;
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
} }
ImGui::InputInt("Coinc. Window (ps)", &m_chosenWindow); ImGui::InputInt("Coinc. Window (ps)", &m_chosenWindow);
@ -120,12 +115,12 @@ namespace Specter {
{ {
if (m_chosenType == DataSource::SourceType::CompassOffline || m_chosenType == DataSource::SourceType::DaqromancyOffline) if (m_chosenType == DataSource::SourceType::CompassOffline || m_chosenType == DataSource::SourceType::DaqromancyOffline)
{ {
PhysicsStartEvent event(m_chosenLocation, m_chosenType, m_chosenWindow, m_chosenPort, false, 0U, m_channels_per_board); PhysicsStartEvent event(m_chosenLocation, m_chosenType, m_chosenWindow, m_chosenPort, false, 0U);
Application::Get().OnEvent(event); Application::Get().OnEvent(event);
} }
else if (m_chosenType == DataSource::SourceType::CompassOnline || m_chosenType == DataSource::SourceType::DaqromancyOnline) else if (m_chosenType == DataSource::SourceType::CompassOnline || m_chosenType == DataSource::SourceType::DaqromancyOnline)
{ {
PhysicsStartEvent event(m_chosenLocation, m_chosenType, m_chosenWindow, m_chosenPort, true, m_bitflags, m_channels_per_board); PhysicsStartEvent event(m_chosenLocation, m_chosenType, m_chosenWindow, m_chosenPort, true, m_bitflags);
Application::Get().OnEvent(event); Application::Get().OnEvent(event);
} }
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();

View File

@ -30,7 +30,6 @@ namespace Specter {
FileDialog m_fileDialog; FileDialog m_fileDialog;
uint16_t m_bitflags; uint16_t m_bitflags;
int m_chosenWindow; int m_chosenWindow;
int m_channels_per_board;
}; };
} }

View File

@ -21,9 +21,8 @@ namespace Specter {
{ {
public: public:
//Bitflags is a final option for random crap needed for a source. Currently used for compass online to indicate header state. //Bitflags is a final option for random crap needed for a source. Currently used for compass online to indicate header state.
PhysicsStartEvent(const std::string& loc, DataSource::SourceType type, uint64_t window, const std::string& port = "51489", bool sortFlag=false, uint16_t bitflags = 0, PhysicsStartEvent(const std::string& loc, DataSource::SourceType type, uint64_t window, const std::string& port = "51489", bool sortFlag=false, uint16_t bitflags = 0) :
int channels_per_board=16) : m_sourceLocation(loc), m_port(port), m_sourceType(type), m_coincidenceWindow(window), m_sortFlag(sortFlag), m_bitflags(bitflags)
m_sourceLocation(loc), m_port(port), m_sourceType(type), m_coincidenceWindow(window), m_sortFlag(sortFlag), m_bitflags(bitflags), m_channels_per_board(channels_per_board)
{} {}
inline const std::string GetSourceLocation() const { return m_sourceLocation; } inline const std::string GetSourceLocation() const { return m_sourceLocation; }
@ -31,7 +30,6 @@ namespace Specter {
inline const DataSource::SourceType GetSourceType() const { return m_sourceType; } inline const DataSource::SourceType GetSourceType() const { return m_sourceType; }
inline const uint64_t GetCoincidenceWindow() const { return m_coincidenceWindow; } inline const uint64_t GetCoincidenceWindow() const { return m_coincidenceWindow; }
inline const bool GetSortFlag() const { return m_sortFlag; } inline const bool GetSortFlag() const { return m_sortFlag; }
inline const int GetChannelsPerBoard() const { return m_channels_per_board; }
inline const uint16_t GetBitFlags() const { return m_bitflags; } inline const uint16_t GetBitFlags() const { return m_bitflags; }
std::string ToString() const override std::string ToString() const override
@ -49,7 +47,6 @@ namespace Specter {
uint64_t m_coincidenceWindow; uint64_t m_coincidenceWindow;
bool m_sortFlag; bool m_sortFlag;
uint16_t m_bitflags; uint16_t m_bitflags;
int m_channels_per_board;
}; };
class PhysicsStopEvent : public Event class PhysicsStopEvent : public Event

View File

@ -5,13 +5,6 @@
need to know if the buffer is/was filled, however we use non-blocking since we don't want the entire process to hang on attempting a connection or waiting need to know if the buffer is/was filled, however we use non-blocking since we don't want the entire process to hang on attempting a connection or waiting
for data to come over the pipe. We handle the case of an un-filled buffer internally. for data to come over the pipe. We handle the case of an un-filled buffer internally.
IMPORTANT
Specter wants a unqiue ID on each hit. To do this we use the idiom:
id = board_number * nchannels_per_board + channel_number
This requires two things: that the class variable m_nchannels_per_board be set to match your physical digitizers, and that ALL of your
digitizers have the SAME number of channels. By default CompassRun assumes 16 channels per board, as this is what is used with the SE-SPS setup at FoxLab.
If you use a different set of boards, CHANGE THIS VALUE! If you use mixed boards, you will need to invent a new id scheme altogether.
ADDITIONALLY ADDITIONALLY
CoMPASS servers provide no stream side information on the state of a transfer (verified via communication w/ CAEN). That is: there are no headers or enders on the data transfers. CoMPASS servers provide no stream side information on the state of a transfer (verified via communication w/ CAEN). That is: there are no headers or enders on the data transfers.
This forces us to use the size of a single CoMPASS datum (CompassHit) to determine the state of a transfer. If the read buffer size is not a whole multiple of CompassHits, the data This forces us to use the size of a single CoMPASS datum (CompassHit) to determine the state of a transfer. If the read buffer size is not a whole multiple of CompassHits, the data
@ -20,13 +13,17 @@
Maybe we can get them to change this? Headers reeaaally should exist for transfers like this. Maybe we can get them to change this? Headers reeaaally should exist for transfers like this.
GWM -- April 2022 GWM -- April 2022
Make it so that number of channels per board is no longer fixed. Use pairing function defined in Utils/Functions.h to generate a UUID for each board channel/pair.
GWM -- Oct 2022
*/ */
#include "CompassOnlineSource.h" #include "CompassOnlineSource.h"
namespace Specter { namespace Specter {
CompassOnlineSource::CompassOnlineSource(const std::string& hostname, const std::string& port, uint16_t header, int channels_per_board) : CompassOnlineSource::CompassOnlineSource(const std::string& hostname, const std::string& port, uint16_t header) :
DataSource(), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_header(header), m_nchannels_per_board(channels_per_board) DataSource(), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_header(header)
{ {
InitConnection(hostname, port); InitConnection(hostname, port);
} }
@ -82,7 +79,7 @@ namespace Specter {
m_datum.shortEnergy = m_currentHit.energyShort; m_datum.shortEnergy = m_currentHit.energyShort;
m_datum.calEnergy = m_currentHit.energyCalibrated; m_datum.calEnergy = m_currentHit.energyCalibrated;
m_datum.timestamp = m_currentHit.timestamp; m_datum.timestamp = m_currentHit.timestamp;
m_datum.id = m_currentHit.board * m_nchannels_per_board + m_currentHit.channel; m_datum.id = Utilities::GetBoardChannelUUID(m_currentHit.board, m_currentHit.channel);
return m_datum; return m_datum;
} }

View File

@ -5,13 +5,6 @@
need to know if the buffer is/was filled, however we use non-blocking since we don't want the entire process to hang on attempting a connection or waiting need to know if the buffer is/was filled, however we use non-blocking since we don't want the entire process to hang on attempting a connection or waiting
for data to come over the pipe. We handle the case of an un-filled buffer internally. for data to come over the pipe. We handle the case of an un-filled buffer internally.
IMPORTANT
Specter wants a unqiue ID on each hit. To do this we use the idiom:
id = board_number * nchannels_per_board + channel_number
This requires two things: that the class variable m_nchannels_per_board be set to match your physical digitizers, and that ALL of your
digitizers have the SAME number of channels. By default CompassRun assumes 16 channels per board, as this is what is used with the SE-SPS setup at FoxLab.
If you use a different set of boards, CHANGE THIS VALUE! If you use mixed boards, you will need to invent a new id scheme altogether.
ADDITIONALLY ADDITIONALLY
CoMPASS servers provide no stream side information on the state of a transfer (verified via communication w/ CAEN). That is: there are no headers or enders on the data transfers. CoMPASS servers provide no stream side information on the state of a transfer (verified via communication w/ CAEN). That is: there are no headers or enders on the data transfers.
This forces us to use the size of a single CoMPASS datum (CompassHit) to determine the state of a transfer. If the read buffer size is not a whole multiple of CompassHits, the data This forces us to use the size of a single CoMPASS datum (CompassHit) to determine the state of a transfer. If the read buffer size is not a whole multiple of CompassHits, the data
@ -26,6 +19,10 @@
of Specter's runtime). Best to use the CoMPASSPlot for waves. of Specter's runtime). Best to use the CoMPASSPlot for waves.
GWM -- May 2022 GWM -- May 2022
Make it so that number of channels per board is no longer fixed. Use pairing function defined in Utils/Functions.h to generate a UUID for each board channel/pair.
GWM -- Oct 2022
*/ */
#ifndef COMPASS_ONLINE_SOURCE_H #ifndef COMPASS_ONLINE_SOURCE_H
#define COMPASS_ONLINE_SOURCE_H #define COMPASS_ONLINE_SOURCE_H
@ -39,7 +36,7 @@ namespace Specter {
class CompassOnlineSource : public DataSource class CompassOnlineSource : public DataSource
{ {
public: public:
CompassOnlineSource(const std::string& hostname, const std::string& port, uint16_t header, int channels_per_board=16); CompassOnlineSource(const std::string& hostname, const std::string& port, uint16_t header);
virtual ~CompassOnlineSource() override; virtual ~CompassOnlineSource() override;
virtual const SpecData& GetData() override; virtual const SpecData& GetData() override;
@ -53,8 +50,6 @@ namespace Specter {
std::vector<char> m_currentBuffer; std::vector<char> m_currentBuffer;
uint16_t m_header; uint16_t m_header;
int m_datasize; //size of CoMPASS hit in bytes, set by header arg int m_datasize; //size of CoMPASS hit in bytes, set by header arg
const int m_nchannels_per_board = 16; //IMPORTANT: Used for ID'ing channels uniquely. If you use boards with 32 or 8 or 64 channels you must change this! If you mix boards with
//different numbers of channels, you will have to find a different id solution.
char* m_bufferIter; char* m_bufferIter;
char* m_bufferEnd; char* m_bufferEnd;
CompassHit m_currentHit; CompassHit m_currentHit;

View File

@ -10,25 +10,29 @@
Updated to also handle scaler data. -- GWM Oct. 2020 Updated to also handle scaler data. -- GWM Oct. 2020
Modifed and updated for use in Specter. Obviously stripped out any ROOT code. Also, now uses the very nice std::filesystem Modifed and updated for use in Specter. Obviously stripped out any ROOT code. Also, now uses the very nice std::filesystem
library to handle filepathing. Also, removed scalers (for now). library to handle filepathing.
GWM -- Feb 2022 GWM -- Feb 2022
Update to reflect new CAEN binary data format with headers to indicate data contents. Update to reflect new CAEN binary data format with headers to indicate data contents.
GWM -- May 2022 GWM -- May 2022
Make it so that number of channels per board is no longer fixed. Use pairing function defined in Utils/Functions.h to generate a UUID for each board channel/pair.
GWM -- Oct 2022
*/ */
#include "CompassRun.h" #include "CompassRun.h"
namespace Specter { namespace Specter {
CompassRun::CompassRun() : CompassRun::CompassRun() :
DataSource(), m_directory(""), m_startIndex(0), m_nchannels_per_board(16) DataSource(), m_directory(""), m_startIndex(0)
{ {
} }
CompassRun::CompassRun(const std::string& dir, int channels_per_board) : CompassRun::CompassRun(const std::string& dir) :
DataSource(), m_directory(dir), m_startIndex(0), m_nchannels_per_board(channels_per_board) DataSource(), m_directory(dir), m_startIndex(0)
{ {
CollectFiles(); CollectFiles();
} }
@ -147,7 +151,7 @@ namespace Specter {
m_datum.shortEnergy = m_hit.energyShort; m_datum.shortEnergy = m_hit.energyShort;
m_datum.calEnergy = m_hit.energyCalibrated; m_datum.calEnergy = m_hit.energyCalibrated;
m_datum.timestamp = m_hit.timestamp; m_datum.timestamp = m_hit.timestamp;
m_datum.id = m_hit.board * m_nchannels_per_board + m_hit.channel; m_datum.id = Utilities::GetBoardChannelUUID(m_hit.board, m_hit.channel);
} }
return m_datum; return m_datum;

View File

@ -8,17 +8,17 @@
Written by G.W. McCann Oct. 2020 Written by G.W. McCann Oct. 2020
Modifed and updated for use in Specter. Obviously stripped out any ROOT code. Also, now uses the very nice std::filesystem Modifed and updated for use in Specter. Obviously stripped out any ROOT code. Also, now uses the very nice std::filesystem
library to handle filepathing. One change of great import: Specter wants a unqiue ID on each hit. To do this we use the idiom: library to handle filepathing.
id = board_number * nchannels_per_board + channel_number
This requires two things: that the class variable m_nchannels_per_board be set to match your physical digitizers, and that ALL of your
digitizers have the SAME number of channels. By default CompassRun assumes 16 channels per board, as this is what is used with the SE-SPS setup at FoxLab.
If you use a different set of boards, CHANGE THIS VALUE! If you use mixed boards, you will need to invent a new id scheme altogether.
GWM -- Feb 2022 GWM -- Feb 2022
Update to reflect new CAEN binary data format with headers to indicate data contents. Update to reflect new CAEN binary data format with headers to indicate data contents.
GWM -- May 2022 GWM -- May 2022
Make it so that number of channels per board is no longer fixed. Use pairing function defined in Utils/Functions.h to generate a UUID for each board channel/pair.
GWM -- Oct 2022
*/ */
#ifndef COMPASSRUN_H #ifndef COMPASSRUN_H
#define COMPASSRUN_H #define COMPASSRUN_H
@ -36,7 +36,7 @@ namespace Specter {
public: public:
CompassRun(); CompassRun();
CompassRun(const std::string& dir, int channels_per_board=16); CompassRun(const std::string& dir);
virtual ~CompassRun(); virtual ~CompassRun();
virtual const SpecData& GetData() override; virtual const SpecData& GetData() override;
inline void SetDirectory(const std::string& dir) { m_directory = dir; CollectFiles(); } inline void SetDirectory(const std::string& dir) { m_directory = dir; CollectFiles(); }
@ -52,8 +52,7 @@ namespace Specter {
std::vector<CompassFile> m_datafiles; std::vector<CompassFile> m_datafiles;
unsigned int m_startIndex; //this is the file we start looking at; increases as we finish files. unsigned int m_startIndex; //this is the file we start looking at; increases as we finish files.
int m_nchannels_per_board; //IMPORTANT: Used for ID'ing channels uniquely. If you use boards with 32 or 8 or 64 channels you must change this! If you mix boards with
//different numbers of channels, you will have to find a different id solution.
ShiftMap m_smap; ShiftMap m_smap;
CompassHit m_hit; CompassHit m_hit;

View File

@ -2,8 +2,8 @@
namespace Specter { namespace Specter {
DYFileSource::DYFileSource(const std::string& directory, int channels_per_board) : DYFileSource::DYFileSource(const std::string& directory) :
DataSource(), m_directory(directory), m_channelsPerBoard(channels_per_board) DataSource(), m_directory(directory)
{ {
CollectFiles(); CollectFiles();
} }
@ -108,7 +108,7 @@ namespace Specter {
m_datum.longEnergy = m_dyHit.energy; m_datum.longEnergy = m_dyHit.energy;
m_datum.shortEnergy = m_dyHit.energyShort; m_datum.shortEnergy = m_dyHit.energyShort;
m_datum.timestamp = m_dyHit.timestamp; m_datum.timestamp = m_dyHit.timestamp;
m_datum.id = m_dyHit.board * m_channelsPerBoard + m_dyHit.channel; m_datum.id = Utilities::GetBoardChannelUUID(m_dyHit.board, m_dyHit.channel);
} }
return m_datum; return m_datum;
} }

View File

@ -10,7 +10,7 @@ namespace Specter {
class DYFileSource : public DataSource class DYFileSource : public DataSource
{ {
public: public:
DYFileSource(const std::string& directory, int channels_per_board = 16); DYFileSource(const std::string& directory);
virtual ~DYFileSource(); virtual ~DYFileSource();
virtual const SpecData& GetData() override; virtual const SpecData& GetData() override;
@ -21,7 +21,6 @@ namespace Specter {
std::filesystem::path m_directory; std::filesystem::path m_directory;
static constexpr std::string_view s_extension = ".dybin"; static constexpr std::string_view s_extension = ".dybin";
int m_channelsPerBoard;
std::vector<DaqGrimoire::DYFileReader> m_files; std::vector<DaqGrimoire::DYFileReader> m_files;
DaqGrimoire::DYListData m_dyHit; DaqGrimoire::DYListData m_dyHit;

View File

@ -2,8 +2,8 @@
namespace Specter { namespace Specter {
DYOnlineSource::DYOnlineSource(const std::string& hostname, const std::string& port, int channelsPerBoard) : DYOnlineSource::DYOnlineSource(const std::string& hostname, const std::string& port) :
DataSource(), m_clientConnection(hostname, port), m_channelsPerBoard(channelsPerBoard) DataSource(), m_clientConnection(hostname, port)
{ {
m_validFlag = m_clientConnection.IsConnected(); m_validFlag = m_clientConnection.IsConnected();
} }
@ -27,7 +27,7 @@ namespace Specter {
m_datum.longEnergy = m_dyHit.energy; m_datum.longEnergy = m_dyHit.energy;
m_datum.shortEnergy = m_dyHit.energyShort; m_datum.shortEnergy = m_dyHit.energyShort;
m_datum.timestamp = m_dyHit.timestamp; m_datum.timestamp = m_dyHit.timestamp;
m_datum.id = m_dyHit.board * m_channelsPerBoard + m_dyHit.channel; m_datum.id = Utilities::GetBoardChannelUUID(m_dyHit.board, m_dyHit.channel);
} }
else else
{ {

View File

@ -9,7 +9,7 @@ namespace Specter {
class DYOnlineSource : public DataSource class DYOnlineSource : public DataSource
{ {
public: public:
DYOnlineSource(const std::string& hostname, const std::string& port, int channelsPerBoard = 16); DYOnlineSource(const std::string& hostname, const std::string& port);
~DYOnlineSource(); ~DYOnlineSource();
virtual const SpecData& GetData() override; virtual const SpecData& GetData() override;

View File

@ -15,14 +15,14 @@
namespace Specter { namespace Specter {
//loc=either an ip address or a file location, port=address port, or unused in case of file //loc=either an ip address or a file location, port=address port, or unused in case of file
DataSource* CreateDataSource(const std::string& location, const std::string& port, uint16_t header, int channels_per_board, DataSource::SourceType type) DataSource* CreateDataSource(const std::string& location, const std::string& port, uint16_t header, DataSource::SourceType type)
{ {
switch(type) switch(type)
{ {
case DataSource::SourceType::CompassOffline : return new CompassRun(location, channels_per_board); case DataSource::SourceType::CompassOffline : return new CompassRun(location);
case DataSource::SourceType::CompassOnline : return new CompassOnlineSource(location, port, header, channels_per_board); case DataSource::SourceType::CompassOnline : return new CompassOnlineSource(location, port, header);
case DataSource::SourceType::DaqromancyOffline: return new DYFileSource(location, channels_per_board); case DataSource::SourceType::DaqromancyOffline: return new DYFileSource(location);
case DataSource::SourceType::DaqromancyOnline: return new DYOnlineSource(location, port, channels_per_board); case DataSource::SourceType::DaqromancyOnline: return new DYOnlineSource(location, port);
case DataSource::SourceType::None : return nullptr; case DataSource::SourceType::None : return nullptr;
} }
SPEC_WARN("Invalid DataSourceType at CreateDataSource!"); SPEC_WARN("Invalid DataSourceType at CreateDataSource!");

View File

@ -40,7 +40,7 @@ namespace Specter {
SpecData m_datum; SpecData m_datum;
}; };
DataSource* CreateDataSource(const std::string& location, const std::string& port, uint16_t bitflags, int channels_per_board, DataSource::SourceType type); DataSource* CreateDataSource(const std::string& location, const std::string& port, uint16_t bitflags, DataSource::SourceType type);
std::string ConvertDataSourceTypeToString(DataSource::SourceType type); std::string ConvertDataSourceTypeToString(DataSource::SourceType type);
} }

View File

@ -114,7 +114,7 @@ namespace Specter {
{ {
SPEC_PROFILE_FUNCTION(); SPEC_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_sourceMutex); //Shouldn't matter for this, but safety first std::scoped_lock<std::mutex> guard(m_sourceMutex); //Shouldn't matter for this, but safety first
m_source.reset(CreateDataSource(event.GetSourceLocation(), event.GetSourcePort(), event.GetBitFlags(), event.GetChannelsPerBoard(), event.GetSourceType())); m_source.reset(CreateDataSource(event.GetSourceLocation(), event.GetSourcePort(), event.GetBitFlags(), event.GetSourceType()));
m_eventBuilder.SetCoincidenceWindow(event.GetCoincidenceWindow()); m_eventBuilder.SetCoincidenceWindow(event.GetCoincidenceWindow());
m_eventBuilder.SetSortFlag(event.GetSortFlag()); m_eventBuilder.SetSortFlag(event.GetSortFlag());
m_eventBuilder.ClearAll(); //Protect against stopping mid-event m_eventBuilder.ClearAll(); //Protect against stopping mid-event

View File

@ -0,0 +1,8 @@
#include "Functions.h"
namespace Specter {
namespace Utilities {
}
}

View File

@ -0,0 +1,19 @@
#ifndef SPEC_FUNCTIONS_H
#define SPEC_FUNCTIONS_H
namespace Specter {
namespace Utilities
{
//Use Szudzik pairing function to convert board/channel number pair to a single universal unqiue identifier (UUID)
//Allows us to unqiuely id board-channel combo even with a set of boards that don't have the same number of channels
//It's constexpr, since for each board/channel pair the value should be evaluated at compile time (and allows us to use values in a switch)
//This is mostly syntactic sugar. When board and channel are constexpr (read: literals) it gives a constexpr value, otherwise behaves same as any other function
constexpr uint32_t GetBoardChannelUUID(uint32_t board, uint32_t channel)
{
return board >= channel ? (board * board + board + channel) : (channel * channel + board);
}
}
}
#endif

View File

@ -18,5 +18,6 @@
#include "Specter/Core/Logger.h" #include "Specter/Core/Logger.h"
#include "Specter/Utils/Instrumentor.h" #include "Specter/Utils/Instrumentor.h"
#include "Specter/Utils/Functions.h"
#endif #endif

@ -1 +1 @@
Subproject commit dedd1e0e953423a50ec8a35f4cb3161218aac642 Subproject commit 2bd094a97c4b244452fe04f2af12b1c8d469894e