mirror of
https://github.com/gwm17/Specter.git
synced 2024-11-22 10:18:50 -05:00
Compare commits
6 Commits
a26ac08f34
...
10462c1eab
Author | SHA1 | Date | |
---|---|---|---|
Gordon McCann | 10462c1eab | ||
Gordon McCann | 54a33d5123 | ||
Gordon McCann | c1d0069f80 | ||
d83d416d10 | |||
1de7bc9b7d | |||
c0b51c5ba9 |
|
@ -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.
|
||||
|
||||
## 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
|
||||
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.
|
|
@ -18,18 +18,18 @@ MassMap::MassMap()
|
|||
std::ifstream massfile("Assets/amdc2016_mass.txt");
|
||||
if (massfile.is_open())
|
||||
{
|
||||
std::string junk, A, element;
|
||||
int Z;
|
||||
std::string junk, element;
|
||||
uint32_t Z, A, key;
|
||||
double atomicMassBig, atomicMassSmall, isotopicMass;
|
||||
getline(massfile, junk);
|
||||
getline(massfile, junk);
|
||||
while (massfile >> junk)
|
||||
{
|
||||
massfile >> Z >> A >> element >> atomicMassBig >> atomicMassSmall;
|
||||
isotopicMass = (atomicMassBig + atomicMassSmall * 1e-6 - Z * electron_mass) * u_to_mev;
|
||||
std::string key = "(" + std::to_string(Z) + "," + A + ")";
|
||||
isotopicMass = (atomicMassBig + atomicMassSmall * 1e-6 - Z * s_eMass) * s_u2MeV;
|
||||
key = GenerateID(Z, A);
|
||||
massTable[key] = isotopicMass;
|
||||
elementTable[Z] = element;
|
||||
elementTable[key] = std::to_string(A) + element;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -41,10 +41,10 @@ MassMap::MassMap()
|
|||
MassMap::~MassMap() {}
|
||||
|
||||
//Returns nuclear mass in MeV
|
||||
double MassMap::FindMass(int Z, int A)
|
||||
double MassMap::FindMass(uint32_t Z, uint32_t A)
|
||||
{
|
||||
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);
|
||||
if (data == massTable.end())
|
||||
{
|
||||
|
@ -55,15 +55,17 @@ double MassMap::FindMass(int Z, int A)
|
|||
}
|
||||
|
||||
//returns element symbol
|
||||
std::string MassMap::FindSymbol(int Z, int A)
|
||||
std::string MassMap::FindSymbol(uint32_t Z, uint32_t A)
|
||||
{
|
||||
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())
|
||||
{
|
||||
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 fullsymbol;
|
||||
return data->second;
|
||||
}
|
|
@ -16,16 +16,21 @@ class MassMap
|
|||
public:
|
||||
MassMap();
|
||||
~MassMap();
|
||||
double FindMass(int Z, int A);
|
||||
std::string FindSymbol(int Z, int A);
|
||||
double FindMass(uint32_t Z, uint32_t A);
|
||||
std::string FindSymbol(uint32_t Z, uint32_t A);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, double> massTable;
|
||||
std::unordered_map<int, std::string> elementTable;
|
||||
//As demonstrated elsewhere, using Szudzik pairing function to make unique id for two unsigned ints
|
||||
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
|
||||
static constexpr double u_to_mev = 931.4940954;
|
||||
static constexpr double electron_mass = 0.000548579909;
|
||||
static constexpr double s_u2MeV = 931.4940954;
|
||||
static constexpr double s_eMass = 0.000548579909;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,10 +15,12 @@ namespace Specter {
|
|||
beamIntegrator("beamIntegrator")
|
||||
{
|
||||
SPEC_PROFILE_FUNCTION();
|
||||
//Bind some parameters
|
||||
manager->BindParameter(delayFLTime);
|
||||
manager->BindParameter(delayFRTime);
|
||||
manager->BindParameter(delayBLTime);
|
||||
manager->BindParameter(delayBRTime);
|
||||
|
||||
//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(x2, 600, -300.0, 300.0);
|
||||
|
@ -29,12 +31,24 @@ namespace Specter {
|
|||
manager->BindParameter(cathode, 4096, 0.0, 4096);
|
||||
manager->BindParameter(xavg_sabreCoinc, 600, -300.0, 300.0);
|
||||
|
||||
std::vector<std::string> sabre_list;
|
||||
for (int i = 0; i < 127; i++)
|
||||
//Example of injecting experiment specific info. I know that SABRE is only used in 16 channel digitizers,
|
||||
//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));
|
||||
sabre.emplace_back(sabre_list[i]);
|
||||
manager->BindParameter(sabre[i]);
|
||||
for (uint32_t channel = 0; channel < 16; channel++)
|
||||
{
|
||||
//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.
|
||||
|
@ -52,70 +66,78 @@ namespace Specter {
|
|||
//Do some physics!
|
||||
void SPSAnalysisStage::AnalyzePhysicsEvent(const SpecEvent& event)
|
||||
{
|
||||
|
||||
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
|
||||
//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).
|
||||
bool sabreFlag = false;
|
||||
sabreFlag = false;
|
||||
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;
|
||||
if (hit.longEnergy > sabre[hit.id].GetValue())
|
||||
sabre[hit.id].SetValue(hit.longEnergy);
|
||||
if (hit.longEnergy > iter->second.GetValue())
|
||||
iter->second.SetValue(hit.longEnergy);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (hit.id)
|
||||
{
|
||||
case 129:
|
||||
case s_scintLeftID:
|
||||
{
|
||||
if (hit.longEnergy > scintLeft.GetValue())
|
||||
scintLeft.SetValue(hit.longEnergy);
|
||||
break;
|
||||
}
|
||||
case 133:
|
||||
case s_beamIntID:
|
||||
{
|
||||
beamIntegrator.Increment();
|
||||
break;
|
||||
}
|
||||
case 135:
|
||||
case s_cathodeID:
|
||||
{
|
||||
if (hit.longEnergy > cathode.GetValue())
|
||||
cathode.SetValue(hit.longEnergy);
|
||||
break;
|
||||
}
|
||||
case 136:
|
||||
case s_delayFrontLeftID:
|
||||
{
|
||||
if (!delayFLTime.IsValid())
|
||||
delayFLTime.SetValue(hit.timestamp / 1.0e3);
|
||||
break;
|
||||
}
|
||||
case 137:
|
||||
case s_delayFrontRightID:
|
||||
{
|
||||
if (!delayFRTime.IsValid())
|
||||
delayFRTime.SetValue(hit.timestamp / 1.0e3);
|
||||
break;
|
||||
}
|
||||
case 138:
|
||||
case s_delayBackLeftID:
|
||||
{
|
||||
if (!delayBLTime.IsValid())
|
||||
delayBLTime.SetValue(hit.timestamp / 1.0e3);
|
||||
break;
|
||||
}
|
||||
case 139:
|
||||
case s_delayBackRightID:
|
||||
{
|
||||
if (!delayBRTime.IsValid())
|
||||
delayBRTime.SetValue(hit.timestamp / 1.0e3);
|
||||
break;
|
||||
}
|
||||
case 141:
|
||||
case s_anodeFrontID:
|
||||
{
|
||||
if (hit.longEnergy > anodeFront.GetValue())
|
||||
anodeFront.SetValue(hit.longEnergy);
|
||||
break;
|
||||
}
|
||||
case 143:
|
||||
case s_anodeBackID:
|
||||
{
|
||||
if (hit.longEnergy > anodeBack.GetValue())
|
||||
anodeBack.SetValue(hit.longEnergy);
|
||||
|
|
|
@ -31,7 +31,9 @@ namespace Specter {
|
|||
Parameter cathode;
|
||||
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
|
||||
Variable x1_weight;
|
||||
|
@ -39,6 +41,19 @@ namespace Specter {
|
|||
|
||||
//Create a scaler
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
|
@ -2,7 +2,7 @@ add_subdirectory(vendor/glad)
|
|||
add_subdirectory(vendor/glfw)
|
||||
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)
|
||||
|
||||
set(GRIMOIRE_ASIO_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/vendor/asio/asio/include)
|
||||
|
|
|
@ -103,6 +103,8 @@ target_sources(Specter PRIVATE
|
|||
Specter/Physics/Daqromancy/DYFileSource.cpp
|
||||
Specter/Physics/Daqromancy/DYOnlineSource.h
|
||||
Specter/Physics/Daqromancy/DYOnlineSource.cpp
|
||||
Specter/Utils/Functions.h
|
||||
Specter/Utils/Functions.cpp
|
||||
)
|
||||
|
||||
#ImPlot sources
|
||||
|
|
|
@ -34,5 +34,6 @@
|
|||
#include "Specter/Events/Event.h"
|
||||
#include "Specter/Utils/TestServerLayer.h"
|
||||
#include "Specter/Utils/Instrumentor.h"
|
||||
#include "Specter/Utils/Functions.h"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -44,7 +44,6 @@ namespace Specter {
|
|||
m_chosenPort = "51489";
|
||||
m_chosenWindow = 3000000;
|
||||
m_bitflags = 0;
|
||||
m_channels_per_board = 16;
|
||||
ImGui::OpenPopup(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;
|
||||
}
|
||||
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
|
||||
}
|
||||
else if (m_chosenType == DataSource::SourceType::CompassOffline)
|
||||
{
|
||||
|
@ -92,13 +90,11 @@ namespace Specter {
|
|||
auto temp = m_fileDialog.RenderFileDialog();
|
||||
if (!temp.first.empty() && temp.second == FileDialog::Type::OpenDir)
|
||||
m_chosenLocation = temp.first;
|
||||
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
|
||||
}
|
||||
else if (m_chosenType == DataSource::SourceType::DaqromancyOnline)
|
||||
{
|
||||
ImGui::InputText("Hostname", &m_chosenLocation);
|
||||
ImGui::InputText("Port", &m_chosenPort);
|
||||
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
|
||||
}
|
||||
else if (m_chosenType == DataSource::SourceType::DaqromancyOffline)
|
||||
{
|
||||
|
@ -111,7 +107,6 @@ namespace Specter {
|
|||
auto temp = m_fileDialog.RenderFileDialog();
|
||||
if (!temp.first.empty() && temp.second == FileDialog::Type::OpenDir)
|
||||
m_chosenLocation = temp.first;
|
||||
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
|
||||
}
|
||||
ImGui::InputInt("Coinc. Window (ps)", &m_chosenWindow);
|
||||
|
||||
|
@ -120,12 +115,12 @@ namespace Specter {
|
|||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
|
|
@ -30,7 +30,6 @@ namespace Specter {
|
|||
FileDialog m_fileDialog;
|
||||
uint16_t m_bitflags;
|
||||
int m_chosenWindow;
|
||||
int m_channels_per_board;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -21,9 +21,8 @@ namespace Specter {
|
|||
{
|
||||
public:
|
||||
//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,
|
||||
int channels_per_board=16) :
|
||||
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)
|
||||
PhysicsStartEvent(const std::string& loc, DataSource::SourceType type, uint64_t window, const std::string& port = "51489", bool sortFlag=false, uint16_t bitflags = 0) :
|
||||
m_sourceLocation(loc), m_port(port), m_sourceType(type), m_coincidenceWindow(window), m_sortFlag(sortFlag), m_bitflags(bitflags)
|
||||
{}
|
||||
|
||||
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 uint64_t GetCoincidenceWindow() const { return m_coincidenceWindow; }
|
||||
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; }
|
||||
|
||||
std::string ToString() const override
|
||||
|
@ -49,7 +47,6 @@ namespace Specter {
|
|||
uint64_t m_coincidenceWindow;
|
||||
bool m_sortFlag;
|
||||
uint16_t m_bitflags;
|
||||
int m_channels_per_board;
|
||||
};
|
||||
|
||||
class PhysicsStopEvent : public Event
|
||||
|
|
|
@ -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
|
||||
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
|
||||
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
|
||||
|
@ -20,13 +13,17 @@
|
|||
Maybe we can get them to change this? Headers reeaaally should exist for transfers like this.
|
||||
|
||||
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"
|
||||
|
||||
namespace Specter {
|
||||
|
||||
CompassOnlineSource::CompassOnlineSource(const std::string& hostname, const std::string& port, uint16_t header, int channels_per_board) :
|
||||
DataSource(), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_header(header), m_nchannels_per_board(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)
|
||||
{
|
||||
InitConnection(hostname, port);
|
||||
}
|
||||
|
@ -82,7 +79,7 @@ namespace Specter {
|
|||
m_datum.shortEnergy = m_currentHit.energyShort;
|
||||
m_datum.calEnergy = m_currentHit.energyCalibrated;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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
|
||||
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
|
||||
|
@ -26,6 +19,10 @@
|
|||
of Specter's runtime). Best to use the CoMPASSPlot for waves.
|
||||
|
||||
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
|
||||
#define COMPASS_ONLINE_SOURCE_H
|
||||
|
@ -39,7 +36,7 @@ namespace Specter {
|
|||
class CompassOnlineSource : public DataSource
|
||||
{
|
||||
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 const SpecData& GetData() override;
|
||||
|
@ -53,8 +50,6 @@ namespace Specter {
|
|||
std::vector<char> m_currentBuffer;
|
||||
uint16_t m_header;
|
||||
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_bufferEnd;
|
||||
CompassHit m_currentHit;
|
||||
|
|
|
@ -10,25 +10,29 @@
|
|||
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
|
||||
library to handle filepathing. Also, removed scalers (for now).
|
||||
library to handle filepathing.
|
||||
|
||||
GWM -- Feb 2022
|
||||
|
||||
Update to reflect new CAEN binary data format with headers to indicate data contents.
|
||||
|
||||
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"
|
||||
|
||||
namespace Specter {
|
||||
|
||||
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) :
|
||||
DataSource(), m_directory(dir), m_startIndex(0), m_nchannels_per_board(channels_per_board)
|
||||
CompassRun::CompassRun(const std::string& dir) :
|
||||
DataSource(), m_directory(dir), m_startIndex(0)
|
||||
{
|
||||
CollectFiles();
|
||||
}
|
||||
|
@ -147,7 +151,7 @@ namespace Specter {
|
|||
m_datum.shortEnergy = m_hit.energyShort;
|
||||
m_datum.calEnergy = m_hit.energyCalibrated;
|
||||
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;
|
||||
|
|
|
@ -8,17 +8,17 @@
|
|||
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
|
||||
library to handle filepathing. One change of great import: 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.
|
||||
library to handle filepathing.
|
||||
|
||||
GWM -- Feb 2022
|
||||
|
||||
Update to reflect new CAEN binary data format with headers to indicate data contents.
|
||||
|
||||
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
|
||||
#define COMPASSRUN_H
|
||||
|
@ -36,7 +36,7 @@ namespace Specter {
|
|||
|
||||
public:
|
||||
CompassRun();
|
||||
CompassRun(const std::string& dir, int channels_per_board=16);
|
||||
CompassRun(const std::string& dir);
|
||||
virtual ~CompassRun();
|
||||
virtual const SpecData& GetData() override;
|
||||
inline void SetDirectory(const std::string& dir) { m_directory = dir; CollectFiles(); }
|
||||
|
@ -52,8 +52,7 @@ namespace Specter {
|
|||
|
||||
std::vector<CompassFile> m_datafiles;
|
||||
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;
|
||||
|
||||
CompassHit m_hit;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace Specter {
|
||||
|
||||
DYFileSource::DYFileSource(const std::string& directory, int channels_per_board) :
|
||||
DataSource(), m_directory(directory), m_channelsPerBoard(channels_per_board)
|
||||
DYFileSource::DYFileSource(const std::string& directory) :
|
||||
DataSource(), m_directory(directory)
|
||||
{
|
||||
CollectFiles();
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ namespace Specter {
|
|||
m_datum.longEnergy = m_dyHit.energy;
|
||||
m_datum.shortEnergy = m_dyHit.energyShort;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Specter {
|
|||
class DYFileSource : public DataSource
|
||||
{
|
||||
public:
|
||||
DYFileSource(const std::string& directory, int channels_per_board = 16);
|
||||
DYFileSource(const std::string& directory);
|
||||
virtual ~DYFileSource();
|
||||
|
||||
virtual const SpecData& GetData() override;
|
||||
|
@ -21,7 +21,6 @@ namespace Specter {
|
|||
|
||||
std::filesystem::path m_directory;
|
||||
static constexpr std::string_view s_extension = ".dybin";
|
||||
int m_channelsPerBoard;
|
||||
|
||||
std::vector<DaqGrimoire::DYFileReader> m_files;
|
||||
DaqGrimoire::DYListData m_dyHit;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace Specter {
|
||||
|
||||
DYOnlineSource::DYOnlineSource(const std::string& hostname, const std::string& port, int channelsPerBoard) :
|
||||
DataSource(), m_clientConnection(hostname, port), m_channelsPerBoard(channelsPerBoard)
|
||||
DYOnlineSource::DYOnlineSource(const std::string& hostname, const std::string& port) :
|
||||
DataSource(), m_clientConnection(hostname, port)
|
||||
{
|
||||
m_validFlag = m_clientConnection.IsConnected();
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace Specter {
|
|||
m_datum.longEnergy = m_dyHit.energy;
|
||||
m_datum.shortEnergy = m_dyHit.energyShort;
|
||||
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
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace Specter {
|
|||
class DYOnlineSource : public DataSource
|
||||
{
|
||||
public:
|
||||
DYOnlineSource(const std::string& hostname, const std::string& port, int channelsPerBoard = 16);
|
||||
DYOnlineSource(const std::string& hostname, const std::string& port);
|
||||
~DYOnlineSource();
|
||||
|
||||
virtual const SpecData& GetData() override;
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
namespace Specter {
|
||||
|
||||
//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)
|
||||
{
|
||||
case DataSource::SourceType::CompassOffline : return new CompassRun(location, channels_per_board);
|
||||
case DataSource::SourceType::CompassOnline : return new CompassOnlineSource(location, port, header, channels_per_board);
|
||||
case DataSource::SourceType::DaqromancyOffline: return new DYFileSource(location, channels_per_board);
|
||||
case DataSource::SourceType::DaqromancyOnline: return new DYOnlineSource(location, port, channels_per_board);
|
||||
case DataSource::SourceType::CompassOffline : return new CompassRun(location);
|
||||
case DataSource::SourceType::CompassOnline : return new CompassOnlineSource(location, port, header);
|
||||
case DataSource::SourceType::DaqromancyOffline: return new DYFileSource(location);
|
||||
case DataSource::SourceType::DaqromancyOnline: return new DYOnlineSource(location, port);
|
||||
case DataSource::SourceType::None : return nullptr;
|
||||
}
|
||||
SPEC_WARN("Invalid DataSourceType at CreateDataSource!");
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace Specter {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ namespace Specter {
|
|||
{
|
||||
SPEC_PROFILE_FUNCTION();
|
||||
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.SetSortFlag(event.GetSortFlag());
|
||||
m_eventBuilder.ClearAll(); //Protect against stopping mid-event
|
||||
|
|
8
Specter/src/Specter/Utils/Functions.cpp
Normal file
8
Specter/src/Specter/Utils/Functions.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "Functions.h"
|
||||
|
||||
namespace Specter {
|
||||
|
||||
namespace Utilities {
|
||||
|
||||
}
|
||||
}
|
19
Specter/src/Specter/Utils/Functions.h
Normal file
19
Specter/src/Specter/Utils/Functions.h
Normal 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
|
|
@ -18,5 +18,6 @@
|
|||
|
||||
#include "Specter/Core/Logger.h"
|
||||
#include "Specter/Utils/Instrumentor.h"
|
||||
#include "Specter/Utils/Functions.h"
|
||||
|
||||
#endif
|
2
Specter/vendor/DaqGrimoire
vendored
2
Specter/vendor/DaqGrimoire
vendored
|
@ -1 +1 @@
|
|||
Subproject commit dedd1e0e953423a50ec8a35f4cb3161218aac642
|
||||
Subproject commit 2bd094a97c4b244452fe04f2af12b1c8d469894e
|
Loading…
Reference in New Issue
Block a user