1
0
Fork 0
mirror of https://github.com/gwm17/Daqromancy.git synced 2024-05-19 15:23:21 -04:00

First commit

This commit is contained in:
Gordon McCann 2022-09-27 14:44:08 -04:00
commit d9d0b81cb4
76 changed files with 13482 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
*.o
*.root
*.d
*.so
*.pcm
*.png
*.cxx
*~
setting/infl_*
.vscode/
build/
bin/
out/

32
CMakeLists.txt Normal file
View File

@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.16) #pch support
set(CMAKE_CXX_STANDARD 20) #c++20
#currently default to debug. Eventually move to release
if(NOT CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_BUILD_TYPE "Debug")
message("Building debug")
else()
message("Building release")
endif()
project(Daqromancy)
set(DY_BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bin)
set(DY_LIBRARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib)
#find the CAEN pre-reqs
#For Windows, CAENDigitizer is installed non-globally (we assume default pathing cause why would ever be otherwise)
set(CAEN_WIN_LIB_PATH "C:/Program Files/CAEN/Digitizers/Library/lib/x86_64")
set(CAEN_WIN_INCL_PATH "C:/Program Files/CAEN/Digitizers/Library/include")
if(MSVC)
find_library(CAEN_DGTZ CAENDigitizer.lib HINTS ${CAEN_WIN_LIB_PATH} REQUIRED)
else()
find_library(CAEN_DGTZ CAENDigitizer REQUIRED)
endif()
add_subdirectory(vendor/glfw)
add_subdirectory(vendor/imgui)
add_subdirectory(vendor/glad)
add_subdirectory(vendor/yaml-cpp)
add_subdirectory(src)

41
README.md Normal file
View File

@ -0,0 +1,41 @@
# Daqromancy
Daqromancy is a data aquisition application designed for use with CAEN DPP digitizers using the CAENDigitizer library. Daqromancy is focused on providing a clean, powerful interface to the digitizers in an open source,
(hopefully) friendly environment.
Daqromancy does not provide online data analysis/visulization short of a simple osciloscope and data rates. Daqromancy does provide a data server which can be used to pipe data to an external analysis program.
## Installation
Currently, Daqromancy only supports installing from source.
To download the Daqromancy source retrieve the repository using `git clone --recursive https://github.com/gwm17/Daqromancy.git`. Move into the repostiory and follow standard CMake build instructions. For example,
on a Linux system use the following commands:
```
mkdir build
cd build
cmake ..
make -j 4
```
This will build and install the Daqromancy executable in the `bin` directory of the repository.
## About
This project started as an extension of the [BoxScore](https://github.com/goluckyryan/BoxScore) application framework, developed by @goluckyryan. As it grew, it became clear that the extension had become it's own
monster. This monster is known as Daqromancy!
First and foremost, Daqromancy was an opprotunity for me to learn more about how a DAQ works and how to design a project of this scope and complexity. So it should be noted that Daqromancy is a learning process,
and will most likely undergo many changes and refactors before it is ready to do anything serious.
Daqromancy aims to provide a cross-platform DAQ that is both readable and transparent without sacrificing performance.
## Dependencies
Daqromancy requires the [CAENDigitizer](https://www.caen.it) library to be installed (this is the middelware that allows for digitizer communication).
The CAENDigitizer library only supports Linux and Windows at this time; as such Daqromancy only targets Windows and Linux.
On Windows, it is recommended to use the Visual Studio environment to build the project.
On Linux, Daqromancy needs the development tools for the X system. On Ubuntu this can be installed using:
`sudo apt install libgl1 libgl1-mesa-dev libglu1-mesa libglu1-mesa-dev xorg-dev mesa-utils`
## Warning
Daqromancy is under very heavy development still, and as such should not be used in any production capacity at this time

130
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,130 @@
add_executable(Daqromancy)
target_include_directories(Daqromancy
PUBLIC ../vendor/imgui
../vendor/glfw/include
../vendor/spdlog/include
../vendor/glad/include
../vendor/yaml-cpp/include
../vendor/implot/
../vendor/asio/asio/include
./
)
#For Windows, CAENDigitizer is installed non-globally (we assume default pathing cause why would ever be otherwise)
if(MSVC)
target_include_directories(Daqromancy SYSTEM PUBLIC ${CAEN_WIN_INCL_PATH})
endif()
target_precompile_headers(Daqromancy PRIVATE dypch.h)
target_sources(Daqromancy PRIVATE
DAQ/Digitizer.h
DAQ/Digitizer.cpp
DAQ/DigitizerDefs.h
Core/Logger.h
Core/Logger.cpp
Core/main.cpp
Core/Application.h
Core/Application.cpp
Events/Event.h
Core/DYCore.h
Events/AppEvent.h
Events/MouseEvent.h
Events/AcqEvent.h
Core/Layer.h
Core/Layer.cpp
Core/LayerStack.h
Core/LayerStack.cpp
Core/Window.h
Platform/OpenGL/OpenGLWindow.h
Renderer/GraphicsContext.h
Platform/OpenGL/OpenGLContext.h
Platform/OpenGL/OpenGLContext.cpp
Platform/OpenGL/OpenGLWindow.cpp
Renderer/RendererAPI.h
Renderer/RendererAPI.cpp
Renderer/RenderCommand.h
Renderer/RenderCommand.cpp
Platform/OpenGL/OpenGLRendererAPI.cpp
ImGui/ImGuiLayer.h
ImGui/ImGuiLayer.cpp
ImGui/ImGuiBuild.cpp
Editor/EditorLayer.h
Editor/EditorLayer.cpp
Editor/FileDialog.h
Editor/FileDialog.cpp
DAQ/AcquisitionLayer.h
DAQ/AcquisitionLayer.cpp
Core/DYProject.h
Core/DYProject.cpp
Editor/DigitizerPanel.h
Editor/DigitizerPanel.cpp
DAQ/DigitizerDefs.cpp
Editor/SyncDialog.cpp
Core/ProjectSerializer.h
Core/ProjectSerializer.cpp
Editor/ScopePanel.h
Editor/ScopePanel.cpp
DYio/DYFile.h
DYio/DYFile.cpp
DYio/DYRun.h
DYio/DYRun.cpp
DYio/TCPServer.h
DYio/TCPServer.cpp
DYio/DYListData.h
DYio/DYMessage.h
Core/ThreadSafeQueue.h
Core/DataDistributor.h
Core/DataDistributor.cpp
DAQ/DigitizerChain.h
DAQ/DigitizerChain.cpp
Core/ScalarDistributor.h
Core/ScalarDistributor.cpp
Editor/ScalarPanel.h
Editor/ScalarPanel.cpp
)
#ImPlot sources
target_sources(Daqromancy PRIVATE
../vendor/implot/implot.cpp
../vendor/implot/implot.h
../vendor/implot/implot_demo.cpp
../vendor/implot/implot_internal.h
../vendor/implot/implot_items.cpp
../vendor/implot/backends/implot_backend.h
../vendor/implot/backends/implot_impl_opengl3.cpp
../vendor/implot/backends/implot_impl_opengl3.h
)
#Packaged dependencies
target_link_libraries(Daqromancy PRIVATE imgui glfw glad yaml-cpp)
#CAEN dependencies
target_link_libraries(Daqromancy PRIVATE ${CAEN_DGTZ})
#Link OS graphics implementations
set(THREADS_PREFER_PTHREAD_FLAG On)
find_package(Threads REQUIRED)
if(APPLE)
target_link_libraries(Daqromancy PRIVATE "-framework Cocoa" "-framework CoreVideo" "-framework IOKit" "-framework OpenGL" "-framework Carbon" ${CMAKE_DL_LIBS} Threads::Threads)
target_compile_definitions(Daqromancy PRIVATE DY_APPLE)
elseif(UNIX)
target_link_libraries(Daqromancy PRIVATE libGL.so libX11.so ${CMAKE_DL_LIBS} Threads::Threads)
target_compile_definitions(Daqromancy PRIVATE DY_LINUX)
elseif(MSVC)
target_link_libraries(Daqromancy PRIVATE opengl32.lib Threads::Threads)
target_compile_definitions(Daqromancy PRIVATE DY_WINDOWS)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Release")
target_compile_definitions(Daqromancy PRIVATE BS_RELEASE)
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(Daqromancy PRIVATE BS_DEBUG)
endif()
#Some extra defs
target_compile_definitions(Daqromancy PRIVATE GLFW_INCLUDE_NONE YAML_CPP_STATIC_DEFINE IMGUI_IMPL_OPENGL_LOADER_GLAD IMPLOT_BACKEND_ENABLE_OPENGL3)
set_target_properties(Daqromancy PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${DY_BINARY_DIR})

118
src/Core/Application.cpp Normal file
View File

@ -0,0 +1,118 @@
#include "Application.h"
#include "Renderer/RenderCommand.h"
#include "Editor/EditorLayer.h"
#include "DAQ/AcquisitionLayer.h"
#include <filesystem>
namespace Daqromancy {
Application* Application::s_instance = nullptr;
Application* CreateApplication(const ApplicationArgs& args) { return new Application(args); }
Application::Application(const ApplicationArgs& args) :
m_args(args), m_running(true)
{
if(s_instance != nullptr)
{
DY_ERROR("Attempting to create more than one application! CreateApplication should only be called once!");
}
s_instance = this;
DY_INFO("Application created.");
//Set current path to find assets
if(!m_args.runtimePath.empty())
std::filesystem::current_path(m_args.runtimePath);
DY_INFO("Runtime path set to {0}", std::filesystem::current_path().string());
m_window = std::unique_ptr<Window>(Window::Create({ m_args.name, 1280, 720 }));
m_window->SetEventCallback(BIND_EVENT_FUNCTION(Application::OnEvent));
m_imguiLayer = new ImGuiLayer();
PushOverlay(m_imguiLayer);
m_project = std::make_shared<DYProject>();
EditorLayer* editor = new EditorLayer(m_project);
editor->SetEventCallback(BIND_EVENT_FUNCTION(Application::OnEvent));
PushLayer(editor);
AcquisitionLayer* acq = new AcquisitionLayer(m_project);
acq->SetEventCallback(BIND_EVENT_FUNCTION(Application::OnEvent));
PushLayer(acq);
}
Application::~Application() {}
void Application::OnEvent(Event& e)
{
EventDispatcher dispatch(e);
dispatch.Dispatch<WindowCloseEvent>(BIND_EVENT_FUNCTION(Application::OnWindowCloseEvent));
for (auto iter = m_layerStack.rbegin(); iter != m_layerStack.rend(); iter++)
{
if (e.handled)
break;
(*iter)->OnEvent(e);
}
}
void Application::PushLayer(Layer* layer)
{
m_layerStack.PushLayer(layer);
layer->OnAttach();
}
void Application::PushOverlay(Layer* layer)
{
m_layerStack.PushOverlay(layer);
layer->OnAttach();
}
bool Application::OnWindowCloseEvent(WindowCloseEvent& e)
{
m_running = false;
return true;
}
void Application::Run()
{
if (!m_running)
DY_ERROR("Application attempting to run after having already exited");
float bckgndColor[4] = { 0.1, 0.1, 0.1, 1.0 };
//Time stuff
double currentTime = 0.0;
double prevTime = 0.0;
double timestep;
while (m_running)
{
RenderCommand::SetClearColor(bckgndColor);
RenderCommand::Clear();
currentTime = RenderCommand::GetFrameTime();
timestep = currentTime - prevTime;
prevTime = currentTime;
for (auto& layer : m_layerStack)
layer->OnUpdate();
m_imguiLayer->Begin();
for (auto& layer : m_layerStack)
layer->OnImGuiRender(timestep);
m_imguiLayer->End();
m_window->OnUpdate();
}
}
}

56
src/Core/Application.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef APPLICATION_H
#define APPLICATION_H
#include "Events/Event.h"
#include "Events/AppEvent.h"
#include "LayerStack.h"
#include "Window.h"
#include "ImGui/ImGuiLayer.h"
#include "DYProject.h"
namespace Daqromancy {
struct ApplicationArgs
{
std::string name;
std::string runtimePath;
int argc;
char** argv;
};
class Application
{
public:
Application(const ApplicationArgs& args);
~Application();
void Run();
void Close() { m_running = false; }
void OnEvent(Event& e);
void PushLayer(Layer* layer);
void PushOverlay(Layer* layer);
//App Event Callbacks
bool OnWindowCloseEvent(WindowCloseEvent& e);
const ApplicationArgs& GetArgs() { return m_args; }
Window& GetWindow() { return *m_window; }
static Application& GetInstance() { return *s_instance; }
private:
ApplicationArgs m_args;
bool m_running;
std::unique_ptr<Window> m_window;
LayerStack m_layerStack;
ImGuiLayer* m_imguiLayer;
DYProject::Ref m_project;
static Application* s_instance;
};
Application* CreateApplication(const ApplicationArgs& args);
}
#endif

26
src/Core/DYCore.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef DY_CORE_H
#define DY_CORE_H
#ifdef BS_WINDOWS
#ifdef _MSC_VER
#pragma warning (disable: 4127) // condition expression is constant
#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when NAV_API is set to__declspec(dllexport)
#pragma warning (disable: 4091) // '__declspec(dllimport)': ignored on left of class 'xxx' when no variable is declared
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
#endif
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#endif
#endif
//Bit field setter
#define BIT(x) (1<<x)
//Macro to bind a function using lambda expressions
#define BIND_EVENT_FUNCTION(x) [this](auto&&... args) -> decltype(auto) { return this->x(std::forward<decltype(args)>(args)...); }
#endif

231
src/Core/DYProject.cpp Normal file
View File

@ -0,0 +1,231 @@
#include "DYProject.h"
namespace Daqromancy {
DYProject::DYProject() :
m_runNumber(0), m_dppMode(DPPAcqMode::List)
{
}
DYProject::~DYProject() {}
void DYProject::InternalClear()
{
m_argList.clear();
m_boardParamList.clear();
m_psdChannelMap.clear();
m_phaChannelMap.clear();
m_psdWaveMap.clear();
m_phaWaveMap.clear();
}
bool DYProject::SetProjectPath(const std::filesystem::path& path)
{
bool status = true;
m_projectPath = path.lexically_normal();
if (!std::filesystem::exists(m_projectPath))
{
status = std::filesystem::create_directory(path);
if (!status)
m_projectPath.clear();
}
return status;
}
//void DYProject::SetDigitizerData(const std::vector<Digitizer::Ref>& chain)
void DYProject::SetDigitizerData(const DigitizerChain& chain)
{
InternalClear();
//buffers
std::vector<PSDParameters> psdChannels;
std::vector<PHAParameters> phaChannels;
PSDWaveParameters psdWaves;
PHAWaveParameters phaWaves;
for (auto& digitizer : chain)
{
auto& args = digitizer->GetDigitizerArgs();
m_argList.push_back(args);
m_boardParamList.push_back(digitizer->GetDigitizerParameters());
if (args.firmware == CAEN_DGTZ_DPPFirmware_PHA)
{
digitizer->GetChannelParameters(phaChannels);
digitizer->GetWaveformParameters(phaWaves);
m_phaChannelMap[args.handle] = phaChannels;
m_phaWaveMap[args.handle] = phaWaves;
}
else if(args.firmware == CAEN_DGTZ_DPPFirmware_PSD)
{
digitizer->GetChannelParameters(psdChannels);
digitizer->GetWaveformParameters(psdWaves);
m_psdChannelMap[args.handle] = psdChannels;
m_psdWaveMap[args.handle] = psdWaves;
}
else
{
DY_ERROR("Invalid digitizer firmware detected at DYProject::SetDigitizerData!");
}
}
}
void DYProject::SetDigitizerArgsList(const std::vector<DigitizerArgs>& args)
{
m_argList = args;
}
void DYProject::SetDigitizerParameterList(const std::vector<DigitizerParameters>& params)
{
m_boardParamList = params;
}
void DYProject::SetDigitizerParameters(int handle, const DigitizerParameters& params)
{
if (handle >= m_boardParamList.size() || handle == -1)
{
DY_ERROR("Attempting to set digitizer parameters for non-extant board: given handle {0}, number of boards {1}", handle, m_boardParamList.size());
return;
}
m_boardParamList[handle] = params;
}
void DYProject::SetPHAParameters(int handle, const std::vector<PHAParameters>& params)
{
m_phaChannelMap[handle] = params;
}
void DYProject::SetPSDParameters(int handle, const std::vector<PSDParameters>& params)
{
m_psdChannelMap[handle] = params;
}
void DYProject::SetPHAWaveParameters(int handle, const PHAWaveParameters& params)
{
m_phaWaveMap[handle] = params;
}
void DYProject::SetPSDWaveParameters(int handle, const PSDWaveParameters& params)
{
m_psdWaveMap[handle] = params;
}
void DYProject::SetRunNumber(uint32_t number)
{
m_runNumber = number;
}
void DYProject::IncrementRunNumber()
{
++m_runNumber;
}
void DYProject::SetDPPAcqMode(DPPAcqMode mode)
{
m_dppMode = mode;
}
const std::filesystem::path& DYProject::GetProjectPath()
{
return m_projectPath;
}
const std::vector<DigitizerArgs>& DYProject::GetDigitizerArgsList()
{
return m_argList;
}
DigitizerArgs DYProject::GetDigitizerArgs(int handle)
{
if (handle >= m_argList.size() || handle == -1)
{
DY_ERROR("Attempting to get digitizer args for non-extant board: given handle {0}, number of boards {1}", handle, m_argList.size());
return DigitizerArgs();
}
return m_argList[handle];
}
const std::vector<DigitizerParameters>& DYProject::GetDigitizerParameterList()
{
return m_boardParamList;
}
DigitizerParameters DYProject::GetDigitizerParameters(int handle)
{
if (handle >= m_boardParamList.size() || handle == -1)
{
DY_ERROR("Attempting to get digitizer parameters for non-extant board: given handle {0}, number of boards {1}", handle, m_boardParamList.size());
return DigitizerParameters();
}
return m_boardParamList[handle];
}
const std::vector<PHAParameters>& DYProject::GetPHAParameters(int handle)
{
auto iter = m_phaChannelMap.find(handle);
if (iter != m_phaChannelMap.end())
return iter->second;
DY_ERROR("Invalid handle {0} given to DYProject::GetPHAParameters!", handle);
return m_nullphaChannels;
}
const std::vector<PSDParameters>& DYProject::GetPSDParameters(int handle)
{
auto iter = m_psdChannelMap.find(handle);
if (iter != m_psdChannelMap.end())
return iter->second;
DY_ERROR("Invalid handle {0} given to DYProject::GetPHAParameters!", handle);
return m_nullpsdChannels;
}
PHAWaveParameters DYProject::GetPHAWaveParameters(int handle)
{
auto iter = m_phaWaveMap.find(handle);
if (iter != m_phaWaveMap.end())
return iter->second;
DY_ERROR("Invalid handle {0} given to DYProject::GetPHAParameters!", handle);
return PHAWaveParameters();
}
PSDWaveParameters DYProject::GetPSDWaveParameters(int handle)
{
auto iter = m_psdWaveMap.find(handle);
if (iter != m_psdWaveMap.end())
return iter->second;
DY_ERROR("Invalid handle {0} given to DYProject::GetPHAParameters!", handle);
return PSDWaveParameters();
}
uint32_t DYProject::GetRunNumber()
{
return m_runNumber;
}
DPPAcqMode DYProject::GetDPPAcqMode()
{
return m_dppMode;
}
size_t DYProject::GetNumberOfBoards()
{
return m_argList.size();
}
std::filesystem::path DYProject::CreateRunDirectory()
{
std::string runName = "run_" + std::to_string(m_runNumber);
std::filesystem::path runPath = m_projectPath / runName;
if (std::filesystem::exists(runPath))
{
DY_WARN("Run directory {0} already exists! Could lead to overwritting data!", runPath);
}
else
{
std::filesystem::create_directory(runPath);
}
return runPath;
}
}

74
src/Core/DYProject.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef DY_PROJECT_H
#define DY_PROJECT_H
#include "DAQ/DigitizerDefs.h"
#include "DAQ/Digitizer.h"
#include "DAQ/DigitizerChain.h"
#include <filesystem>
namespace Daqromancy {
class DYProject
{
public:
using Ref = std::shared_ptr<DYProject>;
DYProject();
~DYProject();
bool SetProjectPath(const std::filesystem::path& path);
//void SetDigitizerData(const std::vector<Digitizer::Ref>& chain);
void SetDigitizerData(const DigitizerChain& chain);
void SetDigitizerArgsList(const std::vector<DigitizerArgs>& args);
void SetDigitizerParameterList(const std::vector<DigitizerParameters>& params);
void SetDigitizerParameters(int handle, const DigitizerParameters& params);
void SetPHAParameters(int handle, const std::vector<PHAParameters>& params);
void SetPSDParameters(int handle, const std::vector<PSDParameters>& params);
void SetPHAWaveParameters(int handle, const PHAWaveParameters& params);
void SetPSDWaveParameters(int handle, const PSDWaveParameters& params);
void SetRunNumber(uint32_t number);
void IncrementRunNumber();
void SetDPPAcqMode(DPPAcqMode mode);
const std::filesystem::path& GetProjectPath();
const std::vector<DigitizerArgs>& GetDigitizerArgsList();
DigitizerArgs GetDigitizerArgs(int handle);
const std::vector<DigitizerParameters>& GetDigitizerParameterList();
DigitizerParameters GetDigitizerParameters(int handle);
const std::vector<PHAParameters>& GetPHAParameters(int handle);
const std::vector<PSDParameters>& GetPSDParameters(int handle);
PHAWaveParameters GetPHAWaveParameters(int handle);
PSDWaveParameters GetPSDWaveParameters(int handle);
uint32_t GetRunNumber();
DPPAcqMode GetDPPAcqMode();
size_t GetNumberOfBoards();
std::filesystem::path CreateRunDirectory();
private:
void InternalClear();
std::vector<DigitizerArgs> m_argList;
std::vector<DigitizerParameters> m_boardParamList;
std::unordered_map<int, std::vector<PHAParameters>> m_phaChannelMap;
std::unordered_map<int, std::vector<PSDParameters>> m_psdChannelMap;
std::unordered_map<int, PHAWaveParameters> m_phaWaveMap;
std::unordered_map<int, PSDWaveParameters> m_psdWaveMap;
std::filesystem::path m_projectPath;
uint32_t m_runNumber;
DPPAcqMode m_dppMode;
//some null result stuff
std::vector<PHAParameters> m_nullphaChannels;
std::vector<PSDParameters> m_nullpsdChannels;
};
}
#endif

View File

@ -0,0 +1,31 @@
#include "DataDistributor.h"
namespace Daqromancy {
uint64_t DataDistributor::s_nextClientID = 0;
std::unordered_map<uint64_t, ThreadSafeQueue<std::vector<DYData>>*> DataDistributor::s_clientMap;
void DataDistributor::PushData(const std::vector<DYData>& data)
{
for (auto& iter : s_clientMap)
iter.second->PushBack(data);
}
DistributorClient DataDistributor::Connect()
{
DistributorClient client;
client.id = s_nextClientID++;
client.dataQueue = new ThreadSafeQueue<std::vector<DYData>>();
s_clientMap[client.id] = client.dataQueue;
return client;
}
void DataDistributor::Disconnect(DistributorClient& client)
{
s_clientMap.erase(client.id);
delete client.dataQueue;
client.dataQueue = nullptr;
}
}

View File

@ -0,0 +1,41 @@
/*
DataDistributor.h
Thread-safe distribution of data to multpile clients. Each client must register using the Connect() function, and de-register using Disconnect().
A client is given a DistributorClient struct which contains the id for that client as well as a raw pointer to the dataQueue allocated for that client.
Use raw pointers to emphasize that memory is not automatically managed, and the Connect()/Disconnect() idiom MUST be used.
This is a singleton, there must always be one and only one DataDistributor
DataDistributor itself is not thread-safe. All connect/disconnect calls should be done from the main application thread. PushData / queue accesss is thread safe,
as the queues are internally thread-safe (as the name implies)
*/
#ifndef DATA_DISTRIBUTOR_H
#define DATA_DISTRIBUTOR_H
#include "ThreadSafeQueue.h"
#include "DAQ/DigitizerDefs.h"
namespace Daqromancy {
struct DistributorClient
{
uint64_t id = -1; //max val, illegal value
ThreadSafeQueue<std::vector<DYData>>* dataQueue = nullptr;
};
class DataDistributor
{
public:
static void PushData(const std::vector<DYData>& data);
static DistributorClient Connect();
static void Disconnect(DistributorClient& client);
private:
static std::unordered_map<uint64_t, ThreadSafeQueue<std::vector<DYData>>*> s_clientMap;
static uint64_t s_nextClientID;
};
}
#endif

11
src/Core/Layer.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "Layer.h"
namespace Daqromancy {
Layer::Layer(const std::string& name) :
m_name(name)
{
}
Layer::~Layer() {}
}

27
src/Core/Layer.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef LAYER_H
#define LAYER_H
#include "Events/Event.h"
namespace Daqromancy {
class Layer
{
public:
Layer(const std::string& name = "Layer");
virtual ~Layer();
virtual void OnAttach() {};
virtual void OnDetach() {};
virtual void OnUpdate() {};
virtual void OnEvent(Event& e) {};
virtual void OnImGuiRender(double timestep) {};
const std::string& GetName() { return m_name; }
private:
std::string m_name;
};
}
#endif

42
src/Core/LayerStack.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "LayerStack.h"
namespace Daqromancy {
LayerStack::LayerStack() {}
LayerStack::~LayerStack()
{
for (Layer* layer : m_stack)
delete layer;
}
void LayerStack::PushLayer(Layer* layer)
{
m_stack.emplace(m_stack.begin() + m_insertIndex, layer);
m_insertIndex++;
}
void LayerStack::PushOverlay(Layer* layer)
{
m_stack.emplace_back(layer);
}
void LayerStack::PopLayer(Layer* layer)
{
auto iter = std::find(m_stack.begin(), m_stack.end(), layer);
if (iter != m_stack.end())
{
m_stack.erase(iter);
m_insertIndex--;
}
}
void LayerStack::PopOverlay(Layer* layer)
{
auto iter = std::find(m_stack.begin(), m_stack.end(), layer);
if (iter != m_stack.end())
{
m_stack.erase(iter);
}
}
}

30
src/Core/LayerStack.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef LAYER_STACK_H
#define LAYER_STACK_H
#include "Layer.h"
namespace Daqromancy {
class LayerStack
{
public:
LayerStack();
~LayerStack();
void PushLayer(Layer* layer);
void PopLayer(Layer* layer);
void PushOverlay(Layer* layer);
void PopOverlay(Layer* layer);
std::vector<Layer*>::iterator begin() { return m_stack.begin(); }
std::vector<Layer*>::iterator end() { return m_stack.end(); }
std::vector<Layer*>::reverse_iterator rbegin() { return m_stack.rbegin(); }
std::vector<Layer*>::reverse_iterator rend() { return m_stack.rend(); }
private:
std::vector<Layer*> m_stack;
size_t m_insertIndex = 0;
};
}
#endif

14
src/Core/Logger.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "Logger.h"
#include "spdlog/sinks/stdout_color_sinks.h"
namespace Daqromancy {
std::shared_ptr<spdlog::logger> Logger::s_logger;
void Logger::Init()
{
spdlog::set_pattern("%^[%T] %n: %v%$");
s_logger = spdlog::stdout_color_mt("BXSC");
s_logger->set_level(spdlog::level::trace);
}
}

30
src/Core/Logger.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef LOGGER_H
#define LOGGER_H
#include "spdlog/spdlog.h"
#include "spdlog/fmt/ostr.h"
namespace Daqromancy {
class Logger
{
public:
static void Init();
static std::shared_ptr<spdlog::logger> GetLogger() { return s_logger; }
private:
static std::shared_ptr<spdlog::logger> s_logger;
};
//Macros for simple logging.
#define DY_CRITICAL(...) ::Daqromancy::Logger::GetLogger()->critical(__VA_ARGS__)
#define DY_WARN(...) ::Daqromancy::Logger::GetLogger()->warn(__VA_ARGS__)
#define DY_INFO(...) ::Daqromancy::Logger::GetLogger()->info(__VA_ARGS__)
#define DY_TRACE(...) ::Daqromancy::Logger::GetLogger()->trace(__VA_ARGS__)
#define DY_ERROR(...) ::Daqromancy::Logger::GetLogger()->error(__VA_ARGS__)
}
#endif

View File

@ -0,0 +1,555 @@
#include "ProjectSerializer.h"
#include <fstream>
#include "yaml-cpp/yaml.h"
namespace YAML {
template<>
struct convert<Daqromancy::DigitizerArgs>
{
static Node encode(const Daqromancy::DigitizerArgs& args)
{
Node node(NodeType::Map);
node.force_insert<std::string, int>("ConnectionType", args.type);
node.force_insert<std::string, int>("LinkNumber", args.linkNumber);
node.force_insert<std::string, int>("CONETNode", args.conetNode);
node.force_insert<std::string, uint32_t>("VMEAddress", args.vmeAddress);
node.force_insert<std::string, int>("Handle", args.handle);
node.force_insert<std::string, int>("Model", args.model);
node.force_insert<std::string, int>("Firmware", args.firmware);
node.force_insert<std::string, int>("Channels", args.channels);
return node;
}
static bool decode(const Node& node, Daqromancy::DigitizerArgs& args)
{
if (!node.IsMap())
return false;
args.type = (CAEN_DGTZ_ConnectionType) node["ConnectionType"].as<int>();
args.linkNumber = node["LinkNumber"].as<int>();
args.conetNode = node["ConetNode"].as<int>();
args.vmeAddress = node["VMEAddress"].as<uint32_t>();
args.handle = node["Handle"].as<int>();
args.model = (CAEN_DGTZ_BoardModel_t) node["Model"].as<int>();
args.firmware = (CAEN_DGTZ_DPPFirmware_t) node["Firmware"].as<int>();
args.channels = node["Channels"].as<int>();
return true;
}
};
template<>
struct convert<Daqromancy::DigitizerParameters>
{
static Node encode(const Daqromancy::DigitizerParameters& params)
{
Node node(NodeType::Map);
node.force_insert<std::string, int>("RecordLength", params.recordLength);
node.force_insert<std::string, uint32_t>("ChannelMask", params.channelMask);
node.force_insert<std::string, int>("EventAggr", params.eventAggr);
node.force_insert<std::string, int>("AcqMode", params.acqMode);
node.force_insert<std::string, int>("DPPAcqMode", params.dppAcqMode);
node.force_insert<std::string, int>("DPPSaveMode", params.dppSaveMode);
node.force_insert<std::string, int>("IOLevel", params.IOlevel);
node.force_insert<std::string, int>("TriggerMode", params.triggerMode);
node.force_insert<std::string, int>("SyncMode", params.syncMode);
return node;
}
static bool decode(const Node& node, Daqromancy::DigitizerParameters& params)
{
if (!node.IsMap())
return false;
params.recordLength = node["RecordLength"].as<uint32_t>();
params.channelMask = node["ChannelMask"].as<uint32_t>();
params.eventAggr = node["EventAggr"].as<int>();
params.acqMode = (CAEN_DGTZ_AcqMode_t)node["AcqMode"].as<int>();
params.dppAcqMode = (CAEN_DGTZ_DPP_AcqMode_t)node["DPPAcqMode"].as<int>();
params.dppSaveMode = (CAEN_DGTZ_DPP_SaveParam_t)node["DPPSaveMode"].as<int>();
params.IOlevel = (CAEN_DGTZ_IOLevel_t)node["IOLevel"].as<int>();
params.triggerMode = (CAEN_DGTZ_TriggerMode_t)node["TriggerMode"].as<int>();
params.syncMode = (CAEN_DGTZ_RunSyncMode_t)node["SyncMode"].as<int>();
return true;
}
};
template<>
struct convert<Daqromancy::PHAParameters>
{
static Node encode(const Daqromancy::PHAParameters& params)
{
Node node(NodeType::Map);
node.force_insert<std::string, bool>("IsEnabled", params.isEnabled);
node.force_insert<std::string, uint32_t>("PreTrigger", params.preTriggerTime);
node.force_insert<std::string, float>("DCOffset", params.dcOffset);
node.force_insert<std::string, int>("PulsePolarity", params.pulsePolarity);
node.force_insert<std::string, int>("DynamicRange", params.dynamicRange);
node.force_insert<std::string, int>("DecayTimeConst", params.decayTimeConst);
node.force_insert<std::string, int>("TrapFlatTop", params.trapFlatTop);
node.force_insert<std::string, int>("TrapRiseTime", params.trapRiseTime);
node.force_insert<std::string, int>("FlatTopDelay", params.flatTopDelay);
node.force_insert<std::string, int>("TriggerSmoothing", params.triggerFilterSmoothing);
node.force_insert<std::string, int>("InputRiseTime", params.inputRiseTime);
node.force_insert<std::string, int>("TriggerThreshold", params.triggerThreshold);
node.force_insert<std::string, int>("SamplesBaseline", params.samplesBaseLineMean);
node.force_insert<std::string, int>("SamplesPeak", params.samplesPeakMean);
node.force_insert<std::string, int>("PeakHoldoff", params.peakHoldOff);
node.force_insert<std::string, int>("BaselineHoldoff", params.baseLineHoldOff);
node.force_insert<std::string, int>("TriggerHoldoff", params.triggerHoldOff);
node.force_insert<std::string, int>("RiseTimeWindow", params.riseTimeValidationWindow);
node.force_insert<std::string, int>("RiseTimeDiscrimination", params.riseTimeDiscrimination);
node.force_insert<std::string, int>("DigitalProbeGain", params.digitalProbeGain);
node.force_insert<std::string, int>("EnergyNormFactor", params.energyNormalizationFactor);
node.force_insert<std::string, int>("InputDecimation", params.inputDecimation);
return node;
}
static bool decode(const Node& node, Daqromancy::PHAParameters& params)
{
if (!node.IsMap())
return false;
params.isEnabled = node["IsEnabled"].as<bool>();
params.preTriggerTime = node["PreTrigger"].as<uint32_t>();
params.dcOffset = node["DCOffset"].as<float>();
params.pulsePolarity = (CAEN_DGTZ_PulsePolarity_t)node["PulsePolarity"].as<int>();
params.dynamicRange = (Daqromancy::DynamicRange)node["DynamicRange"].as<int>();
params.decayTimeConst = node["DecayTimeConst"].as<int>();
params.trapFlatTop = node["TrapFlatTop"].as<int>();
params.trapRiseTime = node["TrapRiseTime"].as<int>();
params.flatTopDelay = node["FlatTopDelay"].as<int>();
params.triggerFilterSmoothing = node["TriggerSmoothing"].as<int>();
params.inputRiseTime = node["InputRiseTime"].as<int>();
params.triggerThreshold = node["TriggerThreshold"].as<int>();
params.samplesBaseLineMean = node["SamplesBaseline"].as<int>();
params.samplesPeakMean = node["SamplesPeak"].as<int>();
params.peakHoldOff = node["PeakHoldoff"].as<int>();
params.baseLineHoldOff = node["BaselineHoldoff"].as<int>();
params.triggerHoldOff = node["TriggerHoldoff"].as<int>();
params.riseTimeValidationWindow = node["RiseTimeWindow"].as<int>();
params.riseTimeDiscrimination = node["RiseTimeDiscrimination"].as<int>();
params.digitalProbeGain = node["DigitalProbeGain"].as<int>();
params.energyNormalizationFactor = node["EnergyNormFactor"].as<int>();
params.inputDecimation = node["InputDecimation"].as<int>();
return true;
}
};
template<>
struct convert<Daqromancy::PSDParameters>
{
static Node encode(const Daqromancy::PSDParameters& params)
{
Node node(NodeType::Map);
node.force_insert<std::string, bool>("IsEnabled", params.isEnabled);
node.force_insert<std::string, uint32_t>("PreTrigger", params.preTriggerTime);
node.force_insert<std::string, float>("DCOffset", params.dcOffset);
node.force_insert<std::string, int>("PulsePolarity", params.pulsePolarity);
node.force_insert<std::string, int>("DynamicRange", params.dynamicRange);
node.force_insert<std::string, int>("BaselineThreshold", params.baselineThreshold);
node.force_insert<std::string, int>("TriggerHoldoff", params.triggerHoldOff);
node.force_insert<std::string, int>("TriggerThreshold", params.triggerThreshold);
node.force_insert<std::string, int>("SelfTrigger", params.selfTrigger);
node.force_insert<std::string, int>("ChargeSensitivity", params.chargeSensitivity);
node.force_insert<std::string, int>("ShortGate", params.shortGate);
node.force_insert<std::string, int>("LongGate", params.longGate);
node.force_insert<std::string, int>("PreGate", params.preGate);
node.force_insert<std::string, int>("TriggerWindow", params.triggerValidationWindow);
node.force_insert<std::string, int>("BaselineSamples", params.samplesBasline);
node.force_insert<std::string, int>("DiscriminatorType", params.discrminatorType);
node.force_insert<std::string, int>("CFDFraction", params.cfdFraction);
node.force_insert<std::string, int>("CFDDelay", params.cfdDelay);
node.force_insert<std::string, int>("TriggerConfig", params.triggerConfig);
node.force_insert<std::string, int>("PileUpRejection", params.pileUpRejection);
node.force_insert<std::string, int>("PURGap", params.purgap);
return node;
}
static bool decode(const Node& node, Daqromancy::PSDParameters& params)
{
if (!node.IsMap())
return false;
params.isEnabled = node["IsEnabled"].as<bool>();
params.preTriggerTime = node["PreTrigger"].as<int>();
params.dcOffset = node["DCOffset"].as<float>();
params.pulsePolarity = (CAEN_DGTZ_PulsePolarity_t)node["PulsePolarity"].as<int>();
params.dynamicRange = (Daqromancy::DynamicRange)node["DynamicRange"].as<int>();
params.baselineThreshold = node["BaselineThreshold"].as<int>();
params.triggerHoldOff = node["TriggerHoldoff"].as<int>();
params.triggerThreshold = node["TriggerThreshold"].as<int>();
params.selfTrigger = node["SelfTrigger"].as<int>();
params.chargeSensitivity = node["ChargeSensitivity"].as<int>();
params.shortGate = node["ShortGate"].as<int>();
params.longGate = node["LongGate"].as<int>();
params.preGate = node["PreGate"].as<int>();
params.triggerValidationWindow = node["TriggerWindow"].as<int>();
params.samplesBasline = node["BaselineSamples"].as<int>();
params.discrminatorType = node["DiscriminatorType"].as<int>();
params.cfdFraction = node["CFDFraction"].as<int>();
params.cfdDelay = node["CFDDelay"].as<int>();
params.triggerConfig = (CAEN_DGTZ_DPP_TriggerConfig_t)node["TriggerConfig"].as<int>();
params.pileUpRejection = (CAEN_DGTZ_DPP_PUR_t)node["PileUpRejection"].as<int>();
params.purgap = node["PURGap"].as<int>();
return true;
}
};
template<>
struct convert<Daqromancy::PHAWaveParameters>
{
static Node encode(const Daqromancy::PHAWaveParameters& waves)
{
Node node(NodeType::Map);
node.force_insert<std::string, int>("IsDual", waves.isDual);
node.force_insert<std::string, int>("AnalogProbe1", waves.analogProbe1);
node.force_insert<std::string, int>("AnalogProbe2", waves.analogProbe2);
node.force_insert<std::string, int>("DigitalProbe1", waves.digitalProbe1);
return node;
}
static bool decode(const Node& node, Daqromancy::PHAWaveParameters& waves)
{
if (!node.IsMap())
return false;
waves.isDual = (CAEN_DGTZ_DPP_VirtualProbe_t)node["IsDual"].as<int>();
waves.analogProbe1 = (Daqromancy::PHAVirtualProbe1Options)node["AnalogProbe1"].as<int>();
waves.analogProbe2 = (Daqromancy::PHAVirtualProbe2Options)node["AnalogProbe2"].as<int>();
waves.digitalProbe1 = (Daqromancy::PHADigitalProbe1Options)node["DigitalProbe1"].as<int>();
return true;
}
};
template<>
struct convert<Daqromancy::PSDWaveParameters>
{
static Node encode(const Daqromancy::PSDWaveParameters& waves)
{
Node node(NodeType::Map);
node.force_insert<std::string, int>("IsDual", waves.isDual);
node.force_insert<std::string, int>("AnalogProbe1", waves.analogProbe1);
node.force_insert<std::string, int>("AnalogProbe2", waves.analogProbe2);
node.force_insert<std::string, int>("DigitalProbe1", waves.digitalProbe1);
node.force_insert<std::string, int>("DigitalProbe2", waves.digitalProbe2);
return node;
}
static bool decode(const Node& node, Daqromancy::PSDWaveParameters& waves)
{
if (!node.IsMap())
return false;
waves.isDual = (CAEN_DGTZ_DPP_VirtualProbe_t)node["IsDual"].as<int>();
waves.analogProbe1 = (Daqromancy::PSDVirtualProbe1Options)node["AnalogProbe1"].as<int>();
waves.analogProbe2 = (Daqromancy::PSDVirtualProbe2Options)node["AnalogProbe2"].as<int>();
waves.digitalProbe1 = (Daqromancy::PSDDigitalProbe1Options)node["DigitalProbe1"].as<int>();
waves.digitalProbe2 = (Daqromancy::PSDDigitalProbe2Options)node["DigitalProbe2"].as<int>();
return true;
}
};
Emitter& operator<<(Emitter& stream, const Daqromancy::DigitizerArgs& args)
{
stream << YAML::BeginMap;
stream << YAML::Key << "ConnectionType" << YAML::Value << args.type;
stream << YAML::Key << "LinkNumber" << YAML::Value << args.linkNumber;
stream << YAML::Key << "CONETNode" << YAML::Value << args.conetNode;
stream << YAML::Key << "VMEAddress" << YAML::Value << args.vmeAddress;
stream << YAML::Key << "Handle" << YAML::Value << args.handle;
stream << YAML::Key << "Model" << YAML::Value << args.model;
stream << YAML::Key << "Firmware" << YAML::Value << args.firmware;
stream << YAML::Key << "Channels" << YAML::Value << args.channels;
stream << YAML::EndMap;
return stream;
}
Emitter& operator<<(Emitter& stream, const Daqromancy::DigitizerParameters& params)
{
stream << YAML::BeginMap;
stream << YAML::Key << "RecordLength" << YAML::Value << params.recordLength;
stream << YAML::Key << "ChannelMask" << YAML::Value << params.channelMask;
stream << YAML::Key << "EventAggr" << YAML::Value << params.eventAggr;
stream << YAML::Key << "AcqMode" << YAML::Value << params.acqMode;
stream << YAML::Key << "DPPAcqMode" << YAML::Value << params.dppAcqMode;
stream << YAML::Key << "DPPSaveMode" << YAML::Value << params.dppSaveMode;
stream << YAML::Key << "IOLevel" << YAML::Value << params.IOlevel;
stream << YAML::Key << "TriggerMode" << YAML::Value << params.triggerMode;
stream << YAML::Key << "SyncMode" << YAML::Value << params.syncMode;
stream << YAML::EndMap;
return stream;
}
Emitter& operator<<(Emitter& stream, const Daqromancy::PHAParameters& params)
{
stream << YAML::BeginMap;
stream << YAML::Key << "IsEnabled" << YAML::Value << params.isEnabled;
stream << YAML::Key << "PreTrigger" << YAML::Value << params.preTriggerTime;
stream << YAML::Key << "DCOffset" << YAML::Value << params.dcOffset;
stream << YAML::Key << "PulsePolarity" << YAML::Value << params.pulsePolarity;
stream << YAML::Key << "DynamicRange" << YAML::Value << params.dynamicRange;
stream << YAML::Key << "DecayTimeConst" << YAML::Value << params.decayTimeConst;
stream << YAML::Key << "TrapFlatTop" << YAML::Value << params.trapFlatTop;
stream << YAML::Key << "TrapRiseTime" << YAML::Value << params.trapRiseTime;
stream << YAML::Key << "FlatTopDelay" << YAML::Value << params.flatTopDelay;
stream << YAML::Key << "TriggerSmoothing" << YAML::Value << params.triggerFilterSmoothing;
stream << YAML::Key << "InputRiseTime" << YAML::Value << params.inputRiseTime;
stream << YAML::Key << "TriggerThreshold" << YAML::Value << params.triggerThreshold;
stream << YAML::Key << "SamplesBaseline" << YAML::Value << params.samplesBaseLineMean;
stream << YAML::Key << "SamplesPeak" << YAML::Value << params.samplesPeakMean;
stream << YAML::Key << "PeakHoldoff" << YAML::Value << params.peakHoldOff;
stream << YAML::Key << "BaselineHoldoff" << YAML::Value << params.baseLineHoldOff;
stream << YAML::Key << "TriggerHoldoff" << YAML::Value << params.triggerHoldOff;
stream << YAML::Key << "RiseTimeWindow" << YAML::Value << params.riseTimeValidationWindow;
stream << YAML::Key << "RiseTimeDiscrimination" << YAML::Value << params.riseTimeDiscrimination;
stream << YAML::Key << "DigitalProbeGain" << YAML::Value << params.digitalProbeGain;
stream << YAML::Key << "EnergyNormFactor" << YAML::Value << params.energyNormalizationFactor;
stream << YAML::Key << "InputDecimation" << YAML::Value << params.inputDecimation;
stream << YAML::EndMap;
return stream;
}
Emitter& operator<<(Emitter& stream, const Daqromancy::PSDParameters& params)
{
stream << YAML::BeginMap;
stream << YAML::Key << "IsEnabled" << YAML::Value << params.isEnabled;
stream << YAML::Key << "PreTrigger" << YAML::Value << params.preTriggerTime;
stream << YAML::Key << "DCOffset" << YAML::Value << params.dcOffset;
stream << YAML::Key << "PulsePolarity" << YAML::Value << params.pulsePolarity;
stream << YAML::Key << "DynamicRange" << YAML::Value << params.dynamicRange;
stream << YAML::Key << "BaselineThreshold" << YAML::Value << params.baselineThreshold;
stream << YAML::Key << "TriggerHoldoff" << YAML::Value << params.triggerHoldOff;
stream << YAML::Key << "TriggerThreshold" << YAML::Value << params.triggerThreshold;
stream << YAML::Key << "SelfTrigger" << YAML::Value << params.selfTrigger;
stream << YAML::Key << "ChargeSensitivity" << YAML::Value << params.chargeSensitivity;
stream << YAML::Key << "ShortGate" << YAML::Value << params.shortGate;
stream << YAML::Key << "LongGate" << YAML::Value << params.longGate;
stream << YAML::Key << "PreGate" << YAML::Value << params.preGate;
stream << YAML::Key << "TriggerWindow" << YAML::Value << params.triggerValidationWindow;
stream << YAML::Key << "BaselineSamples" << YAML::Value << params.samplesBasline;
stream << YAML::Key << "DiscriminatorType" << YAML::Value << params.discrminatorType;
stream << YAML::Key << "CFDFraction" << YAML::Value << params.cfdFraction;
stream << YAML::Key << "CFDDelay" << YAML::Value << params.cfdDelay;
stream << YAML::Key << "TriggerConfig" << YAML::Value << params.triggerConfig;
stream << YAML::Key << "PileUpRejection" << YAML::Value << params.pileUpRejection;
stream << YAML::Key << "PURGap" << YAML::Value << params.purgap;
stream << YAML::EndMap;
return stream;
}
Emitter& operator<<(Emitter& stream, const std::vector<Daqromancy::PHAParameters>& channels)
{
stream << YAML::BeginSeq;
for (const auto& params : channels)
stream << params;
stream << YAML::EndSeq;
return stream;
}
Emitter& operator<<(Emitter& stream, const std::vector<Daqromancy::PSDParameters>& channels)
{
stream << YAML::BeginSeq;
for (const auto& params : channels)
stream << params;
stream << YAML::EndSeq;
return stream;
}
Emitter& operator<<(Emitter& stream, const Daqromancy::PHAWaveParameters& waves)
{
stream << YAML::BeginMap;
stream << YAML::Key << "IsDual" << YAML::Value << waves.isDual;
stream << YAML::Key << "AnalogProbe1" << YAML::Value << waves.analogProbe1;
stream << YAML::Key << "AnalogProbe2" << YAML::Value << waves.analogProbe1;
stream << YAML::Key << "DigitalProbe1" << YAML::Value << waves.digitalProbe1;
stream << YAML::EndMap;
return stream;
}
Emitter& operator<<(Emitter& stream, const Daqromancy::PSDWaveParameters& waves)
{
stream << YAML::BeginMap;
stream << YAML::Key << "IsDual" << YAML::Value << waves.isDual;
stream << YAML::Key << "AnalogProbe1" << YAML::Value << waves.analogProbe1;
stream << YAML::Key << "AnalogProbe2" << YAML::Value << waves.analogProbe1;
stream << YAML::Key << "DigitalProbe1" << YAML::Value << waves.digitalProbe1;
stream << YAML::Key << "DigitalProbe2" << YAML::Value << waves.digitalProbe2;
stream << YAML::EndMap;
return stream;
}
}
namespace Daqromancy {
ProjectSerializer::ProjectSerializer(const std::filesystem::path& filepath) :
m_filepath(filepath)
{
}
ProjectSerializer::~ProjectSerializer() {}
void ProjectSerializer::SerializeData(const DYProject::Ref& project)
{
std::ofstream output(m_filepath);
if (!output.is_open())
{
DY_ERROR("Unable to open {0} to seralize project data!", m_filepath);
return;
}
YAML::Emitter yamlStream;
yamlStream << YAML::BeginMap;
yamlStream << YAML::Key << "ProjectPath" << YAML::Value << project->GetProjectPath().string();
yamlStream << YAML::Key << "RunNumber" << YAML::Value << project->GetRunNumber();
yamlStream << YAML::Key << "DPPAcqMode" << YAML::Value << project->GetDPPAcqMode();
std::vector<DigitizerArgs> argList = project->GetDigitizerArgsList();
yamlStream << YAML::Key << "Digitizers" << YAML::Value << YAML::BeginSeq;
for (auto& args : argList)
{
yamlStream << YAML::BeginMap;
yamlStream << YAML::Key << "Digitizer" << YAML::Value << args.name;
yamlStream << YAML::Key << "DigitizerArgs" << YAML::Value << args;
yamlStream << YAML::Key << "DigitizerParameters" << YAML::Value << project->GetDigitizerParameters(args.handle);
switch (args.firmware)
{
case CAEN_DGTZ_DPPFirmware_PHA:
{
yamlStream << YAML::Key << "PHAChannels" << YAML::Value << project->GetPHAParameters(args.handle);
yamlStream << YAML::Key << "PHAWaves" << YAML::Value << project->GetPHAWaveParameters(args.handle);
break;
}
case CAEN_DGTZ_DPPFirmware_PSD:
{
yamlStream << YAML::Key << "PSDChannels" << YAML::Value << project->GetPSDParameters(args.handle);
yamlStream << YAML::Key << "PSDWaves" << YAML::Value << project->GetPSDWaveParameters(args.handle);
break;
}
case CAEN_DGTZ_DPPFirmwareNotSupported:
{
break;
}
}
yamlStream << YAML::EndMap;
}
yamlStream << YAML::EndSeq << YAML::EndMap;
output << yamlStream.c_str();
output.close();
}
void ProjectSerializer::DeserializeData(const DYProject::Ref& project)
{
YAML::Node data;
try
{
data = YAML::LoadFile(m_filepath.string());
}
catch (YAML::ParserException& e)
{
DY_ERROR("Unable to load in project settings from file {0}", m_filepath);
return;
}
project->SetProjectPath(data["ProjectPath"].as<std::string>());
project->SetRunNumber(data["RunNumber"].as<uint32_t>());
project->SetDPPAcqMode((DPPAcqMode)data["DPPAcqMode"].as<int>());
YAML::Node digitizers = data["Digitizers"];
//Init some memory for digitizer data
DigitizerArgs args;
DigitizerParameters params;
std::vector<PHAParameters> phaChannels;
std::vector<PSDParameters> psdChannels;
PHAWaveParameters phaWaves;
PSDWaveParameters psdWaves;
if (digitizers)
{
for (auto dgtz : digitizers)
{
args = dgtz["DigitizerArgs"].as<DigitizerArgs>();
args.name = dgtz["Digitizer"].as<std::string>();
params = dgtz["DigitizerParameters"].as<DigitizerParameters>();
switch (args.firmware)
{
case CAEN_DGTZ_DPPFirmware_PHA:
{
phaChannels = dgtz["PHAChannels"].as<std::vector<PHAParameters>>();
phaWaves = dgtz["PHAWaves"].as<PHAWaveParameters>();
if (args != project->GetDigitizerArgs(args.handle))
{
DY_ERROR("When attempting to deserialize project settings, board mismatch detected at handle {0}! Stopping load, Daqromancy should be restarted.", args.handle);
return;
}
project->SetDigitizerParameters(args.handle, params);
project->SetPHAParameters(args.handle, phaChannels);
project->SetPHAWaveParameters(args.handle, phaWaves);
break;
}
case CAEN_DGTZ_DPPFirmware_PSD:
{
psdChannels = dgtz["PSDChannels"].as<std::vector<PSDParameters>>();
psdWaves = dgtz["PSDWaves"].as<PSDWaveParameters>();
if (args != project->GetDigitizerArgs(args.handle))
{
DY_ERROR("When attempting to deserialize project settings, board mismatch detected at handle {0}! Stopping load, Daqromancy should be restarted.", args.handle);
return;
}
project->SetDigitizerParameters(args.handle, params);
project->SetPSDParameters(args.handle, psdChannels);
project->SetPSDWaveParameters(args.handle, psdWaves);
break;
}
case CAEN_DGTZ_DPPFirmwareNotSupported:
{
DY_WARN("Invalid firmware detected, this better just be a debug test");
if (args != project->GetDigitizerArgs(args.handle))
{
DY_ERROR("When attempting to deserialize project settings, board mismatch detected at handle {0}! Stopping load, Daqromancy should be restarted.", args.handle);
return;
}
project->SetDigitizerParameters(args.handle, params);
}
}
}
}
}
}

View File

@ -0,0 +1,25 @@
#ifndef PROJECT_SERIALIZER_H
#define PROJECT_SERIALIZER_H
#include "DYProject.h"
#include <filesystem>
namespace Daqromancy {
class ProjectSerializer
{
public:
ProjectSerializer(const std::filesystem::path& path);
~ProjectSerializer();
void SerializeData(const DYProject::Ref& project);
void DeserializeData(const DYProject::Ref& project);
private:
std::filesystem::path m_filepath;
};
}
#endif

View File

@ -0,0 +1,53 @@
#include "ScalarDistributor.h"
namespace Daqromancy {
std::unordered_map<std::string, Scalar::Ref> ScalarDistributor::s_scalarMap;
const Scalar::Ref& ScalarDistributor::Bind(const std::string& name)
{
auto iter = s_scalarMap.find(name);
if (iter != s_scalarMap.end())
return iter->second;
else
{
s_scalarMap[name] = std::make_shared<Scalar>(name);
return s_scalarMap[name];
}
}
void ScalarDistributor::UnBind(const std::string& name)
{
if(s_scalarMap.contains(name))
s_scalarMap.erase(name);
}
void ScalarDistributor::CalculateRates(double timestep)
{
for(auto& scalar : s_scalarMap)
{
uint64_t changeInCounts = scalar.second->value - scalar.second->lastCheckedValue;
scalar.second->lastCheckedValue += changeInCounts;
scalar.second->rate = changeInCounts / timestep;
}
}
float ScalarDistributor::GetRate(const std::string& name)
{
auto iter = s_scalarMap.find(name);
if (iter != s_scalarMap.end())
return iter->second->rate;
else
return 0.0f;
}
void ScalarDistributor::ClearScalars()
{
for (auto& scalar : s_scalarMap)
{
scalar.second->value = 0.0f;
scalar.second->lastCheckedValue = 0.0f;
}
}
}

View File

@ -0,0 +1,43 @@
#ifndef SCALAR_DISTRIBUTOR_H
#define SCALAR_DISTRIBUTOR_H
#include <atomic>
namespace Daqromancy {
//Scalars can be incremented thread-safe, lock-free using atomics
struct Scalar
{
using Ref = std::shared_ptr<Scalar>;
Scalar(const std::string& n) :
value(0), lastCheckedValue(0), name(n)
{
}
std::atomic<uint64_t> value;
std::atomic<uint64_t> lastCheckedValue; //Value from the last time GetRate was called
std::atomic<float> rate;
std::string name;
};
class ScalarDistributor
{
public:
//Bind/UnBind is NOT thread-safe. Should not be used in a multi-threaded context.
static const Scalar::Ref& Bind(const std::string& name);
static void UnBind(const std::string& name);
//CalculateRates/GetRate is implicitly thread safe as it does not modify the map itself
static void CalculateRates(double timestep);
static float GetRate(const std::string& name); //Returns in Hz; timestep should be in seconds
//ClearScalars is implicitly thread safe as it does not modify the map itself
static void ClearScalars();
private:
static std::unordered_map<std::string, Scalar::Ref> s_scalarMap;
};
}
#endif

127
src/Core/ThreadSafeQueue.h Normal file
View File

@ -0,0 +1,127 @@
#ifndef THREAD_SAFE_QUEUE_H
#define THREAD_SAFE_QUEUE_H
#include <deque>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <atomic>
namespace Daqromancy {
template<typename T>
class ThreadSafeQueue
{
public:
ThreadSafeQueue() = default;
ThreadSafeQueue(const ThreadSafeQueue&) = delete; //no copy
~ThreadSafeQueue() { Clear(); }
void PushBack(const T& data)
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
m_queue.push_back(data);
std::scoped_lock<std::mutex> condGuard(m_conditionMutex);
m_conditional.notify_one();
}
void PushFront(const T& data)
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
m_queue.push_front(data);
std::scoped_lock<std::mutex> guard(m_conditionMutex);
m_conditional.notify_one();
}
void PopBack()
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
m_queue.pop_back();
}
void PopFront()
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
m_queue.pop_front();
}
const T& Front()
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
return m_queue.front();
}
const T& Back()
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
return m_queue.back();
}
std::size_t Size()
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
return m_queue.size();
}
bool IsEmpty()
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
return m_queue.empty();
}
void Clear()
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
m_queue.clear();
}
//For iterator loops, need begin()/end() idiom
std::deque<T>::iterator begin()
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
return m_queue.begin();
}
std::deque<T>::iterator end()
{
std::scoped_lock<std::mutex> guard(m_queueMutex);
return m_queue.end();
}
void Wait()
{
while(IsEmpty() && !m_isForceWakeup)
{
std::unique_lock<std::mutex> guard(m_conditionMutex);
m_conditional.wait(guard);
}
}
void ForceWakeup()
{
m_isForceWakeup = true;
std::unique_lock<std::mutex> guard(m_conditionMutex);
m_conditional.notify_one();
}
void ResetWakeup()
{
m_isForceWakeup = false;
}
private:
std::mutex m_queueMutex;
std::deque<T> m_queue;
std::mutex m_conditionMutex;
std::condition_variable m_conditional;
std::atomic<bool> m_isForceWakeup = false;
};
}
#endif

45
src/Core/Window.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef WINDOW_H
#define WINDOW_H
#include "DYCore.h"
#include "Events/Event.h"
namespace Daqromancy {
struct WindowProperties
{
WindowProperties(const std::string& t = "Daqromancy", uint32_t w=1280, uint32_t h=720) :
title(t), width(w), height(h)
{
}
std::string title;
uint32_t width;
uint32_t height;
};
class Window
{
public:
using EventCallbackFunc = std::function<void(Event&)>;
virtual ~Window() {}
virtual void OnUpdate() = 0;
virtual uint32_t GetWidth() = 0;
virtual uint32_t GetHeight() = 0;
virtual std::string GetName() = 0;
virtual void SetEventCallback(EventCallbackFunc function) = 0;
virtual void SetVSync(bool enabled) = 0;
virtual bool IsVSync() = 0;
virtual void* GetNativeWindow() const = 0; //dirty
static Window* Create(const WindowProperties& props = WindowProperties());
};
}
#endif

19
src/Core/main.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "Application.h"
int main(int argc, char** argv)
{
Daqromancy::Logger::Init();
DY_INFO("Welcome to Daqromancy.");
Daqromancy::ApplicationArgs args;
args.name = "Daqromancy";
args.runtimePath = "";
args.argc = argc;
args.argv = argv;
Daqromancy::Application* app = Daqromancy::CreateApplication(args);
app->Run();
delete app;
}

View File

@ -0,0 +1,250 @@
#include "AcquisitionLayer.h"
#include "Core/DYCore.h"
#include "Core/DataDistributor.h"
namespace Daqromancy {
static bool SortByHandle(const Digitizer::Ref& digi1, const Digitizer::Ref& digi2)
{
return digi1->GetDigitizerArgs().handle < digi2->GetDigitizerArgs().handle;
}
AcquisitionLayer::AcquisitionLayer(const DYProject::Ref& project) :
m_project(project), m_acqThread(nullptr), m_running(false), m_server(52324)
{
m_server.PowerOn();
}
AcquisitionLayer::~AcquisitionLayer()
{
DY_INFO("Shutting down acq");
if (m_running)
{
DestroyAcqThread();
}
DY_INFO("Finished");
m_server.Shutdown();
}
void AcquisitionLayer::OnUpdate()
{
}
void AcquisitionLayer::OnEvent(Event& e)
{
EventDispatcher dispatch(e);
dispatch.Dispatch<AcqStartEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqStartEvent));
dispatch.Dispatch<AcqStopEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqStopEvent));
dispatch.Dispatch<AcqParametersEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqParametersEvent));
dispatch.Dispatch<AcqSyncArgsEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqSyncArgsEvent));
dispatch.Dispatch<AcqDPPModeEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqDPPModeEvent));
dispatch.Dispatch<AcqDetectBoardsEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqDetectBoardsEvent));
dispatch.Dispatch<AcqDisconnectBoardsEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqDisconnectBoardsEvent));
}
void AcquisitionLayer::DestroyAcqThread()
{
m_running = false;
if (m_acqThread != nullptr && m_acqThread->joinable())
{
DY_INFO("Destroying acquisition thread...");
m_acqThread->join();
delete m_acqThread;
m_acqThread = nullptr;
DY_INFO("Acquisition thread is destroyed.");
}
else if(m_acqThread != nullptr)
DY_WARN("Unable to destroy acquisition thread, but is active");
}
bool AcquisitionLayer::OnAcqStartEvent(AcqStartEvent& e)
{
if (m_running)
{
DY_WARN("Attempted to start a new acquisition while one is already running!");
return true;
}
else if (m_digitizerChain.empty())
{
DY_WARN("Cannot start acquisition without any digitizers!");
return true;
}
else if (m_acqThread != nullptr)
{
DestroyAcqThread();
}
DY_INFO("Starting acquisition thread...");
m_running = true;
m_acqThread = new std::thread(&AcquisitionLayer::Run, std::ref(*this));
DY_INFO("Running.");
//If we chose to write to disk, start the file handler
if (e.IsWriteToDisk())
m_fileIO.StartRun(m_project);
//If we chose to write to server start the server feed
if (e.IsWriteToServer())
m_server.StartDataFeed();
return true;
}
bool AcquisitionLayer::OnAcqStopEvent(AcqStopEvent& e)
{
DestroyAcqThread();
if (m_fileIO.IsRunning())
m_fileIO.StopRun();
if (m_server.IsFeeding())
m_server.StopDataFeed();
return true;
}
bool AcquisitionLayer::OnAcqParametersEvent(AcqParametersEvent& e)
{
if(m_running)
{
DY_WARN("Cannot change digitizer parameters while aquisition is running!");
return true;
}
if (e.GetAccessType() == DigitizerAccessType::All)
{
for (Digitizer::Ref& digitizer : m_digitizerChain)
{
const DigitizerArgs& args = digitizer->GetDigitizerArgs();
if (args.firmware == CAEN_DGTZ_DPPFirmware_PHA)
{
digitizer->SetDigitizerParameters(m_project->GetDigitizerParameters(args.handle));
digitizer->SetChannelParameters(m_project->GetPHAParameters(args.handle));
digitizer->SetWaveformParameters(m_project->GetPHAWaveParameters(args.handle));
digitizer->LoadSettings();
}
else if (digitizer->GetDigitizerArgs().firmware == CAEN_DGTZ_DPPFirmware_PSD)
{
digitizer->SetDigitizerParameters(m_project->GetDigitizerParameters(args.handle));
digitizer->SetChannelParameters(m_project->GetPSDParameters(args.handle));
digitizer->SetWaveformParameters(m_project->GetPSDWaveParameters(args.handle));
digitizer->LoadSettings();
}
}
}
else if(e.GetAccessType() == DigitizerAccessType::Single)
{
Digitizer::Ref& digitizer = m_digitizerChain[e.GetBoardHandle()];
const DigitizerArgs& args = digitizer->GetDigitizerArgs();
if (args.firmware == CAEN_DGTZ_DPPFirmware_PHA)
{
digitizer->SetDigitizerParameters(m_project->GetDigitizerParameters(args.handle));
digitizer->SetChannelParameters(m_project->GetPHAParameters(args.handle));
digitizer->SetWaveformParameters(m_project->GetPHAWaveParameters(args.handle));
digitizer->LoadSettings();
}
else if (digitizer->GetDigitizerArgs().firmware == CAEN_DGTZ_DPPFirmware_PSD)
{
digitizer->SetDigitizerParameters(m_project->GetDigitizerParameters(args.handle));
digitizer->SetChannelParameters(m_project->GetPSDParameters(args.handle));
digitizer->SetWaveformParameters(m_project->GetPSDWaveParameters(args.handle));
digitizer->LoadSettings();
}
}
//Setting parameters can sometimes lead to CAEN modifying values (invalid value, etc), so inform the project of changes
m_project->SetDigitizerData(m_digitizerChain);
return true;
}
bool AcquisitionLayer::OnAcqSyncArgsEvent(AcqSyncArgsEvent& e)
{
if (m_running)
{
DY_WARN("Cannot update synchronization settings while acquisition is running!");
return true;
}
m_digitizerChain.SynchronizeBoards(e.GetArgs());
return true;
}
bool AcquisitionLayer::OnAcqDPPModeEvent(AcqDPPModeEvent& e)
{
if (m_running)
{
DY_WARN("Cannot update DPP Acquisition settings while aquisitiion is running!");
return true;
}
m_digitizerChain.SetDPPAcqMode(m_project->GetDPPAcqMode());
m_project->SetDigitizerData(m_digitizerChain);
return true;
}
bool AcquisitionLayer::OnAcqDetectBoardsEvent(AcqDetectBoardsEvent& e)
{
DY_INFO("Querying the system for digitizers. WARNING: Daqromancy currently only supports OpticalLink connections");
m_digitizerChain.DetectBoards();
//Tell the project what happened
m_project->SetDigitizerData(m_digitizerChain);
return true;
}
bool AcquisitionLayer::OnAcqDisconnectBoardsEvent(AcqDisconnectBoardsEvent& e)
{
if (m_running)
{
DY_WARN("Cannot disconnect digitizers while the acquisition is running!");
return true;
}
DY_INFO("Disconnecting digitizers...");
m_digitizerChain.DisconnectBoards();
DY_INFO("Digitizers disconnected.");
//Tell the project what happened
m_project->SetDigitizerData(m_digitizerChain);
return true;
}
std::vector<DigitizerArgs> AcquisitionLayer::GetArgList()
{
std::vector<DigitizerArgs> list;
list.reserve(m_digitizerChain.size());
for (auto& digitizer : m_digitizerChain)
list.push_back(digitizer->GetDigitizerArgs());
return list;
}
void AcquisitionLayer::Run()
{
if (!m_digitizerChain.Start())
return;
std::vector<DYData> recievedData; //local data buffer
//Run aquisition loop
while (m_running)
{
for (auto& digitizer : m_digitizerChain)
digitizer->ReadData(recievedData);
if (recievedData.empty())
continue;
DataDistributor::PushData(recievedData);
recievedData.clear();
}
m_digitizerChain.Stop();
}
}

View File

@ -0,0 +1,68 @@
#ifndef ACQUISITION_LAYER_H
#define ACQUISITION_LAYER_H
#include "Core/Layer.h"
#include "Core/DYProject.h"
#include "Events/AcqEvent.h"
#include "Digitizer.h"
#include "DigitizerChain.h"
#include "DYio/DYRun.h"
#include "DYio/TCPServer.h"
#include <thread>
#include <mutex>
#include <atomic>
namespace Daqromancy {
class AcquisitionLayer : public Layer
{
public:
using EventCallbackFunc = std::function<void(Event&)>;
AcquisitionLayer(const DYProject::Ref& project);
~AcquisitionLayer();
void SetEventCallback(const EventCallbackFunc& func) { m_callbackFunction = func; }
virtual void OnUpdate() override;
virtual void OnEvent(Event& e) override;
bool IsRunning() { return m_running; }
private:
//Event handlers
bool OnAcqStartEvent(AcqStartEvent& e);
bool OnAcqStopEvent(AcqStopEvent& e);
bool OnAcqDetectBoardsEvent(AcqDetectBoardsEvent& e);
bool OnAcqParametersEvent(AcqParametersEvent& e);
bool OnAcqSyncArgsEvent(AcqSyncArgsEvent& e);
bool OnAcqDisconnectBoardsEvent(AcqDisconnectBoardsEvent& e);
bool OnAcqDPPModeEvent(AcqDPPModeEvent& e);
//Functionality
void DestroyAcqThread();
std::vector<DigitizerArgs> GetArgList();
//Acquistion loop
void Run();
EventCallbackFunc m_callbackFunction;
DYProject::Ref m_project;
//IO
DYRun m_fileIO;
TCPServer m_server;
DigitizerChain m_digitizerChain;
SyncArgs m_syncStatus;
std::thread* m_acqThread;
std::mutex m_acqMutex;
std::atomic<bool> m_running;
};
}
#endif

620
src/DAQ/Digitizer.cpp Normal file
View File

@ -0,0 +1,620 @@
#include "Digitizer.h"
#include "CAENDigitizer.h"
//Mask a specific bit
#define BIT(x) (1<<x)
namespace Daqromancy {
/////////////////////// Open Function ///////////////////////
Digitizer::Ref OpenDigitizer(DigitizerArgs& args)
{
int code = CAEN_DGTZ_OpenDigitizer(args.type, args.linkNumber, args.conetNode, args.vmeAddress, &args.handle);
if(code != CAEN_DGTZ_ErrorCode::CAEN_DGTZ_Success)
{
//report error
return nullptr;
}
CAEN_DGTZ_BoardInfo_t info;
CAEN_DGTZ_DPPFirmware_t firmware;
code |= CAEN_DGTZ_GetInfo(args.handle, &info);
code |= CAEN_DGTZ_GetDPPFirmwareType(args.handle, &firmware);
if(code != CAEN_DGTZ_ErrorCode::CAEN_DGTZ_Success)
{
//report errors
return nullptr;
}
switch(firmware)
{
case CAEN_DGTZ_DPPFirmware_PHA: return std::make_shared<DigitizerPHA>(args, info, code);
case CAEN_DGTZ_DPPFirmware_PSD: return std::make_shared<DigitizerPSD>(args, info, code);
case CAEN_DGTZ_DPPFirmware_DAW: return nullptr;
case CAEN_DGTZ_DPPFirmware_CI: return nullptr;
case CAEN_DGTZ_DPPFirmware_ZLE: return nullptr;
case CAEN_DGTZ_DPPFirmware_QDC: return nullptr;
}
return nullptr;
}
/////////////////////// Open Function ///////////////////////
/////////////////////// DigitizerPHA ///////////////////////
DigitizerPHA::DigitizerPHA(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec) :
Digitizer(), m_eventData(nullptr), m_waveData(nullptr)
{
Init(args, info, ec);
}
DigitizerPHA::~DigitizerPHA() { Close(); }
void DigitizerPHA::Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec)
{
m_args = args;
m_internalData = info;
m_args.status = ec;
if(info.Model == 1730)
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1730;
else if(info.Model == 1725)
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1725;
else if(info.Model == 1740)
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1740;
else
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_DT5720;
m_args.name = info.ModelName + std::to_string(info.SerialNumber);
m_args.firmware = CAEN_DGTZ_DPPFirmware_PHA;
m_args.channels = info.Channels;
m_channelParams.resize(info.Channels);
m_eventCountsPerChannel = new uint32_t[info.Channels];
m_eventData = new CAEN_DGTZ_DPP_PHA_Event_t*[info.Channels];
m_waveData = new CAEN_DGTZ_DPP_PHA_Waveforms_t*[info.Channels];
for (int i = 0; i < info.Channels; i++)
{
m_scalars.push_back(ScalarDistributor::Bind(m_args.name + std::to_string(i)));
}
m_samplingTime = GetSamplingPeriod(m_args.model);
LoadDigitizerParameters();
LoadChannelParameters();
//Must load default parameters here to generate a buffer
AllocateMemory(); //More specifically: CAEN memory
m_isConnected = true;
}
void DigitizerPHA::Close()
{
StopAquisition(); //Stop aquisition if needed
DeallocateMemory();
delete[] m_eventCountsPerChannel;
m_lowBuffer = nullptr;
m_eventData = nullptr;
m_eventCountsPerChannel = nullptr;
m_waveData = nullptr;
m_isConnected = false;
//Explicitly wipe-out scalars
for (auto& scalar : m_scalars)
{
ScalarDistributor::UnBind(scalar->name);
}
m_scalars.clear();
}
void DigitizerPHA::SetDigitizerParameters(const DigitizerParameters& params)
{
if(!m_isConnected || m_isActive)
return;
m_digitizerParams = params;
}
void DigitizerPHA::InternalSetChannelParameters(const std::vector<PHAParameters>& params)
{
if(!m_isConnected || m_isActive)
return;
m_channelParams = params;
}
void DigitizerPHA::InternalSetWaveformParameters(const PHAWaveParameters& params)
{
if(!m_isConnected || m_isActive)
return;
m_waveParams = params;
}
//This cannot possibly be the correct method for cases where digitizers are chained.
//July 2022: Is not. See Synchronize.h/.cpp
void DigitizerPHA::StartAquisition()
{
if(!m_isActive && m_isConnected)
{
m_args.status |= CAEN_DGTZ_SWStartAcquisition(m_args.handle);
m_isActive = true;
}
}
//This cannot possibly be the correct method for cases where digitizers are chained.
//July 2022: Is not. See Synchronize.h/.cpp
void DigitizerPHA::StopAquisition()
{
if(m_isActive && m_isConnected)
{
m_args.status |= CAEN_DGTZ_SWStopAcquisition(m_args.handle);
m_isActive = false;
}
}
void DigitizerPHA::LoadSettings()
{
DeallocateMemory();
LoadDigitizerParameters();
LoadChannelParameters();
LoadWaveformParameters();
AllocateMemory();
}
//Set digitizer wide parameters
void DigitizerPHA::LoadDigitizerParameters()
{
m_args.status |= CAEN_DGTZ_SetAcquisitionMode(m_args.handle, m_digitizerParams.acqMode);
m_args.status |= CAEN_DGTZ_SetDPPAcquisitionMode(m_args.handle, m_digitizerParams.dppAcqMode, m_digitizerParams.dppSaveMode);
m_args.status |= CAEN_DGTZ_SetRecordLength(m_args.handle, m_digitizerParams.recordLength / m_samplingTime);
m_args.status |= CAEN_DGTZ_SetIOLevel(m_args.handle, m_digitizerParams.IOlevel);
m_args.status |= CAEN_DGTZ_SetExtTriggerInputMode(m_args.handle, m_digitizerParams.triggerMode);
m_args.status |= CAEN_DGTZ_SetDPPEventAggregation(m_args.handle, m_digitizerParams.eventAggr, 0);
m_args.status |= CAEN_DGTZ_SetRunSynchronizationMode(m_args.handle, m_digitizerParams.syncMode);
m_args.status |= CAEN_DGTZ_SetChannelEnableMask(m_args.handle, m_digitizerParams.channelMask);
}
//Set per channel data
void DigitizerPHA::LoadChannelParameters()
{
m_digitizerParams.channelMask = 0;
for(std::size_t i=0; i<m_channelParams.size(); i++)
{
m_args.status |= CAEN_DGTZ_SetChannelDCOffset(m_args.handle, i, uint32_t(0xffff * m_channelParams[i].dcOffset)); //Max range is 0xffff
m_args.status |= CAEN_DGTZ_SetDPPPreTriggerSize(m_args.handle, i, m_channelParams[i].preTriggerTime / m_samplingTime);
m_args.status |= CAEN_DGTZ_SetChannelPulsePolarity(m_args.handle, i, m_channelParams[i].pulsePolarity);
m_args.status |= CAEN_DGTZ_WriteRegister(m_args.handle, 0x1028 + (i<<8), m_channelParams[i].dynamicRange);
if(m_channelParams[i].isEnabled)
{
m_digitizerParams.channelMask |= (BIT(i)); //flip channel bit to 1 scince its enabled
}
//Write data to garbage CAEN style structs
m_caenParams.M[i] = m_channelParams[i].decayTimeConst / m_samplingTime;
m_caenParams.m[i] = m_channelParams[i].trapFlatTop / m_samplingTime;
m_caenParams.k[i] = m_channelParams[i].trapRiseTime / m_samplingTime;
m_caenParams.ftd[i] = m_channelParams[i].flatTopDelay / m_samplingTime;
m_caenParams.a[i] = m_channelParams[i].triggerFilterSmoothing;
m_caenParams.b[i] = m_channelParams[i].inputRiseTime / m_samplingTime;
m_caenParams.thr[i] = m_channelParams[i].triggerThreshold;
m_caenParams.nsbl[i] = m_channelParams[i].samplesBaseLineMean;
m_caenParams.nspk[i] = m_channelParams[i].samplesPeakMean;
m_caenParams.pkho[i] = m_channelParams[i].peakHoldOff / m_samplingTime;
m_caenParams.blho[i] = m_channelParams[i].baseLineHoldOff / m_samplingTime;
m_caenParams.trgho[i] = m_channelParams[i].triggerHoldOff / m_samplingTime;
m_caenParams.twwdt[i] = m_channelParams[i].riseTimeValidationWindow / m_samplingTime;
m_caenParams.trgwin[i] = m_channelParams[i].riseTimeDiscrimination / m_samplingTime;
m_caenParams.dgain[i] = m_channelParams[i].digitalProbeGain;
m_caenParams.enf[i] = m_channelParams[i].energyNormalizationFactor;
m_caenParams.decimation[i] = m_channelParams[i].inputDecimation;
//So far as I can tell these are not used
m_caenParams.enskim[i] = 0;
m_caenParams.eskimlld[i] = 0;
m_caenParams.eskimuld[i] = 0;
m_caenParams.blrclip[i] = 0;
m_caenParams.dcomp[i] = 0;
m_caenParams.trapbsl[i] = 0;
m_caenParams.otrej[i] = 0;
}
/*
Something not immediately clear... there is no GetDPPParameters function
I assume that if a value is passed that is invalid (which we know can happen) the value that is used by
the digitizer is written to the data at the pointer passed to the SetDPPParameters function...
I proceed under this assumption
*/
m_args.status |= CAEN_DGTZ_SetDPPParameters(m_args.handle, m_digitizerParams.channelMask, &m_caenParams);
//Retrieve corrected value for the channel parameters
for (std::size_t i = 0; i < m_channelParams.size(); i++)
{
if (!m_channelParams[i].isEnabled)
{
continue;
}
m_channelParams[i].decayTimeConst = m_caenParams.M[i] * m_samplingTime;
m_channelParams[i].trapFlatTop = m_caenParams.m[i] * m_samplingTime;
m_channelParams[i].trapRiseTime = m_caenParams.k[i] * m_samplingTime;
m_channelParams[i].flatTopDelay = m_caenParams.ftd[i] * m_samplingTime;
m_channelParams[i].triggerFilterSmoothing = m_caenParams.a[i];
m_channelParams[i].inputRiseTime = m_caenParams.b[i] * m_samplingTime;
m_channelParams[i].triggerThreshold = m_caenParams.thr[i];
m_channelParams[i].samplesBaseLineMean = m_caenParams.nsbl[i];
m_channelParams[i].samplesPeakMean = m_caenParams.nspk[i];
m_channelParams[i].peakHoldOff = m_caenParams.pkho[i] * m_samplingTime;
m_channelParams[i].baseLineHoldOff = m_caenParams.blho[i] * m_samplingTime;
m_channelParams[i].triggerHoldOff = m_caenParams.trgho[i] * m_samplingTime;
m_channelParams[i].riseTimeValidationWindow = m_caenParams.twwdt[i] * m_samplingTime;
m_channelParams[i].riseTimeDiscrimination = m_caenParams.trgwin[i] * m_samplingTime;
m_channelParams[i].digitalProbeGain = m_caenParams.dgain[i];
m_channelParams[i].energyNormalizationFactor = m_caenParams.enf[i];
m_channelParams[i].inputDecimation = m_caenParams.decimation[i];
}
}
void DigitizerPHA::LoadWaveformParameters()
{
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, ANALOG_TRACE_1, m_waveParams.analogProbe1);
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, ANALOG_TRACE_2, m_waveParams.analogProbe2);
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, DIGITAL_TRACE_1, m_waveParams.digitalProbe1);
}
//Note: Even though function signatures of MallocDPPEvents and MallocDPPWaveforms are identical
//they behave quite differently. MallocDPPEvents allocates the entire channel-buffer matrix in one call,
//while MallocDPPWaveforms allocates only a single buffer for a single channel at a time.
//This is why void* are dangerous!
void DigitizerPHA::AllocateMemory()
{
m_args.status |= CAEN_DGTZ_MallocReadoutBuffer(m_args.handle, &m_lowBuffer, &m_lowBufferSize);
//void casts are soooo bad .... but required by CAEN API
m_args.status |= CAEN_DGTZ_MallocDPPEvents(m_args.handle, (void**)(m_eventData), &m_eventBufferSize);
for(int channel=0; channel<m_internalData.Channels; channel++)
m_args.status |= CAEN_DGTZ_MallocDPPWaveforms(m_args.handle, (void**)(&m_waveData[channel]), &m_waveBufferSize);
}
void DigitizerPHA::DeallocateMemory()
{
m_args.status |= CAEN_DGTZ_FreeReadoutBuffer(&m_lowBuffer);
m_args.status |= CAEN_DGTZ_FreeDPPEvents(m_args.handle, (void**)(m_eventData));
for(int i=0; i<m_internalData.Channels; i++)
m_args.status |= CAEN_DGTZ_FreeDPPWaveforms(m_args.handle, (void*)(m_waveData[i]));
}
void DigitizerPHA::ReadData(std::vector<DYData>& buffer)
{
if(!m_isActive || !m_isConnected)
return;
m_args.status |= CAEN_DGTZ_ReadData(m_args.handle, CAEN_DGTZ_SLAVE_TERMINATED_READOUT_MBLT, m_lowBuffer, &m_lowBufferSize);
if (m_lowBufferSize == 0)
{
return;
}
m_args.status |= CAEN_DGTZ_GetDPPEvents(m_args.handle, m_lowBuffer, m_lowBufferSize, (void**)(&m_eventData), m_eventCountsPerChannel);
size_t waveSize;
DYData tempData;
tempData.board = m_args.handle;
for(int i=0; i<m_internalData.Channels; i++)
{
tempData.channel = i;
//Increment scalars
m_scalars[i]->value += m_eventCountsPerChannel[i];
for(int j=0; j<m_eventCountsPerChannel[i]; j++)
{
tempData.energy = m_eventData[i][j].Energy;
tempData.timestamp = m_eventData[i][j].TimeTag;
tempData.flags = m_eventData[i][j].Extras;
if(m_digitizerParams.dppAcqMode != CAEN_DGTZ_DPP_ACQ_MODE_List)
{
CAEN_DGTZ_DecodeDPPWaveforms(m_args.handle, (void*)&(m_eventData[i][j]), m_waveData);
tempData.waveSize = m_waveData[i]->Ns;
waveSize = m_waveData[i]->Ns;
if(waveSize != 0)
{
//Copy the data to our vectors PHA supports 2 analog traces and 2 digital traces
tempData.trace1Samples.assign(m_waveData[i]->Trace1, m_waveData[i]->Trace1 + waveSize);
tempData.trace2Samples.assign(m_waveData[i]->Trace2, m_waveData[i]->Trace2 + waveSize); //This is all zero if in single analog trace mode
tempData.digitalTrace1Samples.assign(m_waveData[i]->DTrace1, m_waveData[i]->DTrace1 + waveSize);
tempData.digitalTrace2Samples.assign(m_waveData[i]->DTrace2, m_waveData[i]->DTrace2 + waveSize);
}
}
buffer.push_back(tempData);
}
}
}
/////////////////////// DigitizerPHA ///////////////////////
/////////////////////// DigitizerPSD ///////////////////////
DigitizerPSD::DigitizerPSD(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int code) :
Digitizer(), m_eventData(nullptr), m_waveData(nullptr)
{
Init(args, info, code);
}
DigitizerPSD::~DigitizerPSD() { Close(); }
void DigitizerPSD::Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec)
{
m_args = args;
m_internalData = info;
m_args.status = ec;
if(info.Model == 1730)
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1730;
else if(info.Model == 1725)
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1725;
else if(info.Model == 1740)
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1740;
else
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_DT5720;
m_args.name = info.ModelName + std::to_string(info.SerialNumber);
m_args.firmware = CAEN_DGTZ_DPPFirmware_PSD;
m_args.channels = info.Channels;
m_channelParams.resize(info.Channels);
m_eventCountsPerChannel = new uint32_t[info.Channels];
m_eventData = new CAEN_DGTZ_DPP_PSD_Event_t*[info.Channels];
m_waveData = new CAEN_DGTZ_DPP_PSD_Waveforms_t*[info.Channels];
LoadDigitizerParameters();
LoadChannelParameters();
//Must load default parameters here to generate a buffer
AllocateMemory(); //More specifically: CAEN memory
for (int i = 0; i < info.Channels; i++)
{
m_scalars.push_back(ScalarDistributor::Bind(m_args.name + std::to_string(i)));
}
m_samplingTime = GetSamplingPeriod(m_args.model);
m_isConnected = true;
}
void DigitizerPSD::Close()
{
StopAquisition(); //Stop aquisition if needed
DeallocateMemory();
delete[] m_eventCountsPerChannel;
m_lowBuffer = nullptr;
m_eventData = nullptr;
m_eventCountsPerChannel = nullptr;
m_waveData = nullptr;
m_isConnected = false;
//Explicitly wipe-out scalars
for (auto& scalar : m_scalars)
{
ScalarDistributor::UnBind(scalar->name);
}
m_scalars.clear();
}
void DigitizerPSD::SetDigitizerParameters(const DigitizerParameters& params)
{
if(!m_isConnected || m_isActive)
return;
m_digitizerParams = params;
}
void DigitizerPSD::InternalSetChannelParameters(const std::vector<PSDParameters>& params)
{
if(!m_isConnected || m_isActive)
return;
m_channelParams = params;
}
void DigitizerPSD::InternalSetWaveformParameters(const PSDWaveParameters& params)
{
if(!m_isConnected || m_isActive)
return;
m_waveParams = params;
}
//This cannot possibly be the correct method for cases where digitizers are chained.
//July 2022: Is not. See Synchronize.h/.cpp
void DigitizerPSD::StartAquisition()
{
if(!m_isActive && m_isConnected)
{
m_args.status |= CAEN_DGTZ_SWStartAcquisition(m_args.handle);
m_isActive = true;
}
}
//This cannot possibly be the correct method for cases where digitizers are chained.
//July 2022: Is not. See Synchronize.h/.cpp
void DigitizerPSD::StopAquisition()
{
if(m_isActive && m_isConnected)
{
m_args.status |= CAEN_DGTZ_SWStopAcquisition(m_args.handle);
m_isActive = false;
}
}
void DigitizerPSD::LoadSettings()
{
DeallocateMemory();
LoadDigitizerParameters();
LoadChannelParameters();
LoadWaveformParameters();
AllocateMemory();
}
//Set digitizer wide parameters
void DigitizerPSD::LoadDigitizerParameters()
{
m_args.status |= CAEN_DGTZ_SetAcquisitionMode(m_args.handle, m_digitizerParams.acqMode);
m_args.status |= CAEN_DGTZ_SetDPPAcquisitionMode(m_args.handle, m_digitizerParams.dppAcqMode, m_digitizerParams.dppSaveMode); //why would you ever not want one of these?
m_args.status |= CAEN_DGTZ_SetRecordLength(m_args.handle, m_digitizerParams.recordLength / m_samplingTime);
m_args.status |= CAEN_DGTZ_SetIOLevel(m_args.handle, m_digitizerParams.IOlevel);
m_args.status |= CAEN_DGTZ_SetExtTriggerInputMode(m_args.handle, m_digitizerParams.triggerMode);
m_args.status |= CAEN_DGTZ_SetDPPEventAggregation(m_args.handle, m_digitizerParams.eventAggr, 0);
m_args.status |= CAEN_DGTZ_SetRunSynchronizationMode(m_args.handle, m_digitizerParams.syncMode);
m_args.status |= CAEN_DGTZ_SetChannelEnableMask(m_args.handle, m_digitizerParams.channelMask);
}
//Set per channel data
void DigitizerPSD::LoadChannelParameters()
{
m_digitizerParams.channelMask = 0;
for(std::size_t i=0; i<m_channelParams.size(); i++)
{
m_args.status |= CAEN_DGTZ_SetChannelDCOffset(m_args.handle, i, uint32_t(0xffff * m_channelParams[i].dcOffset)); //Max range is 0xffff
m_args.status |= CAEN_DGTZ_SetDPPPreTriggerSize(m_args.handle, i, m_channelParams[i].preTriggerTime / m_samplingTime);
m_args.status |= CAEN_DGTZ_SetChannelPulsePolarity(m_args.handle, i, m_channelParams[i].pulsePolarity);
m_args.status |= CAEN_DGTZ_WriteRegister(m_args.handle, 0x1028 + (i<<8), m_channelParams[i].dynamicRange);
if(m_channelParams[i].isEnabled)
{
m_digitizerParams.channelMask |= (BIT(i)); //flip channel bit to 1 scince its enabled
}
//Write data to garbage CAEN style structs
m_caenParams.thr[i] = m_channelParams[i].triggerThreshold;
m_caenParams.selft[i] = m_channelParams[i].selfTrigger;
m_caenParams.csens[i] = m_channelParams[i].chargeSensitivity;
m_caenParams.sgate[i] = m_channelParams[i].shortGate / m_samplingTime;
m_caenParams.lgate[i] = m_channelParams[i].longGate / m_samplingTime;
m_caenParams.pgate[i] = m_channelParams[i].preGate / m_samplingTime;
m_caenParams.tvaw[i] = m_channelParams[i].triggerValidationWindow / m_samplingTime;
m_caenParams.nsbl[i] = m_channelParams[i].samplesBasline;
m_caenParams.discr[i] = m_channelParams[i].discrminatorType;
m_caenParams.cfdf[i] = m_channelParams[i].cfdFraction;
m_caenParams.cfdd[i] = m_channelParams[i].cfdDelay / m_samplingTime;
m_caenParams.trgc[i] = (CAEN_DGTZ_DPP_TriggerConfig_t)1; // Deprecated, must be set to one according to docs
}
//These are like global digitizer params but PSD specific. Here we treat first channel as "global" setting (similar to CoMPASS)
/*
Something not immediately clear... there is no GetDPPParameters function
I assume that if a value is passed that is invalid (which we know can happen) the value that is used by
the digitizer is written to the data at the pointer passed to the SetDPPParameters function...
I proceed under this assumption
*/
m_args.status |= CAEN_DGTZ_SetDPPParameters(m_args.handle, m_digitizerParams.channelMask, &m_caenParams);
//Retrieve corrected value for the channel parameters
for (std::size_t i = 0; i < m_channelParams.size(); i++)
{
if (!m_channelParams[i].isEnabled)
{
continue;
}
m_channelParams[i].triggerThreshold = m_caenParams.thr[i];
m_channelParams[i].selfTrigger = m_caenParams.selft[i];
m_channelParams[i].chargeSensitivity = m_caenParams.csens[i];
m_channelParams[i].shortGate = m_caenParams.sgate[i] * m_samplingTime;
m_channelParams[i].longGate = m_caenParams.lgate[i] * m_samplingTime;
m_channelParams[i].preGate = m_caenParams.pgate[i] * m_samplingTime;
m_channelParams[i].triggerValidationWindow = m_caenParams.tvaw[i] * m_samplingTime;
m_channelParams[i].samplesBasline = m_caenParams.nsbl[i];
m_channelParams[i].discrminatorType = m_caenParams.discr[i];
m_channelParams[i].cfdFraction = m_caenParams.cfdf[i];
m_channelParams[i].cfdDelay = m_caenParams.cfdd[i] * m_samplingTime;
}
m_channelParams[0].baselineThreshold = m_caenParams.blthr;
m_channelParams[0].bltmo = m_caenParams.bltmo;
m_channelParams[0].triggerHoldOff = m_caenParams.trgho;
m_channelParams[0].pileUpRejection = m_caenParams.purh;
m_channelParams[0].purgap = m_caenParams.purgap;
}
void DigitizerPSD::LoadWaveformParameters()
{
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, ANALOG_TRACE_1, m_waveParams.analogProbe1);
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, ANALOG_TRACE_2, m_waveParams.analogProbe2);
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, DIGITAL_TRACE_1, m_waveParams.digitalProbe1);
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, DIGITAL_TRACE_2, m_waveParams.digitalProbe2);
}
//Note: Even though function signatures of MallocDPPEvents and MallocDPPWaveforms are identical
//they behave quite differently. MallocDPPEvents allocates the entire channel-buffer matrix in one call,
//while MallocDPPWaveforms allocates only a single buffer for a single channel at a time.
//This is why void* are dangerous!
void DigitizerPSD::AllocateMemory()
{
m_args.status |= CAEN_DGTZ_MallocReadoutBuffer(m_args.handle, &m_lowBuffer, &m_lowBufferSize);
//void casts are soooo bad .... but required by CAEN API
m_args.status |= CAEN_DGTZ_MallocDPPEvents(m_args.handle, (void**)(m_eventData), &m_eventBufferSize);
for(int channel=0; channel<m_internalData.Channels; channel++)
m_args.status |= CAEN_DGTZ_MallocDPPWaveforms(m_args.handle, (void**)(&m_waveData[channel]), &m_waveBufferSize);
}
void DigitizerPSD::DeallocateMemory()
{
m_args.status |= CAEN_DGTZ_FreeReadoutBuffer(&m_lowBuffer);
m_args.status |= CAEN_DGTZ_FreeDPPEvents(m_args.handle, (void**)(m_eventData));
for(int i=0; i<m_internalData.Channels; i++)
m_args.status |= CAEN_DGTZ_FreeDPPWaveforms(m_args.handle, (void*)(m_waveData[i]));
}
void DigitizerPSD::ReadData(std::vector<DYData>& buffer)
{
if (!m_isActive || !m_isConnected)
return;
m_args.status |= CAEN_DGTZ_ReadData(m_args.handle, CAEN_DGTZ_SLAVE_TERMINATED_READOUT_MBLT, m_lowBuffer, &m_lowBufferSize);
if (m_lowBufferSize == 0)
{
return;
}
m_args.status |= CAEN_DGTZ_GetDPPEvents(m_args.handle, m_lowBuffer, m_lowBufferSize, (void**)(&m_eventData), m_eventCountsPerChannel);
size_t waveSize;
DYData tempData;
tempData.board = m_args.handle;
for(int i=0; i<m_internalData.Channels; i++)
{
tempData.channel = i;
//Increment scalars
m_scalars[i]->value += m_eventCountsPerChannel[i];
for(int j=0; j<m_eventCountsPerChannel[i]; j++)
{
tempData.energy = m_eventData[i][j].ChargeLong;
tempData.energyShort = m_eventData[i][j].ChargeShort;
tempData.timestamp = m_eventData[i][j].TimeTag;
tempData.flags = m_eventData[i][j].Extras;
if(m_digitizerParams.dppAcqMode != CAEN_DGTZ_DPP_ACQ_MODE_List)
{
CAEN_DGTZ_DecodeDPPWaveforms(m_args.handle, (void*)&(m_eventData[i][j]), m_waveData);
tempData.waveSize = m_waveData[i]->Ns;
waveSize = m_waveData[i]->Ns;
if(tempData.waveSize != 0)
{
//Copy the data to our vectors PHA supports 2 analog traces and 2 digital traces
tempData.trace1Samples.assign(m_waveData[i]->Trace1, m_waveData[i]->Trace1 + waveSize);
tempData.trace2Samples.assign(m_waveData[i]->Trace2, m_waveData[i]->Trace2 + waveSize); //This is all zero if in single analog trace mode
tempData.digitalTrace1Samples.assign(m_waveData[i]->DTrace1, m_waveData[i]->DTrace1 + waveSize);
tempData.digitalTrace2Samples.assign(m_waveData[i]->DTrace2, m_waveData[i]->DTrace2 + waveSize);
}
}
buffer.push_back(tempData);
}
}
}
}

231
src/DAQ/Digitizer.h Normal file
View File

@ -0,0 +1,231 @@
/*
Digitizer.h
Abstract base class for CAEN digitizer
GWM -- June 2022
*/
#ifndef DIGITIZER_H
#define DIGITIZER_H
#include "DigitizerDefs.h"
#include "Core/ScalarDistributor.h"
namespace Daqromancy {
//Abstract digitizer base class
class Digitizer
{
public:
using Ref = std::shared_ptr<Digitizer>;
Digitizer() :
m_isActive(false), m_isConnected(false), m_lowBuffer(NULL), m_lowBufferSize(0), m_eventBufferSize(0),
m_eventCountsPerChannel(nullptr), m_waveBufferSize(0)
{
}
virtual ~Digitizer() { delete[] m_lowBuffer; }
virtual void Close() = 0;
virtual void StartAquisition() = 0;
virtual void StopAquisition() = 0;
virtual void SetDigitizerParameters(const DigitizerParameters& params) = 0;
/*
These functions look scary but really aren't. We provide a public interface that accepts any type of argument, such that the user can have a digitizer
and call digitizer->SetChannelParamters(params)/SetWaveformParameters(params) for any valid parameter type without having to cast pointers around.
The handling of type cases is done by the InternalSetChannel/WaveformParamters functions, which are overloaded for each case. The default behavior of
each setter should be to print a warning telling the user that the parameters were invalid. Then each specific derived class overrides it's specific internal
setter and properly handles the parameters. Biggest advantage is that as new digitizer firmwares are added to the code base, the public interface basically
doesn't change.
This also has the advantage of implicit error handling. If the user attempts to write code for which no internal setter exists, the compiler will complain.
*/
template<typename T>
void SetChannelParameters(const T& params)
{
InternalSetChannelParameters(params);
}
template<typename T>
void SetWaveformParameters(const T& params)
{
InternalSetWaveformParameters(params);
}
/*
Similar to the setters, we have getters which allow for a simple access interface. The argument, paramBuffer, is the returned value of the state of the parameters.
*/
template<typename T>
void GetChannelParameters(T& paramBuffer) const
{
InternalGetChannelParameters(paramBuffer);
}
template<typename T>
void GetWaveformParameters(T& paramBuffer) const
{
InternalGetWaveformParameters(paramBuffer);
}
virtual void LoadSettings() = 0;
virtual void ReadData(std::vector<DYData>& buffer) = 0;
const DigitizerParameters& GetDigitizerParameters() const { return m_digitizerParams; }
const DigitizerArgs& GetDigitizerArgs() const { return m_args; }
const bool IsActive() const { return m_isActive; }
const bool IsConnected() const { return m_isConnected; }
protected:
virtual void Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec) = 0;
/*
Functions which actually set parameters. Should be an overload for each valid type. By default should always print an incompatibility warning,
and then be overloaded by the correct derived class to actually set parameters.
*/
virtual void InternalSetChannelParameters(const std::vector<PHAParameters>& params)
{
("Attempting to set PHA parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
}
virtual void InternalSetChannelParameters(const std::vector<PSDParameters>& params)
{
DY_WARN("Attempting to set PSD parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
}
virtual void InternalSetWaveformParameters(const PHAWaveParameters& params)
{
DY_WARN("Attempting to set PHA wave parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
}
virtual void InternalSetWaveformParameters(const PSDWaveParameters& params)
{
DY_WARN("Attempting to set PSD wave parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
}
virtual void InternalGetChannelParameters(std::vector<PHAParameters>& paramBuffer) const
{
DY_WARN("Attempting to get PHA parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
}
virtual void InternalGetChannelParameters(std::vector<PSDParameters>& paramBuffer) const
{
DY_WARN("Attempting to get PSD parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
}
virtual void InternalGetWaveformParameters(PHAWaveParameters& paramBuffer) const
{
DY_WARN("Attempting to get PHA wave parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
}
virtual void InternalGetWaveformParameters(PSDWaveParameters& paramBuffer) const
{
DY_WARN("Attempting to get PSD wave parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
}
DigitizerArgs m_args;
DigitizerParameters m_digitizerParams;
bool m_isActive;
bool m_isConnected;
char* m_lowBuffer;
uint32_t m_lowBufferSize;
uint32_t m_eventBufferSize;
uint32_t* m_eventCountsPerChannel;
uint32_t m_waveBufferSize;
std::vector<Scalar::Ref> m_scalars; //Count rates for each channel. Indexed by channel number
double m_samplingTime; //Use this to convert from our UI friendly time parameters to the digitizer-friendly sample based parameters
CAEN_DGTZ_BoardInfo_t m_internalData; //internal use only
};
//Creation function
Digitizer::Ref OpenDigitizer(DigitizerArgs& args);
//Digitizers are better sliced by Firmware than by model. A lot of boards support multpile firmwares.
class DigitizerPHA : public Digitizer
{
public:
DigitizerPHA(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec);
virtual ~DigitizerPHA();
virtual void Close() override;
virtual void StartAquisition() override;
virtual void StopAquisition() override;
virtual void LoadSettings() override;
virtual void SetDigitizerParameters(const DigitizerParameters& params) override;
virtual void ReadData(std::vector<DYData>& buffer) override;
private:
virtual void Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec) override;
virtual void InternalSetChannelParameters(const std::vector<PHAParameters>& params) override;
virtual void InternalSetWaveformParameters(const PHAWaveParameters& params) override;
virtual void InternalGetChannelParameters(std::vector<PHAParameters>& paramBuffer) const override { paramBuffer = m_channelParams; }
virtual void InternalGetWaveformParameters(PHAWaveParameters& paramBuffer) const override { paramBuffer = m_waveParams; }
void LoadDigitizerParameters();
void LoadChannelParameters();
void LoadWaveformParameters();
void AllocateMemory();
void DeallocateMemory();
//CAEN required data storage, does not interface to other parts of the program
CAEN_DGTZ_DPP_PHA_Event_t** m_eventData;
CAEN_DGTZ_DPP_PHA_Waveforms_t** m_waveData;
CAEN_DGTZ_DPP_PHA_Params_t m_caenParams;
std::vector<PHAParameters> m_channelParams;
PHAWaveParameters m_waveParams;
};
class DigitizerPSD : public Digitizer
{
public:
DigitizerPSD(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec);
virtual ~DigitizerPSD();
virtual void Close() override;
virtual void StartAquisition() override;
virtual void StopAquisition() override;
virtual void LoadSettings() override;
virtual void SetDigitizerParameters(const DigitizerParameters& params) override;
virtual void ReadData(std::vector<DYData>& buffer) override;
private:
virtual void Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec) override;
virtual void InternalSetChannelParameters(const std::vector<PSDParameters>& params) override;
virtual void InternalSetWaveformParameters(const PSDWaveParameters& params) override;
virtual void InternalGetChannelParameters(std::vector<PSDParameters>& paramBuffer) const override { paramBuffer = m_channelParams; }
virtual void InternalGetWaveformParameters(PSDWaveParameters& paramBuffer) const override { paramBuffer = m_waveParams; }
void LoadDigitizerParameters();
void LoadChannelParameters();
void LoadWaveformParameters();
void AllocateMemory();
void DeallocateMemory();
//CAEN required data storage, does not interface to other parts of the program
CAEN_DGTZ_DPP_PSD_Event_t** m_eventData;
CAEN_DGTZ_DPP_PSD_Waveforms_t** m_waveData;
CAEN_DGTZ_DPP_PSD_Params_t m_caenParams;
std::vector<PSDParameters> m_channelParams;
PSDWaveParameters m_waveParams;
};
}
#endif

210
src/DAQ/DigitizerChain.cpp Normal file
View File

@ -0,0 +1,210 @@
#include "DigitizerChain.h"
#include "CAENDigitizer.h"
namespace Daqromancy {
static bool SortByHandle(const Digitizer::Ref& digi1, const Digitizer::Ref& digi2)
{
return digi1->GetDigitizerArgs().handle < digi2->GetDigitizerArgs().handle;
}
DigitizerChain::DigitizerChain()
{
}
DigitizerChain::~DigitizerChain()
{
}
void DigitizerChain::DetectBoards()
{
m_chain.clear();
static int nLinks = 4;
static int nNodes = 8;
DigitizerArgs args;
for (int link = 0; link < nLinks; link++)
{
for (int node = 0; node < nNodes; node++)
{
args = DigitizerArgs();
args.conetNode = node;
args.linkNumber = link;
Digitizer::Ref digi = OpenDigitizer(args);
if (digi != nullptr)
{
args = digi->GetDigitizerArgs();
m_chain.push_back(digi);
DY_INFO("Found digitizer named {0} at link {1} on node {2}", args.name, args.linkNumber, args.conetNode);
}
}
}
if (size() == 0)
("No digitizers found... check to see that they are on and connected to the system via optical link");
else
std::sort(begin(), end(), SortByHandle); //in general seems to not be necessary, but just to be safe
}
void DigitizerChain::DisconnectBoards()
{
//Maybe someday more complicated stuff here?
clear();
}
void DigitizerChain::SetDPPAcqMode(DPPAcqMode mode)
{
CAEN_DGTZ_DPP_AcqMode_t modeIn;
CAEN_DGTZ_DPP_AcqMode_t modeOut;
CAEN_DGTZ_DPP_SaveParam_t saveIn = CAEN_DGTZ_DPP_SAVE_PARAM_EnergyAndTime;
CAEN_DGTZ_DPP_SaveParam_t saveOut;
switch (mode)
{
case DPPAcqMode::List: modeIn = CAEN_DGTZ_DPP_ACQ_MODE_List; break;
case DPPAcqMode::Waves: modeIn = CAEN_DGTZ_DPP_ACQ_MODE_Oscilloscope; break;
case DPPAcqMode::None: DY_WARN("Attempted to set DPPAcqMode::None!"); return;
}
int ec = 0;
for (auto& digitizer : m_chain)
{
ec |= CAEN_DGTZ_SetDPPAcquisitionMode(digitizer->GetDigitizerArgs().handle, modeIn, saveIn);
ec |= CAEN_DGTZ_GetDPPAcquisitionMode(digitizer->GetDigitizerArgs().handle, &modeOut, &saveOut);
DigitizerParameters params = digitizer->GetDigitizerParameters();
params.dppAcqMode = modeOut;
params.dppSaveMode = saveOut;
digitizer->SetDigitizerParameters(params);
}
if (ec != 0)
{
DY_ERROR("Error code {0} recieved from DigitizerChain::SetDPPAcqMode!", ec);
}
}
void DigitizerChain::SynchronizeBoards(const SyncArgs& args)
{
if (size() <= 1)
{
DY_WARN("Cannot synchronize digitizer chain of size {0}", size());
return;
}
CAEN_DGTZ_RunSyncMode_t value = CAEN_DGTZ_RUN_SYNC_Disabled;
CAEN_DGTZ_RunSyncMode_t setSyncValue;
CAEN_DGTZ_AcqMode_t setAcqMode;
switch (args.syncMethod)
{
case SyncMethod::SIn_TrigOut: value = CAEN_DGTZ_RUN_SYNC_TrgOutSinDaisyChain; break;
case SyncMethod::None: value = CAEN_DGTZ_RUN_SYNC_Disabled; break;
}
int ec = 0;
for (auto& digitizer : m_chain)
{
ec |= CAEN_DGTZ_SetRunSynchronizationMode(digitizer->GetDigitizerArgs().handle, value);
ec |= CAEN_DGTZ_GetRunSynchronizationMode(digitizer->GetDigitizerArgs().handle, &setSyncValue);
ec |= CAEN_DGTZ_GetAcquisitionMode(digitizer->GetDigitizerArgs().handle, &setAcqMode);
auto params = digitizer->GetDigitizerParameters();
params.syncMode = setSyncValue;
params.acqMode = setAcqMode;
digitizer->SetDigitizerParameters(params);
}
if (ec != 0)
{
DY_ERROR("Error code {0} recieved from DigitizerChain::SynchronizeBoards!", ec);
}
}
bool DigitizerChain::Start()
{
if (size() == 0)
{
DY_ERROR("Cannot run data aquisition without any digitizers!");
return false;
}
else if (size() == 1)
{
m_chain[0]->StartAquisition();
if (!m_chain[0]->IsActive())
{
DY_ERROR("Unable to start single digitizer config!");
return false;
}
}
else
return StartSynchronizedRun();
return true;
}
void DigitizerChain::Stop()
{
if (size() == 0)
{
DY_ERROR("How the hell did you get here?");
return;
}
else if (size() == 1)
{
m_chain[0]->StopAquisition();
if (m_chain[0]->IsActive())
{
DY_ERROR("Single digitizer stop failed...");
return;
}
}
else
StopSynchronizedRun();
}
bool DigitizerChain::StartSynchronizedRun()
{
static uint32_t softwareTrigger = 0x4;
static uint32_t hardwareLevelTrigger = 0x5;
if (m_syncArgs.syncMethod != SyncMethod::SIn_TrigOut)
{
DY_WARN("Unsupported sync method {0} at StartSynchronizedRun!", SyncMethodToString(m_syncArgs.syncMethod));
return false;
}
int error_code = 0;
switch (m_syncArgs.startType)
{
case SyncStartType::HardwareControlled:
{
error_code |= CAEN_DGTZ_WriteRegister(m_chain[0]->GetDigitizerArgs().handle, CAEN_DGTZ_ACQ_CONTROL_ADD, softwareTrigger);
break;
}
case SyncStartType::SoftwareControlled:
{
error_code |= CAEN_DGTZ_WriteRegister(m_chain[0]->GetDigitizerArgs().handle, CAEN_DGTZ_ACQ_CONTROL_ADD, hardwareLevelTrigger);
break;
}
}
if (error_code != 0)
{
DY_ERROR("Error code {0} recieved from DigitizerChain::StartSynchronizedRun!", error_code);
return false;
}
return true;
}
void DigitizerChain::StopSynchronizedRun()
{
if (m_syncArgs.syncMethod != SyncMethod::SIn_TrigOut)
{
DY_WARN("Unsupported sync method {0} at StartSynchronizedRun!", SyncMethodToString(m_syncArgs.syncMethod));
return;
}
int ec = CAEN_DGTZ_SWStopAcquisition(m_chain[0]->GetDigitizerArgs().handle); //Seems pretty universal, for all methods 0 indicates stop
if(ec != 0)
DY_ERROR("Error code {0} recieved from DigitizerChain::StopSynchronizedRun!", ec);
}
}

58
src/DAQ/DigitizerChain.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef DIGITIZER_CHAIN_H
#define DIGITIZER_CHAIN_H
#include "Digitizer.h"
#include "Events/AcqEvent.h"
namespace Daqromancy {
/*
Wrapper around std::vector list of digitizers in a setup
*/
class DigitizerChain
{
public:
DigitizerChain();
~DigitizerChain();
const Digitizer::Ref& operator[](std::size_t index) const
{
return m_chain[index];
}
Digitizer::Ref& operator[](std::size_t index)
{
return m_chain[index];
}
//Mimic stl
std::size_t size() const { return m_chain.size(); }
bool empty() const { return m_chain.empty(); }
void clear() { m_chain.clear(); }
void DetectBoards();
void DisconnectBoards();
void SetDPPAcqMode(DPPAcqMode mode);
void SynchronizeBoards(const SyncArgs& e);
bool Start();
void Stop();
std::vector<Digitizer::Ref>::iterator begin() { return m_chain.begin(); }
std::vector<Digitizer::Ref>::iterator end() { return m_chain.end(); }
std::vector<Digitizer::Ref>::const_iterator begin() const { return m_chain.begin(); }
std::vector<Digitizer::Ref>::const_iterator end() const { return m_chain.end(); }
private:
bool StartSynchronizedRun();
void StopSynchronizedRun();
std::vector<Digitizer::Ref> m_chain;
SyncArgs m_syncArgs;
};
}
#endif

291
src/DAQ/DigitizerDefs.cpp Normal file
View File

@ -0,0 +1,291 @@
#include "DigitizerDefs.h"
namespace Daqromancy {
std::string BoardIOToString(int value)
{
switch (value)
{
case CAEN_DGTZ_IOLevel_NIM: return "NIM";
case CAEN_DGTZ_IOLevel_TTL: return "TTL";
}
return "None";
}
std::string PolarityToString(int value)
{
switch (value)
{
case CAEN_DGTZ_PulsePolarityPositive: return "Positive";
case CAEN_DGTZ_PulsePolarityNegative: return "Negative";
}
return "None";
}
std::string DynamicRangeToString(DynamicRange range)
{
switch (range)
{
case DynamicRange::MilliVolt_500: return "0.5V";
case DynamicRange::Volt_2: return "2.0V";
}
return "None";
}
//Simple integer power calc
int Power(int base, int order)
{
int value = 1;
for (int i = 1; i <= order; i++)
value = value * base;
return value;
}
std::string IntSwitchToString(int value)
{
switch (value)
{
case 0: return "Off";
case 1: return "On";
}
return "None";
}
std::string DiscriminatorModeToString(int mode)
{
switch (mode)
{
case CAEN_DGTZ_DPP_DISCR_MODE_CFD: return "CFD";
case CAEN_DGTZ_DPP_DISCR_MODE_LED: return "Leading-Edge";
}
return "None";
}
std::string CFDFractionToString(int val)
{
switch (val)
{
case 0: return "0.25";
case 1: return "0.5";
case 2: return "0.75";
case 3: return "1.0";
}
return "None";
}
std::string TriggerConfigToString(int val)
{
switch (val)
{
case CAEN_DGTZ_DPP_TriggerMode_Normal: return "Normal";
case CAEN_DGTZ_DPP_TriggerMode_Coincidence: return "External Coinc.";
}
return "None";
}
std::string PileUpModeToString(int val)
{
switch (val)
{
case CAEN_DGTZ_DPP_PSD_PUR_DetectOnly: return "Off";
case CAEN_DGTZ_DPP_PSD_PUR_Enabled: return "On";
}
return "None";
}
//Requires both dynamic range and charge sens. value (different ranges = different values)
std::string ChargeSensToString(int cs_val, DynamicRange range)
{
if (range == DynamicRange::MilliVolt_500)
{
switch (cs_val)
{
case 0: return "1.25 fC/LSB";
case 1: return "5 fC/LSB";
case 2: return "20 fC/LSB";
case 3: return "80 fC/LSB";
case 4: return "320 fC/LSB";
case 5: return "1.28 pC/LSB";
}
}
else if (range == DynamicRange::Volt_2)
{
switch (cs_val)
{
case 0: return "5 fC/LSB";
case 1: return "20 fC/LSB";
case 2: return "80 fC/LSB";
case 3: return "320 fC/LSB";
case 4: return "1.28 pC/LSB";
case 5: return "5.12 pC/LSB";
}
}
return "None";
}
std::string PHAVirtualProbe1ToString(int val)
{
switch (val)
{
case PHAVirtualProbe1Options::PHAVP1_Input: return "Input";
case PHAVirtualProbe1Options::PHAVP1_Trapezoid: return "Trapezoid";
case PHAVirtualProbe1Options::PHAVP1_Delta: return "Delta";
case PHAVirtualProbe1Options::PHAVP1_Delta2: return "Delta2";
}
return "None";
}
std::string PHAVirtualProbe2ToString(int val)
{
switch (val)
{
case PHAVirtualProbe2Options::PHAVP2_Input: return "Input";
case PHAVirtualProbe2Options::PHAVP2_Baseline: return "Baseline";
case PHAVirtualProbe2Options::PHAVP2_Threshold: return "Threshold";
case PHAVirtualProbe2Options::PHAVP2_TrapezoidReduced: return "Trap. Reduced";
case PHAVirtualProbe2Options::PHAVP2_None: return "None";
}
return "None";
}
std::string PHADigitalProbeToString(int val)
{
switch (val)
{
case PHADigitalProbe1Options::PHADP_ACQVeto: return "Acq. Veto";
case PHADigitalProbe1Options::PHADP_Armed: return "Armed";
case PHADigitalProbe1Options::PHADP_BaselineFreeze: return "Baseline Freeze";
case PHADigitalProbe1Options::PHADP_BFMVeto: return "BFM Veto";
case PHADigitalProbe1Options::PHADP_Busy: return "Busy";
case PHADigitalProbe1Options::PHADP_CoincidenceWindow: return "Coincidence Window";
case PHADigitalProbe1Options::PHADP_ExternalTrig: return "External Trig.";
case PHADigitalProbe1Options::PHADP_Peaking: return "Peaking";
case PHADigitalProbe1Options::PHADP_PileUp: return "PileUp";
case PHADigitalProbe1Options::PHADP_PrgVeto: return "PrgVeto";
case PHADigitalProbe1Options::PHADP_TriggerHoldoff: return "Trig. Hold-Off";
case PHADigitalProbe1Options::PHADP_TriggerValue: return "Trig. Value";
case PHADigitalProbe1Options::PHADP_TriggerWindow: return "Trig. Window";
}
return "None";
}
std::string PSDVirtualProbe1ToString(int val)
{
switch (val)
{
case PSDVirtualProbe1Options::PSDVP1_Input: return "Input";
case PSDVirtualProbe1Options::PSDVP1_CFD: return "CFD";
}
return "None";
}
std::string PSDVirtualProbe2ToString(int val)
{
switch (val)
{
case PSDVirtualProbe2Options::PSDVP2_Baseline: return "Baseline";
case PSDVirtualProbe2Options::PSDVP2_CFD: return "CFD";
case PSDVirtualProbe2Options::PSDVP2_None: return "None";
}
return "None";
}
std::string PSDDigitalProbe1ToString(int val)
{
switch (val)
{
case PSDDigitalProbe1Options::PSDDP1_Coincidence: return "Coincidence";
case PSDDigitalProbe1Options::PSDDP1_CoincidenceWindow: return "Coincidence Window";
case PSDDigitalProbe1Options::PSDDP1_Gate: return "Gate";
case PSDDigitalProbe1Options::PSDDP1_OverThreshold: return "Over Threshold";
case PSDDigitalProbe1Options::PSDDP1_PileUp: return "PileUp";
case PSDDigitalProbe1Options::PSDDP1_Trigger: return "Trigger";
case PSDDigitalProbe1Options::PSDDP1_TriggerOut: return "TriggerOut";
}
return "None";
}
std::string PSDDigitalProbe2ToString(int val)
{
switch (val)
{
case PSDDigitalProbe2Options::PSDDP2_Coincidence: return "Coincidence";
case PSDDigitalProbe2Options::PSDDP2_GateShort: return "Short Gate";
case PSDDigitalProbe2Options::PSDDP2_OverThreshold: return "Over Threshold";
case PSDDigitalProbe2Options::PSDDP2_PileUp: return "PileUp";
case PSDDigitalProbe2Options::PSDDP2_Trigger: return "Trigger";
case PSDDigitalProbe2Options::PSDDP2_TriggerHoldoff: return "Trig. Hold-Off";
case PSDDigitalProbe2Options::PSDDP2_TriggerVal: return "Trig. Value";
}
return "None";
}
std::string SyncModeToString(int val)
{
switch (val)
{
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_Disabled: return "Disabled";
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_TrgOutSinDaisyChain: return "TrigOut-S In";
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_SinFanout: return "S In Fanout";
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_TrgOutTrgInDaisyChain: return "TrigOut-TrigIn";
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_GpioGpioDaisyChain: return "Broken";
}
return "None";
}
std::string AcqModeToString(int val)
{
switch (val)
{
case CAEN_DGTZ_AcqMode_t::CAEN_DGTZ_FIRST_TRG_CONTROLLED: return "FirstTrg";
case CAEN_DGTZ_AcqMode_t::CAEN_DGTZ_LVDS_CONTROLLED: return "LVDS";
case CAEN_DGTZ_AcqMode_t::CAEN_DGTZ_SW_CONTROLLED: return "Software";
case CAEN_DGTZ_AcqMode_t::CAEN_DGTZ_S_IN_CONTROLLED: return "S In";
}
return "None";
}
std::string FirmwareTypeToString(int val)
{
switch (val)
{
case CAEN_DGTZ_DPPFirmware_PHA: return "PHA";
case CAEN_DGTZ_DPPFirmware_PSD: return "PSD";
case CAEN_DGTZ_DPPFirmware_CI: return "CI";
case CAEN_DGTZ_DPPFirmware_DAW: return "DAW";
case CAEN_DGTZ_DPPFirmware_QDC: return "QDC";
case CAEN_DGTZ_DPPFirmware_ZLE: return "ZLE";
case CAEN_DGTZ_DPPFirmwareNotSupported: return "NotSupported";
}
return "None";
}
std::string SyncMethodToString(SyncMethod method)
{
switch (method)
{
case SyncMethod::SIn_TrigOut: return "S-In TrigOut";
case SyncMethod::None: return "None";
}
return "None";
}
double GetSamplingPeriod(CAEN_DGTZ_BoardModel_t model)
{
switch (model)
{
case CAEN_DGTZ_V1730: return 2.0; //ns -> 500 MSamples/s
case CAEN_DGTZ_V1725: return 4.0; //ns -> 250 MSamples/s
}
return 4.0; //Idk? This is a fairly common value...
}
}

291
src/DAQ/DigitizerDefs.h Normal file
View File

@ -0,0 +1,291 @@
#ifndef DIGITIZER_DEFS_H
#define DIGITIZER_DEFS_H
#include <CAENDigitizerType.h>
#include <string>
#include <vector>
namespace Daqromancy {
namespace Data
{
static constexpr uint64_t dataSize = 24; //size of writable data in bytes (no waveforms)
}
enum class DigitizerAccessType
{
All,
Single,
None
};
//Unified Daqromancy Data structure (similar to CAEN CoMPASS)
struct DYData
{
uint16_t board;
uint16_t channel;
uint64_t timestamp;
uint32_t energy;
uint32_t energyShort;
uint32_t flags;
uint32_t waveSize;
std::vector<uint16_t> trace1Samples; //For waves: traces are actual data to be saved potentially, digital is result of algorithms
std::vector<uint16_t> trace2Samples;
std::vector<uint8_t> digitalTrace1Samples;
std::vector<uint8_t> digitalTrace2Samples; //Technically DPP supports 4 in docs? But all examples only seem to support 2...
};
enum DPPAcqMode
{
List = 1,
Waves = 2,
None = 0
};
enum class SyncStartType
{
HardwareControlled,
SoftwareControlled,
None
};
//Add more later, right now only accept SPS standard
enum class SyncMethod
{
SIn_TrigOut,
None
};
struct SyncArgs
{
SyncStartType startType = SyncStartType::SoftwareControlled;
SyncMethod syncMethod = SyncMethod::SIn_TrigOut;
};
//These need to be plain enums cause we're gonna cast them to int, so just be careful
enum DynamicRange
{
Volt_2 = 0, //2Volt
MilliVolt_500 = 1//0.5Volt
};
enum PHAVirtualProbe1Options
{
PHAVP1_Input = CAEN_DGTZ_DPP_VIRTUALPROBE_Input,
PHAVP1_Delta = CAEN_DGTZ_DPP_VIRTUALPROBE_Delta,
PHAVP1_Delta2 = CAEN_DGTZ_DPP_VIRTUALPROBE_Delta2,
PHAVP1_Trapezoid = CAEN_DGTZ_DPP_VIRTUALPROBE_Trapezoid
};
enum PHAVirtualProbe2Options
{
PHAVP2_Input = CAEN_DGTZ_DPP_VIRTUALPROBE_Input,
PHAVP2_Threshold = CAEN_DGTZ_DPP_VIRTUALPROBE_Threshold,
PHAVP2_Baseline = CAEN_DGTZ_DPP_VIRTUALPROBE_Baseline,
PHAVP2_TrapezoidReduced = CAEN_DGTZ_DPP_VIRTUALPROBE_TrapezoidReduced,
PHAVP2_None = CAEN_DGTZ_DPP_VIRTUALPROBE_None
};
enum PHADigitalProbe1Options
{
PHADP_TriggerWindow = CAEN_DGTZ_DPP_DIGITALPROBE_TRGWin,
PHADP_Armed = CAEN_DGTZ_DPP_DIGITALPROBE_Armed,
PHADP_PkRun = CAEN_DGTZ_DPP_DIGITALPROBE_PkRun,
PHADP_PileUp = CAEN_DGTZ_DPP_DIGITALPROBE_PileUp,
PHADP_Peaking = CAEN_DGTZ_DPP_DIGITALPROBE_Peaking,
PHADP_CoincidenceWindow = CAEN_DGTZ_DPP_DIGITALPROBE_CoincWin,
PHADP_BaselineFreeze = CAEN_DGTZ_DPP_DIGITALPROBE_BLFreeze,
PHADP_TriggerHoldoff = CAEN_DGTZ_DPP_DIGITALPROBE_TRGHoldoff,
PHADP_TriggerValue = CAEN_DGTZ_DPP_DIGITALPROBE_TRGVal,
PHADP_ACQVeto = CAEN_DGTZ_DPP_DIGITALPROBE_ACQVeto,
PHADP_BFMVeto = CAEN_DGTZ_DPP_DIGITALPROBE_BFMVeto,
PHADP_ExternalTrig = CAEN_DGTZ_DPP_DIGITALPROBE_ExtTRG,
PHADP_Busy = CAEN_DGTZ_DPP_DIGITALPROBE_Busy,
PHADP_PrgVeto = CAEN_DGTZ_DPP_DIGITALPROBE_PrgVeto
};
enum PSDVirtualProbe1Options
{
PSDVP1_Input = CAEN_DGTZ_DPP_VIRTUALPROBE_Input,
PSDVP1_CFD = CAEN_DGTZ_DPP_VIRTUALPROBE_CFD
};
enum PSDVirtualProbe2Options
{
PSDVP2_Baseline = CAEN_DGTZ_DPP_VIRTUALPROBE_Baseline,
PSDVP2_CFD = CAEN_DGTZ_DPP_VIRTUALPROBE_CFD,
PSDVP2_None = CAEN_DGTZ_DPP_VIRTUALPROBE_None
};
enum PSDDigitalProbe1Options
{
PSDDP1_Gate = CAEN_DGTZ_DPP_DIGITALPROBE_Gate,
PSDDP1_OverThreshold = CAEN_DGTZ_DPP_DIGITALPROBE_OverThr,
PSDDP1_TriggerOut = CAEN_DGTZ_DPP_DIGITALPROBE_TRGOut,
PSDDP1_CoincidenceWindow = CAEN_DGTZ_DPP_DIGITALPROBE_CoincWin,
PSDDP1_PileUp = CAEN_DGTZ_DPP_DIGITALPROBE_PileUp,
PSDDP1_Coincidence = CAEN_DGTZ_DPP_DIGITALPROBE_Coincidence,
PSDDP1_Trigger = CAEN_DGTZ_DPP_DIGITALPROBE_Trigger
};
enum PSDDigitalProbe2Options
{
PSDDP2_GateShort = CAEN_DGTZ_DPP_DIGITALPROBE_GateShort,
PSDDP2_OverThreshold = CAEN_DGTZ_DPP_DIGITALPROBE_OverThr,
PSDDP2_TriggerVal = CAEN_DGTZ_DPP_DIGITALPROBE_TRGVal,
PSDDP2_TriggerHoldoff = CAEN_DGTZ_DPP_DIGITALPROBE_TRGHoldoff,
PSDDP2_PileUp = CAEN_DGTZ_DPP_DIGITALPROBE_PileUp,
PSDDP2_Coincidence = CAEN_DGTZ_DPP_DIGITALPROBE_Coincidence,
PSDDP2_Trigger = CAEN_DGTZ_DPP_DIGITALPROBE_Trigger
};
//Data vital to status/exsistance of digitizer
struct DigitizerArgs
{
CAEN_DGTZ_ConnectionType type = CAEN_DGTZ_ConnectionType::CAEN_DGTZ_OpticalLink; //default (makes sense for FSU)
int linkNumber = -1;
int conetNode = -1;
uint32_t vmeAddress = 0;
int handle = -1;
CAEN_DGTZ_BoardModel_t model = CAEN_DGTZ_V1730; //Find way for default?
CAEN_DGTZ_DPPFirmware_t firmware = CAEN_DGTZ_NotDPPFirmware;
std::string name = "None";
int channels = 0; //number of channels in board
int status = CAEN_DGTZ_NotYetImplemented;
bool operator==(const DigitizerArgs& rhs)
{
if (this->type == rhs.type && this->linkNumber == rhs.linkNumber && this->conetNode == rhs.conetNode && this->vmeAddress == rhs.vmeAddress
&& this->handle == rhs.handle && this->model == rhs.model && this->firmware == rhs.firmware && this->name == rhs.name && this->channels == rhs.channels)
{
return true;
}
return false;
}
bool operator!=(const DigitizerArgs& rhs)
{
return !(*this == rhs);
}
};
//Parameters that are applied to digitizer as a whole
struct DigitizerParameters
{
uint32_t recordLength = 600; //nanoseconds
uint32_t channelMask = 0xffff;
int eventAggr = 0; //Allow CAEN to optimize
CAEN_DGTZ_AcqMode_t acqMode = CAEN_DGTZ_SW_CONTROLLED;
CAEN_DGTZ_DPP_AcqMode_t dppAcqMode = CAEN_DGTZ_DPP_ACQ_MODE_List;
CAEN_DGTZ_DPP_SaveParam_t dppSaveMode = CAEN_DGTZ_DPP_SAVE_PARAM_EnergyAndTime;
CAEN_DGTZ_IOLevel_t IOlevel = CAEN_DGTZ_IOLevel_NIM;
CAEN_DGTZ_TriggerMode_t triggerMode = CAEN_DGTZ_TRGMODE_ACQ_ONLY;
CAEN_DGTZ_RunSyncMode_t syncMode = CAEN_DGTZ_RUN_SYNC_Disabled;
};
//Per channel PHA settings
struct PHAParameters
{
bool isEnabled = true;
uint32_t preTriggerTime = 120; //nanoseconds
float dcOffset = 0.2f;
CAEN_DGTZ_PulsePolarity_t pulsePolarity = CAEN_DGTZ_PulsePolarityPositive;
DynamicRange dynamicRange = DynamicRange::Volt_2;
int decayTimeConst = 100000; //nanoseconds
int trapFlatTop = 1984; //nanoseconds
int trapRiseTime = 384; //nanoseconds
int flatTopDelay = 1000; //nanoseconds
int triggerFilterSmoothing = 16; // Valid powers of 2 up to 32
int inputRiseTime = 192; //nanoseconds
int triggerThreshold = 300; //LSB
int samplesBaseLineMean = 3; //Options: 1->16 samples; 2->64 samples; 3->256 samples; 4->1024 samples; 5->4096 samples; 6->16384 samples
int samplesPeakMean = 3; //Options: 0-> 1 sample; 1->4 samples; 2->16 samples; 3->64 samples
int peakHoldOff = 1600; //nanoseconds
int baseLineHoldOff = 2000; //nanoseconds
int otrej= 0; // n/a
int triggerHoldOff = 1984; //nanoseconds
int riseTimeValidationWindow = 400;//nanoseconds
int riseTimeDiscrimination = 0; // 0 off 1 on
int digitalProbeGain = 0; //Options: 0->DigitalGain=1; 1->DigitalGain=2 (only with decimation >= 2samples); 2->DigitalGain=4 (only with decimation >= 4samples); 3->DigitalGain=8( only with decimation = 8samples)
float energyNormalizationFactor = 1.0; //Default 1.0
int inputDecimation = 0; // decimation (the input signal samples are averaged within this number of samples): 0 ->disabled; 1->2 samples; 2->4 samples; 3->8 samples
};
//Per channel PSD settings
struct PSDParameters
{
bool isEnabled = true;
uint32_t preTriggerTime = 120; //nanoseconds
float dcOffset = 0.2f;
CAEN_DGTZ_PulsePolarity_t pulsePolarity = CAEN_DGTZ_PulsePolarityPositive;
DynamicRange dynamicRange = DynamicRange::Volt_2;
int baselineThreshold = 0; //Defined with fixed baseline only (LSB)
int bltmo = 0;//n/a
int triggerHoldOff = 1984; //nanoseconds
int triggerThreshold = 300; //LSB
int selfTrigger = 1; //0-Disabled 1-Enabled
int chargeSensitivity = 1; //Comments only for 1720 & 1751? Check compass
int shortGate = 96; //nanoseconds
int longGate = 400; //nanoseconds
int preGate = 32; //nanoseconds
int triggerValidationWindow = 4000; //nanoseconds
int samplesBasline = 4; //Again only 1720 and 1751
int discrminatorType = CAEN_DGTZ_DPP_DISCR_MODE_CFD;
int cfdFraction = 0; //0 25% 1 50% 2 75% 3 100%
int cfdDelay = 16; //nanoseconds
CAEN_DGTZ_DPP_TriggerConfig_t triggerConfig = CAEN_DGTZ_DPP_TriggerConfig_Threshold;
CAEN_DGTZ_DPP_PUR_t pileUpRejection = CAEN_DGTZ_DPP_PSD_PUR_DetectOnly;
int purgap = 100; //Purity Gap LSB
};
//Option of dual analog, which types (digital probe 2 is always trigger for PHA)
struct PHAWaveParameters
{
CAEN_DGTZ_DPP_VirtualProbe_t isDual = CAEN_DGTZ_DPP_VIRTUALPROBE_SINGLE; //Default to a single analog trace
PHAVirtualProbe1Options analogProbe1 = PHAVirtualProbe1Options::PHAVP1_Input; //Main analog trace defaults to input signal;
PHAVirtualProbe2Options analogProbe2 = PHAVirtualProbe2Options::PHAVP2_None; //Default val; in default config wont be displayed
PHADigitalProbe1Options digitalProbe1 = PHADigitalProbe1Options::PHADP_TriggerWindow; //Idk guess this is good default
};
struct PSDWaveParameters
{
CAEN_DGTZ_DPP_VirtualProbe_t isDual = CAEN_DGTZ_DPP_VIRTUALPROBE_SINGLE; //Default to a single analog trace
PSDVirtualProbe1Options analogProbe1 = PSDVirtualProbe1Options::PSDVP1_Input; //Main trace defaults to input
PSDVirtualProbe2Options analogProbe2 = PSDVirtualProbe2Options::PSDVP2_None; //Defaults to off
PSDDigitalProbe1Options digitalProbe1 = PSDDigitalProbe1Options::PSDDP1_Gate; //Defaults to long gate
PSDDigitalProbe2Options digitalProbe2 = PSDDigitalProbe2Options::PSDDP2_GateShort; //Defaults to short gate
};
//String conversion helper functions//
std::string BoardIOToString(int value);
std::string PolarityToString(int value);
std::string DynamicRangeToString(DynamicRange range);
std::string IntSwitchToString(int value);
std::string DiscriminatorModeToString(int mode);
std::string CFDFractionToString(int val);
std::string TriggerConfigToString(int val);
std::string PileUpModeToString(int val);
//Requires both dynamic range and charge sens. value (different ranges = different values)
std::string ChargeSensToString(int cs_val, DynamicRange range);
std::string PHAVirtualProbe1ToString(int val);
std::string PHAVirtualProbe2ToString(int val);
std::string PHADigitalProbeToString(int val);
std::string PSDVirtualProbe1ToString(int val);
std::string PSDVirtualProbe2ToString(int val);
std::string PSDDigitalProbe1ToString(int val);
std::string PSDDigitalProbe2ToString(int val);
std::string SyncModeToString(int val);
std::string AcqModeToString(int val);
std::string FirmwareTypeToString(int val);
std::string SyncMethodToString(SyncMethod method);
//Simple integer power calc
int Power(int base, int order);
//Get the time per sample for a type of board (returned in nanoseconds)
double GetSamplingPeriod(CAEN_DGTZ_BoardModel_t model);
}
#endif

57
src/DYio/DYFile.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "DYFile.h"
#include "DYListData.h"
#include <bit>
namespace Daqromancy {
DYFile::DYFile() :
m_fileHandle(nullptr), m_currentLocation(0)
{
if (std::endian::native == std::endian::big)
("You are using a big endian system! Data will be encoded in reverse byte order on most machines");
m_buffer.resize(s_bufferSize);
}
DYFile::DYFile(const std::filesystem::path& path) :
m_fileHandle(nullptr), m_currentLocation(0)
{
if (std::endian::native == std::endian::big)
DY_WARN("You are using a big endian system! Data will be encoded in reverse byte order on most machines");
m_buffer.resize(s_bufferSize);
Open(path);
}
DYFile::~DYFile()
{
if (IsOpen())
Close();
}
void DYFile::Open(const std::filesystem::path& path)
{
m_path = path;
m_fileHandle = std::make_shared<std::ofstream>(path);
}
void DYFile::Close()
{
if (m_currentLocation != 0)
Flush();
m_fileHandle->close();
}
void DYFile::Flush()
{
m_fileHandle->write(m_buffer.data(), m_currentLocation);
m_currentLocation = 0;
}
//See BSListData.h for details on loading data to buffer.
void DYFile::Write(const DYData& data)
{
m_currentLocation = LoadDYDataToBuffer(m_buffer, data);
if (m_currentLocation == s_bufferSize)
Flush();
}
}

35
src/DYio/DYFile.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef DY_FILE_H
#define DY_FILE_H
#include <filesystem>
#include "DAQ/DigitizerDefs.h"
namespace Daqromancy {
class DYFile
{
public:
DYFile();
DYFile(const std::filesystem::path& path);
~DYFile();
void Open(const std::filesystem::path& path);
void Write(const DYData& data);
void Close();
bool IsOpen() const { return m_fileHandle == nullptr ? false : m_fileHandle->is_open(); }
const std::filesystem::path& GetPath() const { return m_path; }
private:
void Flush();
std::vector<char> m_buffer;
std::shared_ptr<std::ofstream> m_fileHandle;
std::filesystem::path m_path;
std::size_t m_currentLocation;
static constexpr uint64_t s_bufferSize = Data::dataSize * 100; //Store 100 hits before flushing? Idk
};
}
#endif

106
src/DYio/DYListData.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef BS_LIST_DATA_H
#define BS_LIST_DATA_H
#include "DAQ/DigitizerDefs.h"
namespace Daqromancy {
//This is the subset of DYData that will be transferred via binary writes/reads
//on either network or file io
struct BSListData
{
uint16_t board;
uint16_t channel;
uint64_t timestamp;
uint32_t energy;
uint32_t energyShort;
uint32_t flags;
};
/*
Load DYData to a binary buffer(in our implementation this is as std::vector<char>),
stripping away any wave data, returns index after last written to.
Quite nasty. Writing structs is dangerous for cross-platformness, even with pod's
Cannot guarantee padding is equivalent from different compilers, architectures.
So, convert each individual data member to binary, and write our own compact message.
*/
static std::size_t LoadDYDataToBuffer(std::vector<char>& buffer, const DYData& data)
{
char* dataPointer;
int loopIndex; // predeclare to save allocation time
int position = buffer.empty() ? 0 : buffer.size() - 1;
if (buffer.size() - position < Data::dataSize)
{
DY_ERROR("Could not load data to buffer; data size exceeded remaining buffer size {0}", buffer.size() - position);
return position;
}
//Write board (2 bytes)
dataPointer = (char*)&(data.board);
for (loopIndex = 0; loopIndex < sizeof(data.board); loopIndex++)
buffer[position++] = dataPointer[loopIndex];
//Write channel (2 bytes)
dataPointer = (char*)&(data.channel);
for (loopIndex = 0; loopIndex < sizeof(data.channel); loopIndex++)
buffer[position++] = dataPointer[loopIndex];
//Write timestamp (8 bytes)
dataPointer = (char*)&(data.timestamp);
for (loopIndex = 0; loopIndex < sizeof(data.timestamp); loopIndex++)
buffer[position++] = dataPointer[loopIndex];
//Write energy (4 bytes)
dataPointer = (char*)&(data.energy);
for (loopIndex = 0; loopIndex < sizeof(data.energy); loopIndex++)
buffer[position++] = dataPointer[loopIndex];
//Write energyShort (4 bytes)
dataPointer = (char*)&(data.energyShort);
for (loopIndex = 0; loopIndex < sizeof(data.energyShort); loopIndex++)
buffer[position++] = dataPointer[loopIndex];
//Write flags (4 bytes)
dataPointer = (char*)&(data.flags);
for (loopIndex = 0; loopIndex < sizeof(data.flags); loopIndex++)
buffer[position++] = dataPointer[loopIndex];
return position;
}
/*
Unload BSListData from a binary buffer(in our implementation this is as std::vector<char>),
returns index after last pulled from. Writes to input parameter data
Again, rather nasty. Here we C-style cast the data in the buffer to our types in the list data,
write them to the input parameter reference, and then iterate forward the appropriate number of bytes.
*/
static std::size_t UnLoadBSListDataFromBuffer(const std::vector<char>& buffer, BSListData& data, std::size_t startPosition)
{
std::size_t position = startPosition;
if (buffer.size() - startPosition < Data::dataSize)
{
DY_ERROR("Could not unload buffer to data; data size exceeded remaining buffer size {0}", buffer.size() - startPosition);
return startPosition;
}
data.board = *(uint16_t*)&(buffer[position]);
position += 2;
data.channel = *(uint16_t*)&(buffer[position]);
position += 2;
data.timestamp = *(uint16_t*)&(buffer[position]);
position += 8;
data.timestamp = *(uint16_t*)&(buffer[position]);
position += 8;
data.energy = *(uint32_t*)&(buffer[position]);
position += 4;
data.energyShort = *(uint32_t*)&(buffer[position]);
position += 4;
data.flags = *(uint32_t*)&(buffer[position]);
position += 4;
return position;
}
}
#endif

61
src/DYio/DYMessage.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef BS_MESSAGE_H
#define BS_MESSAGE_H
#include "DAQ/DigitizerDefs.h"
#include "DYListData.h"
namespace Daqromancy {
enum class BSMessageType : uint64_t
{
List,
Mixed, //Maybe someday hahaha
None
};
struct BSHeader
{
BSMessageType type = BSMessageType::None;
uint64_t size = 0;
};
struct BSMessage
{
BSMessage() = default;
BSMessage(const std::vector<DYData>& data)
{
header.type = BSMessageType::List;
for (const auto& datum : data)
LoadDYDataToBuffer(body, datum);
}
BSHeader header;
std::vector<char> body;
std::size_t Size() const
{
return header.size;
}
//Nasty work. Convert header into a raw bytes array for transmission. Makes it so padding is no longer a concern.
std::vector<char> GetHeaderRaw() const
{
std::vector<char> rawBytes(sizeof(header.type) + sizeof(header.size));
std::size_t position = 0;
int loopIndex;
char* dataPointer = (char*)&header.type;
for (loopIndex = 0; loopIndex < sizeof(header.type); loopIndex++)
rawBytes[position++] = dataPointer[loopIndex];
dataPointer = (char*)&header.size;
for (loopIndex = 0; loopIndex < sizeof(header.size); loopIndex++)
rawBytes[position++] = dataPointer[loopIndex];
return rawBytes;
}
};
}
#endif

113
src/DYio/DYRun.cpp Normal file
View File

@ -0,0 +1,113 @@
#include "DYRun.h"
#include "Core/ProjectSerializer.h"
namespace Daqromancy {
//Maps two uint32_t's to a single uint32_t
static uint32_t SzudzikPairingFunction(uint32_t v1, uint32_t v2)
{
return v1 >= v2 ? (v1 * v1 + v1 + v2) : (v2 * v2 + v1);
}
DYRun::DYRun() :
m_isRunning(false), m_processingThread(nullptr)
{
}
DYRun::~DYRun()
{
StopRun();
}
void DYRun::InitFiles(const DYProject::Ref& project)
{
std::filesystem::path runPath = project->CreateRunDirectory();
//Write a run local copy of the daq settings
ProjectSerializer serializer(runPath / "settings.yaml");
serializer.SerializeData(project);
const std::vector<DigitizerArgs>& argList = project->GetDigitizerArgsList();
std::string filename;
uint32_t key;
for (const auto& args : argList)
{
for (int i = 0; i < args.channels; i++)
{
filename = args.name + "_CH_" + std::to_string(i) + ".bsbin";
key = SzudzikPairingFunction(args.handle, i);
m_files.insert_or_assign(key, DYFile(runPath / filename));
if (!(m_files[key].IsOpen()))
{
DY_WARN("File named {0} could not be opened!", filename);
}
}
}
}
void DYRun::StartRun(const DYProject::Ref& project)
{
if (m_isRunning)
StopRun();
//m_ringConsumerID = RingBuffer::Attach();
m_dataHandle = DataDistributor::Connect();
InitFiles(project);
m_isRunning = true;
m_processingThread = new std::thread(&DYRun::ProcessData, std::ref(*this));
}
void DYRun::StopRun()
{
if (m_dataHandle.dataQueue == nullptr)
return;
else if(!m_dataHandle.dataQueue->IsEmpty())
{
DY_INFO("Finishing writing data to file before stopping... Still have {0} events to process", m_dataHandle.dataQueue->Size());
while (!m_dataHandle.dataQueue->IsEmpty())
{
continue;
}
}
m_isRunning = false;
m_dataHandle.dataQueue->ForceWakeup();
if (m_processingThread != nullptr && m_processingThread->joinable())
{
m_processingThread->join();
delete m_processingThread;
m_processingThread = nullptr;
DataDistributor::Disconnect(m_dataHandle);
//Close all files and clear the map to be ready for re-use
for (auto& fileIter : m_files)
fileIter.second.Close();
m_files.clear();
}
else if(m_processingThread != nullptr)
DY_WARN("Unable to destroy the processing thread for file writing!");
m_dataHandle.dataQueue->ResetWakeup();
}
void DYRun::ProcessData()
{
std::vector<DYData> dataBuffer;
uint32_t key;
while (m_isRunning)
{
m_dataHandle.dataQueue->Wait(); //Block for the queue to be full
if (!m_dataHandle.dataQueue->IsEmpty()) //Protect for edge cases
{
dataBuffer = m_dataHandle.dataQueue->Front();
for (auto& datum : dataBuffer)
{
key = SzudzikPairingFunction(datum.board, datum.channel);
m_files[key].Write(datum);
}
m_dataHandle.dataQueue->PopFront();
}
}
}
}

34
src/DYio/DYRun.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef DY_RUN_H
#define DY_RUN_H
#include "DYFile.h"
#include "Core/DYProject.h"
#include "Core/DataDistributor.h"
#include <atomic>
#include <thread>
namespace Daqromancy {
class DYRun
{
public:
DYRun();
~DYRun();
void StartRun(const DYProject::Ref& project);
void StopRun();
bool IsRunning() const { return m_isRunning; }
private:
void InitFiles(const DYProject::Ref& project);
void ProcessData();
std::unordered_map<uint32_t, DYFile> m_files;
DistributorClient m_dataHandle;
std::atomic<bool> m_isRunning;
std::thread* m_processingThread;
};
}
#endif

203
src/DYio/TCPServer.cpp Normal file
View File

@ -0,0 +1,203 @@
#include "TCPServer.h"
namespace Daqromancy {
TCPServerConnection::TCPServerConnection(asio::io_context& context, asio::ip::tcp::socket socket) :
m_contextRef(context), m_socket(std::move(socket))
{
}
TCPServerConnection::~TCPServerConnection()
{
}
/*
The job we push to asio is slightly strange. If the queue is empty when we try to push a new job to it, it tells us
that the loop of write calls and job submissions had stopped (or was not yet started), so we need to restart/start the
cycle of writing data to the socket.
*/
void TCPServerConnection::Send(const BSMessage& message)
{
asio::post(m_contextRef,
[this, message]()
{
bool wasEmpty = m_queue.IsEmpty();
m_queue.PushBack(message);
if (wasEmpty)
{
WriteHeader();
}
});
}
void TCPServerConnection::Disconnect()
{
if (IsConnected())
asio::post(m_contextRef, [this]() { m_socket.close(); });
}
void TCPServerConnection::WriteHeader()
{
std::vector<char> headerData = m_queue.Front().GetHeaderRaw();
asio::async_write(m_socket, asio::buffer(headerData, headerData.size()),
[this](std::error_code ec, std::size_t length) //Callback upon completion
{
if (!ec)
{
if (m_queue.Front().Size() > 0)
WriteBody(); //submit next job to asio: write body data
else
{
m_queue.PopFront();
if (!m_queue.IsEmpty())
WriteHeader(); //submit next job to asio: write the next message
}
}
else
{
DY_WARN("TCPServerConnection -> Failure to write header of message, with error code: {0}", ec.message());
DY_WARN("Closing connection...");
m_socket.close();
}
});
}
void TCPServerConnection::WriteBody()
{
asio::async_write(m_socket, asio::buffer(m_queue.Front().body, m_queue.Front().Size()),
[this](std::error_code ec, std::size_t length)
{
if (!ec)
{
m_queue.PopFront();
if (!m_queue.IsEmpty())
WriteHeader();
}
else
{
DY_WARN("TCPServerConnection -> Failure to write body of message, with error code: {0}", ec.message());
DY_WARN("Closing connection...");
m_socket.close();
}
});
}
TCPServer::TCPServer(uint16_t serverPort) :
m_acceptor(m_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), serverPort)), m_isDataFeedRunning(false)
{
}
TCPServer::~TCPServer()
{
}
void TCPServer::PowerOn()
{
try
{
WaitForClient();
m_acceptorThread = std::thread([this]() { m_context.run(); });
}
catch (std::exception& e)
{
DY_ERROR("Server caught exception: {0}", e.what());
return;
}
DY_INFO("Server started and waiting for client connection");
}
void TCPServer::Shutdown()
{
StopDataFeed();
m_context.stop();
if (m_acceptorThread.joinable())
m_acceptorThread.join();
DY_INFO("Sever has been shutdown");
}
void TCPServer::StartDataFeed()
{
if (m_dataFeedThread.joinable())
{
DY_WARN("Server malfunction! You forgot to call StopDataFeed before calling StartDataFeed! Trying to recover...");
StopDataFeed();
}
if (m_dataHandle.dataQueue != nullptr)
m_dataHandle = DataDistributor::Connect();
m_isDataFeedRunning = true;
m_dataFeedThread = std::thread([this]()
{
while (m_isDataFeedRunning)
{
m_dataHandle.dataQueue->Wait();
if (!m_dataHandle.dataQueue->IsEmpty())
continue;
BSMessage message(m_dataHandle.dataQueue->Front());
MessageClients(message);
m_dataHandle.dataQueue->PopFront();
}
});
}
void TCPServer::StopDataFeed()
{
if (m_isDataFeedRunning == false)
return;
m_isDataFeedRunning = false;
m_dataHandle.dataQueue->ForceWakeup();
if (m_dataFeedThread.joinable())
m_dataFeedThread.join();
m_dataHandle.dataQueue->ResetWakeup();
}
void TCPServer::MessageClients(const BSMessage& message)
{
bool isAnyClientInvalid = false;
std::scoped_lock<std::mutex> guard(m_clientMutex);
for (auto& client : m_clients)
{
if (client->IsConnected())
{
client->Send(message);
}
else
{
client->Disconnect();
isAnyClientInvalid = true;
}
}
if (isAnyClientInvalid)
m_clients.erase(std::remove_if(m_clients.begin(), m_clients.end(), [](TCPServerConnection::Ref& client) { return client->IsConnected(); }), m_clients.end());
}
void TCPServer::WaitForClient()
{
m_acceptor.async_accept(
[this](std::error_code ec, asio::ip::tcp::socket socket)
{
if (!ec)
{
DY_INFO("Server connection to new client: {0}", socket.remote_endpoint());
{
//TCPServerConnection::Ref newClient = std::make_shared<TCPServerConnection>(m_context, std::move(socket));
std::scoped_lock<std::mutex> guard(m_clientMutex);
//m_clients.push_back(newClient);
m_clients.push_back(std::make_shared<TCPServerConnection>(m_context, std::move(socket)));
}
}
else
DY_ERROR("Server Connection Failure with error code: {0}", ec.message());
});
}
}

108
src/DYio/TCPServer.h Normal file
View File

@ -0,0 +1,108 @@
#ifndef TCP_CONNECTION_H
#define TCP_CONNECTION_H
#include "DYMessage.h"
#include "Core/ThreadSafeQueue.h"
#include "Core/DataDistributor.h"
#include "asio.hpp"
#include <thread>
#include <mutex>
#include <atomic>
namespace Daqromancy {
class TCPServerConnection
{
public:
using Ref = std::shared_ptr<TCPServerConnection>;
TCPServerConnection(asio::io_context& context, asio::ip::tcp::socket socket);
~TCPServerConnection();
bool IsConnected() const { return m_socket.is_open(); }
void Send(const BSMessage& message);
void Disconnect();
private:
void WriteHeader();
void WriteBody();
asio::ip::tcp::socket m_socket;
asio::io_context& m_contextRef;
ThreadSafeQueue<BSMessage> m_queue;
};
/*
TCPServer
Daqromancy specialized server implementation. First take at what is sure to evolve significantly as more testing is done
Currently creates two threads, one for accepting clients, and one for posting data.
PowerOn()/Shutdown() pair are use to initialize the server and start up the client accepting thread. This pair is essentially intended to go with
application start and stop.
StartDataFeed()/StopDataFeed() pair are used to tell the server when data is ready to be pulled from the ring buffer. This pari is intended to go with
a data run start and stop.
*/
class TCPServer
{
public:
TCPServer(uint16_t serverPort);
~TCPServer();
/*
PowerOn()
Starts up the acceptor thread of the server. This allows clients to begin to connect
to our service. This should be done more or less once, at application initialization.
*/
void PowerOn();
/*
Shutdown()
Shutdown the entire server, including both the acceptor thread and the data feed thread.
This should be done essentially once, at application shutdown.
*/
void Shutdown();
/*
StartDataFeed()
Starts the process to take data from the RingBuffer and feed it to clients (if they exist).
This will be called many times, usually at the start of a new data taking run.
*/
void StartDataFeed();
/*
StopDataFeed()
Stops the process to take data from the RingBuffer and feed it to clients (if they exist).
This will be called many times, usually at the stop of a data taking run.
*/
void StopDataFeed();
bool IsActive() { return m_acceptor.is_open(); }
bool IsFeeding() { return m_isDataFeedRunning; }
private:
void MessageClients(const BSMessage& message);
void WaitForClient();
asio::io_context m_context;
asio::ip::tcp::acceptor m_acceptor;
std::mutex m_clientMutex;
std::vector<TCPServerConnection::Ref> m_clients;
DistributorClient m_dataHandle;
std::thread m_acceptorThread;
std::thread m_dataFeedThread;
std::atomic<bool> m_isDataFeedRunning;
};
}
#endif

View File

@ -0,0 +1,972 @@
#include "DigitizerPanel.h"
namespace Daqromancy {
static std::string PanelTypeToString(DigitizerPanel::Type type)
{
switch (type)
{
case DigitizerPanel::Type::PHA: return "DPP PHA";
case DigitizerPanel::Type::PSD: return "DPP PSD";
case DigitizerPanel::Type::None: return "Invalid firmware";
}
return "Invalid firmware";
}
DigitizerPanel::DigitizerPanel(const DigitizerArgs& args) :
m_args(args)
{
Init();
}
DigitizerPanel::~DigitizerPanel() {}
void DigitizerPanel::Init()
{
if (m_args.firmware == CAEN_DGTZ_DPPFirmware_PHA)
{
m_panelType = Type::PHA;
m_panelName = m_args.name + "_PHA";
m_phaChannels.resize(m_args.channels);
m_digitizerEnabled = true;
}
else if (m_args.firmware == CAEN_DGTZ_DPPFirmware_PSD)
{
m_panelType = Type::PSD;
m_panelName = m_args.name + "_PSD";
m_psdChannels.resize(m_args.channels);
m_digitizerEnabled = true;
}
else
{
m_panelType = Type::None;
m_panelName = m_args.name + "_None";
m_digitizerEnabled = false;
}
}
void DigitizerPanel::SetDigitizerParameters(const DigitizerParameters& params)
{
m_params = params;
}
void DigitizerPanel::SetPHAParameters(const std::vector<PHAParameters>& ch_params, const PHAWaveParameters& wv_params)
{
if (ch_params.size() != m_phaChannels.size())
{
DY_ERROR("Trying to load channel parameters of size {0} to panel with {1} channels", ch_params.size(), m_phaChannels.size());
return;
}
m_phaChannels = ch_params;
m_phaWaves = wv_params;
}
void DigitizerPanel::SetPSDParameters(const std::vector<PSDParameters>& ch_params, const PSDWaveParameters& wv_params)
{
if (ch_params.size() != m_psdChannels.size())
{
DY_ERROR("Trying to load channel parameters of size {0} to panel with {1} channels", ch_params.size(), m_psdChannels.size());
return;
}
m_psdChannels = ch_params;
m_psdWaves = wv_params;
}
bool DigitizerPanel::OnImGuiRender()
{
bool changed = false;
if (ImGui::BeginTabItem(m_panelName.c_str()))
{
ImGui::Text("%s", PanelTypeToString(m_panelType).c_str());
ImGui::Text("%s", fmt::format("Optical Link: {0} CONET Node: {1}", m_args.linkNumber, m_args.conetNode).c_str());
ImGui::Text("Settings file: ");
ImGui::SameLine();
ImGui::Text("%s", m_settingsFile.c_str());
ImGui::Button("Open...");
ImGui::SameLine();
ImGui::Button("Save...");
ImGui::NewLine();
ImGui::Text("Board Global Settings");
changed |= RenderDigitizerParams();
switch (m_panelType)
{
case Type::PHA:
{
ImGui::NewLine();
ImGui::Text("PHA Channel Settings");
changed |= RenderPHAParameters();
ImGui::NewLine();
ImGui::Text("PHA Waves Settings");
changed |= RenderPHAWaveParameters();
break;
}
case Type::PSD:
{
ImGui::NewLine();
ImGui::Text("PSD Channel Settings");
changed |= RenderPSDParameters();
ImGui::NewLine();
ImGui::Text("PSD Waves Settings");
changed |= RenderPSDWaveParameters();
break;
}
case Type::None: break;
}
ImGui::EndTabItem();
}
return changed;
}
bool DigitizerPanel::RenderDigitizerParams()
{
bool changed = false;
if (ImGui::BeginTable("Digitizer Parameters", 6, tableFlags))
{
ImGui::TableSetupColumn("Enable/Disable");
ImGui::TableSetupColumn("Record Length (ns)");
ImGui::TableSetupColumn("Event Aggregation");
ImGui::TableSetupColumn("Acq. Mode");
//ImGui::TableSetupColumn("DPP Acq. Mode");
ImGui::TableSetupColumn("Board IO Level");
//ImGui::TableSetupColumn("Trigger Mode");
ImGui::TableSetupColumn("Sync Mode");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::RadioButton("##globalenable", m_digitizerEnabled))
{
changed |= true;
m_digitizerEnabled = !m_digitizerEnabled;
if (m_digitizerEnabled)
m_params.channelMask = 0xffff;
else
m_params.channelMask = 0;
}
if (!m_digitizerEnabled)
ImGui::BeginDisabled();
ImGui::TableNextColumn();
if (ImGui::InputScalar("##recordlength", ImGuiDataType_U32, &m_params.recordLength))
{
changed |= true;
}
ImGui::TableNextColumn();
if (ImGui::InputScalar("##eventAggregate", ImGuiDataType_U32, &m_params.eventAggr))
{
changed |= true;
}
ImGui::TableNextColumn();
ImGui::Text("%s", AcqModeToString(m_params.acqMode).c_str());
ImGui::TableNextColumn();
if (ImGui::BeginCombo("##boardIO", BoardIOToString(m_params.IOlevel).c_str()))
{
if (ImGui::Selectable("NIM", "NIM" == BoardIOToString(m_params.IOlevel)))
{
changed |= true;
m_params.IOlevel = CAEN_DGTZ_IOLevel_NIM;
}
if (ImGui::Selectable("TTL", "TTL" == BoardIOToString(m_params.IOlevel)))
{
changed |= true;
m_params.IOlevel = CAEN_DGTZ_IOLevel_TTL;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
ImGui::Text("%s", SyncModeToString(m_params.syncMode).c_str());
if (!m_digitizerEnabled)
ImGui::EndDisabled();
ImGui::EndTable();
}
return changed;
}
//PHA options; given per channel in the digitizer
bool DigitizerPanel::RenderPHAParameters()
{
bool changed = false;
static std::string tempString; //useful for comps in widgets
if (!m_digitizerEnabled)
ImGui::BeginDisabled();
if (ImGui::BeginTable("PHA Channel Parameters", 22, tableFlags | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY, ImVec2(0, 300)))
{
ImGui::TableSetupColumn("Channel");
ImGui::TableSetupColumn("Enable/Disable");
ImGui::TableSetupColumn("Pre-Trigger Time (ns)");
ImGui::TableSetupColumn("DC Offset (%)");
ImGui::TableSetupColumn("Polarity");
ImGui::TableSetupColumn("Dynamic Range");
ImGui::TableSetupColumn("Decay Time Const (ns)");
ImGui::TableSetupColumn("Trap. Flat Top (ns)");
ImGui::TableSetupColumn("Trap. Rise Time (ns)");
ImGui::TableSetupColumn("Flat Top Delay (ns)");
ImGui::TableSetupColumn("Smoothing");
ImGui::TableSetupColumn("Input Rise Time (ns)");
ImGui::TableSetupColumn("Threshold (lsb)");
ImGui::TableSetupColumn("Baseline Samples");
ImGui::TableSetupColumn("Peak Samples");
ImGui::TableSetupColumn("Peak Hold-off (ns)");
ImGui::TableSetupColumn("Baseline Hold-off (ns)");
ImGui::TableSetupColumn("Trigger Hold-off (ns)");
ImGui::TableSetupColumn("Validation Window (ns)");
ImGui::TableSetupColumn("Rise Time Disc.");
ImGui::TableSetupColumn("Probe Gain");
ImGui::TableSetupColumn("Input Samples");
ImGui::TableHeadersRow();
for (size_t i = 0; i < m_phaChannels.size(); i++)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%ld", i);
ImGui::TableNextColumn();
auto& channel = m_phaChannels[i];
if (ImGui::RadioButton(fmt::format("##channelEnable_{0}", i).c_str(), channel.isEnabled))
{
changed |= true;
channel.isEnabled = !channel.isEnabled;
}
if (!channel.isEnabled)
ImGui::BeginDisabled();
ImGui::TableNextColumn();
if (ImGui::InputScalar(fmt::format("##pretrigger_{0}", i).c_str(), ImGuiDataType_U32, &channel.preTriggerTime))
{
changed |= true;
}
ImGui::TableNextColumn();
if (ImGui::InputFloat(fmt::format("##dcoff_{0}", i).c_str(), &channel.dcOffset))
{
changed |= true;
}
ImGui::TableNextColumn();
tempString = PolarityToString(channel.pulsePolarity);
if (ImGui::BeginCombo(fmt::format("##polarity_{0}", i).c_str(), tempString.c_str()))
{
if (ImGui::Selectable("Positive", "Positive" == tempString))
{
changed |= true;
channel.pulsePolarity = CAEN_DGTZ_PulsePolarityPositive;
}
if (ImGui::Selectable("Negative", "Negative" == tempString))
{
changed |= true;
channel.pulsePolarity = CAEN_DGTZ_PulsePolarityNegative;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = DynamicRangeToString(channel.dynamicRange);
if (ImGui::BeginCombo(fmt::format("##dynamicRange_{0}", i).c_str(), tempString.c_str()))
{
if (ImGui::Selectable("0.5V", "0.5V" == tempString))
{
changed |= true;
channel.dynamicRange = DynamicRange::MilliVolt_500;
}
if (ImGui::Selectable("2.0V", "2.0V" == tempString))
{
changed |= true;
channel.dynamicRange = DynamicRange::Volt_2;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##decayTC_{0}", i).c_str(), &channel.decayTimeConst, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##trapFlatTop_{0}", i).c_str(), &channel.trapFlatTop, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##trapRiseT_{0}", i).c_str(), &channel.trapRiseTime, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##flatTopDelay_{0}", i).c_str(), &channel.decayTimeConst, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
tempString = std::to_string(channel.triggerFilterSmoothing);
if(ImGui::BeginCombo(fmt::format("##smoothing_{0}", i).c_str(), tempString.c_str()))
{
int val;
for (int pow = 0; pow < 6; pow++)
{
val = Power(2, pow);
if (ImGui::Selectable(std::to_string(val).c_str(), val == channel.triggerFilterSmoothing))
{
changed = true;
channel.triggerFilterSmoothing = val;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##inputRiseTime_{0}", i).c_str(), &channel.inputRiseTime, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##triggerThresh_{0}", i).c_str(), &channel.triggerThreshold, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
tempString = std::to_string(Power(4, 1+channel.samplesBaseLineMean));
if (ImGui::BeginCombo(fmt::format("##samplesBase_{0}", i).c_str(), tempString.c_str()))
{
int val;
for (int pow = 1; pow < 7; pow++)
{
val = Power(4, pow + 1);
if (ImGui::Selectable(std::to_string(val).c_str(), pow == channel.samplesBaseLineMean))
{
changed = true;
channel.samplesBaseLineMean = pow;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = std::to_string(Power(4, channel.samplesPeakMean));
if (ImGui::BeginCombo(fmt::format("##samplesPeak_{0}", i).c_str(), tempString.c_str()))
{
int val;
for (int pow = 0; pow < 4; pow++)
{
val = Power(4, pow);
if (ImGui::Selectable(std::to_string(val).c_str(), pow == channel.samplesPeakMean))
{
changed = true;
channel.samplesPeakMean = pow;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##peakHold_{0}", i).c_str(), &channel.peakHoldOff, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##baseHold_{0}", i).c_str(), &channel.baseLineHoldOff, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##trigHold_{0}", i).c_str(), &channel.triggerHoldOff, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##validWin_{0}", i).c_str(), &channel.riseTimeValidationWindow, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
tempString = IntSwitchToString(channel.riseTimeDiscrimination);
if (ImGui::BeginCombo(fmt::format("##riseTimeDisc_{0}", i).c_str(), tempString.c_str()))
{
if (ImGui::Selectable("On", "On" == tempString))
{
changed = true;
channel.riseTimeDiscrimination = 1;
}
if (ImGui::Selectable("Off", "Off" == tempString))
{
changed = true;
channel.riseTimeDiscrimination = 0;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = std::to_string(Power(2, channel.digitalProbeGain));
if (ImGui::BeginCombo(fmt::format("##digitalGain_{0}", i).c_str(), tempString.c_str()))
{
int value;
for (int pow = 0; pow < 4; pow++)
{
value = Power(2, pow);
if (ImGui::Selectable(std::to_string(value).c_str(), pow == channel.digitalProbeGain))
{
changed = true;
channel.digitalProbeGain = pow;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = std::to_string(Power(2, channel.inputDecimation));
if (ImGui::BeginCombo(fmt::format("##inputDeci_{0}", i).c_str(), tempString.c_str()))
{
int value;
for (int pow = 0; pow < 4; pow++)
{
value = Power(2, pow);
if (ImGui::Selectable(std::to_string(value).c_str(), pow == channel.inputDecimation))
{
changed = true;
channel.inputDecimation = pow;
}
}
ImGui::EndCombo();
}
if (!channel.isEnabled)
ImGui::EndDisabled();
}
ImGui::EndTable();
}
if (!m_digitizerEnabled)
ImGui::EndDisabled();
return changed;
}
bool DigitizerPanel::RenderPSDParameters()
{
bool changed = false;
static std::string tempString; //useful for comps in widgets
if (!m_digitizerEnabled)
ImGui::BeginDisabled();
if (ImGui::BeginTable("PSD Channel Parameters", 21, tableFlags | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY, ImVec2(0, 300)))
{
ImGui::TableSetupColumn("Channel");
ImGui::TableSetupColumn("Enable/Disable");
ImGui::TableSetupColumn("Pre-Trigger Time (ns)");
ImGui::TableSetupColumn("DC Offset (%)");
ImGui::TableSetupColumn("Polarity");
ImGui::TableSetupColumn("Dynamic Range");
ImGui::TableSetupColumn("Baseline Threshold (lsb)");
ImGui::TableSetupColumn("Trigger Threshold (lsb)");
ImGui::TableSetupColumn("Trigger Hold-Off (ns)");
ImGui::TableSetupColumn("Self-Trigger");
ImGui::TableSetupColumn("Charge Sensitivity");
ImGui::TableSetupColumn("Short Gate (ns)");
ImGui::TableSetupColumn("Long Gate (ns)");
ImGui::TableSetupColumn("Pre-Gate (ns)");
ImGui::TableSetupColumn("Validation Window (ns)");
ImGui::TableSetupColumn("Baseline Samples");
ImGui::TableSetupColumn("Discrimintaor Mode");
ImGui::TableSetupColumn("CFD Fraction");
ImGui::TableSetupColumn("CFD Delay (ns)");
ImGui::TableSetupColumn("PileUp Reject.");
ImGui::TableSetupColumn("Purity Gap");
ImGui::TableHeadersRow();
for (size_t i = 0; i < m_psdChannels.size(); i++)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%ld", i);
ImGui::TableNextColumn();
auto& channel = m_psdChannels[i];
if (ImGui::RadioButton(fmt::format("##channelEnable_{0}", i).c_str(), channel.isEnabled))
{
changed |= true;
channel.isEnabled = !channel.isEnabled;
}
if (!channel.isEnabled)
ImGui::BeginDisabled();
ImGui::TableNextColumn();
if (ImGui::InputScalar(fmt::format("##pretrigger_{0}", i).c_str(), ImGuiDataType_U32, &channel.preTriggerTime))
{
changed |= true;
}
ImGui::TableNextColumn();
if (ImGui::InputFloat(fmt::format("##dcoff_{0}", i).c_str(), &channel.dcOffset))
{
changed |= true;
}
ImGui::TableNextColumn();
tempString = PolarityToString(channel.pulsePolarity);
if (ImGui::BeginCombo(fmt::format("##polarity_{0}", i).c_str(), tempString.c_str()))
{
if (ImGui::Selectable("Positive", "Positive" == tempString))
{
changed |= true;
channel.pulsePolarity = CAEN_DGTZ_PulsePolarityPositive;
}
if (ImGui::Selectable("Negative", "Negative" == tempString))
{
changed |= true;
channel.pulsePolarity = CAEN_DGTZ_PulsePolarityNegative;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = DynamicRangeToString(channel.dynamicRange);
if (ImGui::BeginCombo(fmt::format("##dynamicRange_{0}", i).c_str(), tempString.c_str()))
{
if (ImGui::Selectable("0.5V", "0.5V" == tempString))
{
changed |= true;
channel.dynamicRange = DynamicRange::MilliVolt_500;
}
if (ImGui::Selectable("2.0V", "2.0V" == tempString))
{
changed |= true;
channel.dynamicRange = DynamicRange::Volt_2;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##baseThresh_{0}", i).c_str(), &channel.baselineThreshold, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##triggerThresh_{0}", i).c_str(), &channel.triggerThreshold, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##trigHold_{0}", i).c_str(), &channel.triggerHoldOff, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
tempString = IntSwitchToString(channel.selfTrigger);
if (ImGui::BeginCombo(fmt::format("##selfTrig_{0}", i).c_str(), tempString.c_str()))
{
if (ImGui::Selectable("On", "On" == tempString))
{
changed = true;
channel.selfTrigger = 1;
}
if (ImGui::Selectable("Off", "Off" == tempString))
{
changed = true;
channel.selfTrigger = 0;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = ChargeSensToString(channel.chargeSensitivity, channel.dynamicRange);
if (ImGui::BeginCombo(fmt::format("##charge_sens{0}", i).c_str(), tempString.c_str()))
{
for (int val = 0; val < 6; val++)
{
if (ImGui::Selectable(ChargeSensToString(val, channel.dynamicRange).c_str(), val == channel.chargeSensitivity))
{
changed = true;
channel.chargeSensitivity = val;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##shortGate_{0}", i).c_str(), &channel.shortGate, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##longGate_{0}", i).c_str(), &channel.longGate, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##preGate_{0}", i).c_str(), &channel.preGate, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##trigValidWindow_{0}", i).c_str(), &channel.triggerValidationWindow, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
tempString = channel.samplesBasline == 0 ? "Absolute" : std::to_string(Power(4, 1 + channel.samplesBasline));
if (ImGui::BeginCombo(fmt::format("##samplesBase_{0}", i).c_str(), tempString.c_str()))
{
int val;
if (ImGui::Selectable("Absolute", channel.samplesBasline == 0))
{
changed = true;
channel.samplesBasline = 0;
}
for (int pow = 1; pow < 5; pow++)
{
val = Power(4, pow + 1);
if (ImGui::Selectable(std::to_string(val).c_str(), pow == channel.samplesBasline))
{
changed = true;
channel.samplesBasline = pow;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = DiscriminatorModeToString(channel.discrminatorType);
if (ImGui::BeginCombo(fmt::format("##discrMode_{0}", i).c_str(), tempString.c_str()))
{
if (ImGui::Selectable("CFD", CAEN_DGTZ_DPP_DISCR_MODE_CFD == channel.discrminatorType))
{
changed = true;
channel.discrminatorType = CAEN_DGTZ_DPP_DISCR_MODE_CFD;
}
if (ImGui::Selectable("Leading-Edge", CAEN_DGTZ_DPP_DISCR_MODE_LED == channel.discrminatorType))
{
changed = true;
channel.discrminatorType = CAEN_DGTZ_DPP_DISCR_MODE_LED;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = CFDFractionToString(channel.cfdFraction);
if (ImGui::BeginCombo(fmt::format("##cfdFrac_{0}", i).c_str(), tempString.c_str()))
{
for (int val = 0; val < 4; val++)
{
if (ImGui::Selectable(CFDFractionToString(val).c_str(), val = channel.cfdFraction))
{
changed = true;
channel.cfdFraction = val;
}
}
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##cfdDelay_{0}", i).c_str(), &channel.cfdDelay, 0, 0))
{
changed = true;
}
ImGui::TableNextColumn();
tempString = PileUpModeToString(channel.pileUpRejection);
if (ImGui::BeginCombo(fmt::format("##purSwitch_{0}", i).c_str(), tempString.c_str()))
{
if (ImGui::Selectable("On", CAEN_DGTZ_DPP_PSD_PUR_Enabled == channel.pileUpRejection))
{
changed = true;
channel.discrminatorType = CAEN_DGTZ_DPP_PSD_PUR_Enabled;
}
if (ImGui::Selectable("Off", CAEN_DGTZ_DPP_PSD_PUR_DetectOnly == channel.pileUpRejection))
{
changed = true;
channel.discrminatorType = CAEN_DGTZ_DPP_PSD_PUR_DetectOnly;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (ImGui::InputInt(fmt::format("##purityGap_{0}", i).c_str(), &channel.purgap, 0, 0))
{
changed = true;
}
if (!channel.isEnabled)
ImGui::EndDisabled();
}
ImGui::EndTable();
}
if (!m_digitizerEnabled)
ImGui::EndDisabled();
return changed;
}
bool DigitizerPanel::RenderPHAWaveParameters()
{
bool changed = false;
static std::string tempString;
if (!m_digitizerEnabled)
ImGui::BeginDisabled();
if (ImGui::BeginTable("PHAWaveParams", 3, tableFlags))
{
ImGui::TableSetupColumn("VirtualProbe1");
ImGui::TableSetupColumn("VirtualProbe2");
ImGui::TableSetupColumn("DigitalProbe");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
tempString = PHAVirtualProbe1ToString(m_phaWaves.analogProbe1);
if (ImGui::BeginCombo("##Vprobe1", tempString.c_str()))
{
if (ImGui::Selectable("Input", m_phaWaves.analogProbe1 == PHAVP1_Input))
{
changed = true;
m_phaWaves.analogProbe1 = PHAVP1_Input;
}
if (ImGui::Selectable("Trapezoid", m_phaWaves.analogProbe1 == PHAVP1_Trapezoid))
{
changed = true;
m_phaWaves.analogProbe1 = PHAVP1_Trapezoid;
}
if (ImGui::Selectable("Delta", m_phaWaves.analogProbe1 == PHAVP1_Delta))
{
changed = true;
m_phaWaves.analogProbe1 = PHAVP1_Delta;
}
if (ImGui::Selectable("Delta2", m_phaWaves.analogProbe1 == PHAVP1_Delta2))
{
changed = true;
m_phaWaves.analogProbe1 = PHAVP1_Delta2;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = PHAVirtualProbe2ToString(m_phaWaves.analogProbe2);
if (ImGui::BeginCombo("##Vprobe2", tempString.c_str()))
{
if (ImGui::Selectable("Input", m_phaWaves.analogProbe2 == PHAVP2_Input))
{
changed = true;
m_phaWaves.analogProbe2 = PHAVP2_Input;
}
if (ImGui::Selectable("Baseline", m_phaWaves.analogProbe2 == PHAVP2_Baseline))
{
changed = true;
m_phaWaves.analogProbe2 = PHAVP2_Baseline;
}
if (ImGui::Selectable("Threshold", m_phaWaves.analogProbe2 == PHAVP2_Threshold))
{
changed = true;
m_phaWaves.analogProbe2 = PHAVP2_Threshold;
}
if (ImGui::Selectable("Trap. Reduced", m_phaWaves.analogProbe2 == PHAVP2_TrapezoidReduced))
{
changed = true;
m_phaWaves.analogProbe2 = PHAVP2_TrapezoidReduced;
}
if (ImGui::Selectable("None", m_phaWaves.analogProbe2 == PHAVP2_None))
{
changed = true;
m_phaWaves.analogProbe2 = PHAVP2_None;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = PHADigitalProbeToString(m_phaWaves.digitalProbe1);
if (ImGui::BeginCombo("##Dprobe1", tempString.c_str()))
{
if (ImGui::Selectable("Acq. Veto", m_phaWaves.digitalProbe1 == PHADP_ACQVeto))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_ACQVeto;
}
if (ImGui::Selectable("Armed", m_phaWaves.digitalProbe1 == PHADP_Armed))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_Armed;
}
if (ImGui::Selectable("Baseline Freeze", m_phaWaves.digitalProbe1 == PHADP_BaselineFreeze))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_BaselineFreeze;
}
if (ImGui::Selectable("BFM Veto", m_phaWaves.digitalProbe1 == PHADP_BFMVeto))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_BFMVeto;
}
if (ImGui::Selectable("Busy", m_phaWaves.digitalProbe1 == PHADP_Busy))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_Busy;
}
if (ImGui::Selectable("Coincidence Window", m_phaWaves.digitalProbe1 == PHADP_CoincidenceWindow))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_CoincidenceWindow;
}
if (ImGui::Selectable("Ext. Trigger", m_phaWaves.digitalProbe1 == PHADP_ExternalTrig))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_ExternalTrig;
}
if (ImGui::Selectable("Peaking", m_phaWaves.digitalProbe1 == PHADP_Peaking))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_Peaking;
}
if (ImGui::Selectable("PileUp", m_phaWaves.digitalProbe1 == PHADP_PileUp))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_PileUp;
}
if (ImGui::Selectable("PrgVeto", m_phaWaves.digitalProbe1 == PHADP_PrgVeto))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_PrgVeto;
}
if (ImGui::Selectable("Trig. Hold-Off", m_phaWaves.digitalProbe1 == PHADP_TriggerHoldoff))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_TriggerHoldoff;
}
if (ImGui::Selectable("Trig. Value", m_phaWaves.digitalProbe1 == PHADP_TriggerValue))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_TriggerValue;
}
if (ImGui::Selectable("Trig. Window", m_phaWaves.digitalProbe1 == PHADP_TriggerWindow))
{
changed = true;
m_phaWaves.digitalProbe1 = PHADP_TriggerWindow;
}
ImGui::EndCombo();
}
ImGui::EndTable();
}
if (!m_digitizerEnabled)
ImGui::EndDisabled();
return changed;
}
bool DigitizerPanel::RenderPSDWaveParameters()
{
bool changed = false;
static std::string tempString;
if (!m_digitizerEnabled)
ImGui::BeginDisabled();
if (ImGui::BeginTable("PSDWaveParams", 4, tableFlags))
{
ImGui::TableSetupColumn("VirtualProbe1");
ImGui::TableSetupColumn("VirtualProbe2");
ImGui::TableSetupColumn("DigitalProbe1");
ImGui::TableSetupColumn("DigitalProbe2");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
tempString = PSDVirtualProbe1ToString(m_psdWaves.analogProbe1);
if (ImGui::BeginCombo("##virtprobe1", tempString.c_str()))
{
if (ImGui::Selectable("Input", m_psdWaves.analogProbe1 == PSDVP1_Input))
{
changed = true;
m_psdWaves.analogProbe1 = PSDVP1_Input;
}
if (ImGui::Selectable("CFD", m_psdWaves.analogProbe1 == PSDVP1_CFD))
{
changed = true;
m_psdWaves.analogProbe1 = PSDVP1_CFD;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = PSDVirtualProbe2ToString(m_psdWaves.analogProbe2);
if (ImGui::BeginCombo("##virtprobe2", tempString.c_str()))
{
if (ImGui::Selectable("Baseline", m_psdWaves.analogProbe2 == PSDVP2_Baseline))
{
changed = true;
m_psdWaves.analogProbe2 = PSDVP2_Baseline;
}
if (ImGui::Selectable("CFD", m_psdWaves.analogProbe2 == PSDVP2_CFD))
{
changed = true;
m_psdWaves.analogProbe2 = PSDVP2_CFD;
}
if (ImGui::Selectable("None", m_psdWaves.analogProbe2 == PSDVP2_None))
{
changed = true;
m_psdWaves.analogProbe2 = PSDVP2_None;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = PSDDigitalProbe1ToString(m_psdWaves.digitalProbe1);
if (ImGui::BeginCombo("##digiprobe1", tempString.c_str()))
{
if (ImGui::Selectable("Coincidence", m_psdWaves.digitalProbe1 == PSDDP1_Coincidence))
{
changed = true;
m_psdWaves.digitalProbe1 = PSDDP1_Coincidence;
}
if (ImGui::Selectable("Coincidence Window", m_psdWaves.digitalProbe1 == PSDDP1_CoincidenceWindow))
{
changed = true;
m_psdWaves.digitalProbe1 = PSDDP1_CoincidenceWindow;
}
if (ImGui::Selectable("Gate", m_psdWaves.digitalProbe1 == PSDDP1_Gate))
{
changed = true;
m_psdWaves.digitalProbe1 = PSDDP1_Gate;
}
if (ImGui::Selectable("Over Threshold", m_psdWaves.digitalProbe1 == PSDDP1_OverThreshold))
{
changed = true;
m_psdWaves.digitalProbe1 = PSDDP1_OverThreshold;
}
if (ImGui::Selectable("PileUp", m_psdWaves.digitalProbe1 == PSDDP1_PileUp))
{
changed = true;
m_psdWaves.digitalProbe1 = PSDDP1_PileUp;
}
if (ImGui::Selectable("Trigger", m_psdWaves.digitalProbe1 == PSDDP1_Trigger))
{
changed = true;
m_psdWaves.digitalProbe1 = PSDDP1_Trigger;
}
if (ImGui::Selectable("TriggerOut", m_psdWaves.digitalProbe1 == PSDDP1_TriggerOut))
{
changed = true;
m_psdWaves.digitalProbe1 = PSDDP1_TriggerOut;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
tempString = PSDDigitalProbe2ToString(m_psdWaves.digitalProbe2);
if (ImGui::BeginCombo("##digiprobe2", tempString.c_str()))
{
if (ImGui::Selectable("Coincidence", m_psdWaves.digitalProbe2 == PSDDP2_Coincidence))
{
changed = true;
m_psdWaves.digitalProbe2 = PSDDP2_Coincidence;
}
if (ImGui::Selectable("Short Gate", m_psdWaves.digitalProbe2 == PSDDP2_GateShort))
{
changed = true;
m_psdWaves.digitalProbe2 = PSDDP2_GateShort;
}
if (ImGui::Selectable("Over Threshold", m_psdWaves.digitalProbe2 == PSDDP2_OverThreshold))
{
changed = true;
m_psdWaves.digitalProbe2 = PSDDP2_OverThreshold;
}
if (ImGui::Selectable("PileUp", m_psdWaves.digitalProbe2 == PSDDP2_PileUp))
{
changed = true;
m_psdWaves.digitalProbe2 = PSDDP2_PileUp;
}
if (ImGui::Selectable("Trigger", m_psdWaves.digitalProbe2 == PSDDP2_Trigger))
{
changed = true;
m_psdWaves.digitalProbe2 = PSDDP2_Trigger;
}
if (ImGui::Selectable("Trig. Hold-Off", m_psdWaves.digitalProbe2 == PSDDP2_TriggerHoldoff))
{
changed = true;
m_psdWaves.digitalProbe2 = PSDDP2_TriggerHoldoff;
}
if (ImGui::Selectable("Trig. Value", m_psdWaves.digitalProbe2 == PSDDP2_TriggerVal))
{
changed = true;
m_psdWaves.digitalProbe2 = PSDDP2_TriggerVal;
}
ImGui::EndCombo();
}
ImGui::EndTable();
}
if (!m_digitizerEnabled)
ImGui::EndDisabled();
return changed;
}
}

View File

@ -0,0 +1,64 @@
#ifndef DIGITIZER_PANEL_H
#define DIGITIZER_PANEL_H
#include "DAQ/DigitizerDefs.h"
#include "imgui.h"
namespace Daqromancy {
class DigitizerPanel
{
public:
enum class Type
{
PHA,
PSD,
None
};
DigitizerPanel(const DigitizerArgs& args);
~DigitizerPanel();
void SetDigitizerParameters(const DigitizerParameters& params);
void SetPHAParameters(const std::vector<PHAParameters>& ch_params, const PHAWaveParameters& wv_params);
void SetPSDParameters(const std::vector<PSDParameters>& ch_params, const PSDWaveParameters& wv_params);
//Retrieve settings
const DigitizerParameters& GetDigitizerParameters() const { return m_params; }
const std::vector<PHAParameters>& GetPHAChannelParameters() const { return m_phaChannels; }
const std::vector<PSDParameters>& GetPSDChannelParameters() const { return m_psdChannels; }
const PHAWaveParameters& GetPHAWaveParameters() const { return m_phaWaves; }
const PSDWaveParameters& GetPSDWaveParameters() const { return m_psdWaves; }
Type GetPanelType() const { return m_panelType; }
int GetDigitizerHandle() { return m_args.handle; }
bool OnImGuiRender();
private:
void Init();
bool RenderDigitizerParams();
bool RenderPHAParameters();
bool RenderPSDParameters();
bool RenderPHAWaveParameters();
bool RenderPSDWaveParameters();
DigitizerArgs m_args;
DigitizerParameters m_params;
std::vector<PHAParameters> m_phaChannels;
std::vector<PSDParameters> m_psdChannels;
PHAWaveParameters m_phaWaves;
PSDWaveParameters m_psdWaves;
Type m_panelType;
std::string m_panelName;
std::string m_settingsFile;
bool m_digitizerEnabled;
const ImGuiTableFlags tableFlags = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Hideable;
};
}
#endif

364
src/Editor/EditorLayer.cpp Normal file
View File

@ -0,0 +1,364 @@
#include "EditorLayer.h"
#include "Core/Application.h"
#include "Events/AcqEvent.h"
#include "Core/ProjectSerializer.h"
#include "misc/cpp/imgui_stdlib.h"
#include <filesystem>
namespace Daqromancy {
EditorLayer::EditorLayer(const DYProject::Ref& project) :
Layer("EditorLayer"), m_project(project), m_scopePanel(nullptr)
{
m_projectPath = m_project->GetProjectPath().string();
m_runNumber = m_project->GetRunNumber();
m_digitizerArgList = m_project->GetDigitizerArgsList();
//temp
m_digiPanels.emplace_back(DigitizerArgs());
}
EditorLayer::~EditorLayer()
{
delete m_scopePanel;
}
void EditorLayer::OnAttach() {}
void EditorLayer::OnDetach() {}
void EditorLayer::OnUpdate()
{
if (m_scopePanel)
m_scopePanel->OnUpdate();
}
void EditorLayer::OnEvent(Event& e)
{
}
void EditorLayer::UpdateDigitizerPanels()
{
if (m_project->GetNumberOfBoards() != m_digiPanels.size())
{
m_digitizerArgList = m_project->GetDigitizerArgsList();
m_digiPanels.clear();
for (auto& args : m_digitizerArgList)
m_digiPanels.emplace_back(args);
}
std::vector<DigitizerParameters> boardParams = m_project->GetDigitizerParameterList();
int handle;
for (auto& panel : m_digiPanels)
{
handle = panel.GetDigitizerHandle();
if (handle == -1)
continue;
panel.SetDigitizerParameters(boardParams[handle]);
switch (panel.GetPanelType())
{
case DigitizerPanel::Type::PHA: panel.SetPHAParameters(m_project->GetPHAParameters(handle), m_project->GetPHAWaveParameters(handle)); break;
case DigitizerPanel::Type::PSD: panel.SetPSDParameters(m_project->GetPSDParameters(handle), m_project->GetPSDWaveParameters(handle)); break;
case DigitizerPanel::Type::None: break;
}
}
}
void EditorLayer::OnImGuiRender(double timestep)
{
static uint32_t stepSize = 1;
static uint32_t fastStepSize = 5;
static bool autoIncrFlag = true;
static std::string dppModeString = "";
static bool dataServer = false;
static bool writeDataToDisk = false;
static bool showScalars = false;
static std::string runString = "Start Run";
static bool disableAll = false;
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
// because it would be confusing to have two docking targets within each others.
if (opt_fullscreen)
{
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
}
// When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background.
if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
window_flags |= ImGuiWindowFlags_NoBackground;
// Important: note that we proceed even if Begin() returns false (aka window is collapsed).
// This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
// all active windows docked into it will lose their parent and become undocked.
// We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
// any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("MyTestSpace", &dockspaceOpen, window_flags);
ImGui::PopStyleVar();
if (opt_fullscreen)
ImGui::PopStyleVar(2);
// DockSpace
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
float minWinSizeX = style.WindowMinSize.x;
style.WindowMinSize.x = 370.0f;
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
{
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
}
style.WindowMinSize.x = minWinSizeX;
if (disableAll)
ImGui::BeginDisabled();
//Menu bar
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("File"))
{
if (ImGui::MenuItem("Open Project..."))
{
m_fileDialog.OpenDialog(FileDialog::Type::OpenDir);
}
if (ImGui::MenuItem("Save Project"))
{
SaveProject();
}
if (ImGui::MenuItem("Save Project As..."))
{
m_fileDialog.OpenDialog(FileDialog::Type::SaveDir);
}
if (ImGui::MenuItem("Exit"))
{
WindowCloseEvent wc_event;
m_eventCallback(wc_event);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Digitizer"))
{
if (ImGui::MenuItem("Scan for boards..."))
{
AcqDetectBoardsEvent db_event;
m_eventCallback(db_event);
UpdateDigitizerPanels();
}
if (ImGui::MenuItem("Disconnect boards..."))
{
AcqDisconnectBoardsEvent db_event;
m_eventCallback(db_event);
UpdateDigitizerPanels();
}
if (ImGui::MenuItem("Synchronize boards..."))
{
m_syncDialog.OpenDialog();
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
if (disableAll)
ImGui::EndDisabled();
//End of menu bar
//Begin main ImGui window
if (ImGui::Begin("Controls"))
{
//Begin internal tab bar
if (ImGui::BeginTabBar("BS_TabBar", ImGuiTabBarFlags_None))
{
if (ImGui::BeginTabItem("Project"))
{
if (ImGui::Button(runString.c_str()))
{
if (runString == "Start Run")
{
AcqStartEvent e(writeDataToDisk, dataServer);
m_eventCallback(e);
runString = "Stop Run";
disableAll = true;
}
else if (runString == "Stop Run")
{
AcqStopEvent e;
m_eventCallback(e);
runString = "Start Run";
disableAll = false;
}
}
if (disableAll)
ImGui::BeginDisabled();
ImGui::Text("Project Directory: ");
ImGui::SameLine();
ImGui::Text("%s", m_projectPath.c_str());
ImGui::InputScalar("Run Number", ImGuiDataType_U32, &m_runNumber, &stepSize, &fastStepSize);
ImGui::SameLine();
ImGui::Checkbox("Auto-increment", &autoIncrFlag);
if (ImGui::BeginCombo("DPP Acquisition Mode", dppModeString.c_str()))
{
if (ImGui::Selectable("List", dppModeString == "List"))
{
dppModeString = "List";
m_project->SetDPPAcqMode(DPPAcqMode::List);
AcqDPPModeEvent e;
m_eventCallback(e);
delete m_scopePanel;
m_scopePanel = nullptr;
}
if (ImGui::Selectable("Waves", dppModeString == "Waves"))
{
dppModeString = "Waves";
m_project->SetDPPAcqMode(DPPAcqMode::Waves);
AcqDPPModeEvent e;
m_eventCallback(e);
m_scopePanel = new ScopePanel(m_digiPanels.size());
}
ImGui::EndCombo();
}
//These don't really do anything right now, to be implemented
ImGui::Checkbox("Write data to disk", &writeDataToDisk);
ImGui::Checkbox("Write data to server", &dataServer);
if (ImGui::Checkbox("Show Scalars", &showScalars))
{
if (showScalars)
m_scalarPanel.ResetScalars();
}
ImGui::EndTabItem();
}
bool panel_status;
for (auto& panel : m_digiPanels)
{
panel_status = panel.OnImGuiRender();
//handle state change
if (panel_status)
{
//Update project
m_project->SetDigitizerParameters(panel.GetDigitizerHandle(), panel.GetDigitizerParameters());
if (panel.GetPanelType() == DigitizerPanel::Type::PHA)
{
m_project->SetPHAParameters(panel.GetDigitizerHandle(), panel.GetPHAChannelParameters());
m_project->SetPHAWaveParameters(panel.GetDigitizerHandle(), panel.GetPHAWaveParameters());
}
else if (panel.GetPanelType() == DigitizerPanel::Type::PSD)
{
m_project->SetPSDParameters(panel.GetDigitizerHandle(), panel.GetPSDChannelParameters());
m_project->SetPSDWaveParameters(panel.GetDigitizerHandle(), panel.GetPSDWaveParameters());
}
else
{
DY_WARN("Unidentified digitizer type attempting to communicate with project");
}
//Emit event to update aquisition side
AcqParametersEvent e(DigitizerAccessType::Single, panel.GetDigitizerHandle());
m_eventCallback(e);
}
}
ImGui::EndTabBar();
}
//End internal tab bar
if (disableAll)
ImGui::EndDisabled();
}
ImGui::End();
//Render the scope if needed
if (m_scopePanel)
m_scopePanel->OnImGuiRender();
if (showScalars)
m_scalarPanel.OnImGuiRender(m_digitizerArgList, timestep);
//Render file dialog if needed
auto fd_result = m_fileDialog.RenderFileDialog(".yaml");
if (!fd_result.first.empty())
{
switch (fd_result.second)
{
case FileDialog::Type::OpenDir:
{
std::filesystem::path dir(fd_result.first);
if (!std::filesystem::exists(dir / "settings.yaml"))
{
DY_ERROR("Project settings file {0} does not exist! Cannot be opened!", dir.string());
break;
}
ProjectSerializer serializer(dir / "settings.yaml");
serializer.DeserializeData(m_project);
m_projectPath = m_project->GetProjectPath().string();
UpdateDigitizerPanels();
AcqParametersEvent e(DigitizerAccessType::All);
m_eventCallback(e);
break;
}
case FileDialog::Type::SaveDir:
{
bool status = m_project->SetProjectPath(fd_result.first);
std::filesystem::path path(fd_result.first);
if (!status)
{
DY_ERROR("Project directory {0} could not be created! Settings not saved.", fd_result.first);
break;
}
ProjectSerializer serializer(m_project->GetProjectPath() / "settings.yaml");
serializer.SerializeData(m_project);
m_projectPath = m_project->GetProjectPath().string();
break;
}
case FileDialog::Type::OpenFile: break; //Unused
case FileDialog::Type::SaveFile: break; //Unused
case FileDialog::Type::None: break; //Null result
}
}
//render sync dialog if needed
bool test = m_syncDialog.OnImGuiRender();
if (test)
{
AcqSyncArgsEvent e = m_syncDialog.GetSyncEvent();
m_eventCallback(e);
UpdateDigitizerPanels();
}
ImGui::End();
}
void EditorLayer::SaveProject()
{
const std::filesystem::path& path = m_project->GetProjectPath();
if (!path.empty())
{
ProjectSerializer serializer(m_project->GetProjectPath() / "settings.yaml");
serializer.SerializeData(m_project);
}
else
m_fileDialog.OpenDialog(FileDialog::Type::SaveDir);
}
}

60
src/Editor/EditorLayer.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef EDITOR_LAYER_H
#define EDITOR_LAYER_H
#include "Core/DYCore.h"
#include "Core/Layer.h"
#include "FileDialog.h"
#include "Events/AcqEvent.h"
#include "Core/DYProject.h"
#include "DigitizerPanel.h"
#include "SyncDialog.h"
#include "ScopePanel.h"
#include "ScalarPanel.h"
#include "imgui.h"
namespace Daqromancy {
class EditorLayer : public Layer
{
public:
using EventCallbackFunc = std::function<void(Event&)>;
EditorLayer(const DYProject::Ref& project);
~EditorLayer();
void SetEventCallback(const EventCallbackFunc& func) { m_eventCallback = func; }
virtual void OnAttach() override;
virtual void OnDetach() override;
virtual void OnUpdate() override;
virtual void OnEvent(Event& e) override;
virtual void OnImGuiRender(double timestep) override;
private:
void UpdateDigitizerPanels();
void SaveProject(); //For call to save project to current path
EventCallbackFunc m_eventCallback;
DYProject::Ref m_project;
std::string m_projectPath;
uint32_t m_runNumber;
std::vector<DigitizerArgs> m_digitizerArgList;
FileDialog m_fileDialog;
std::vector<DigitizerPanel> m_digiPanels;
SyncDialog m_syncDialog;
ScopePanel* m_scopePanel;
ScalarPanel m_scalarPanel;
//ImGui Settings
bool dockspaceOpen = true;
bool opt_fullscreen = true;
bool opt_fullscreen_persistant = true;
ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
};
}
#endif

281
src/Editor/FileDialog.cpp Normal file
View File

@ -0,0 +1,281 @@
#include "FileDialog.h"
#include "misc/cpp/imgui_stdlib.h"
namespace Daqromancy {
//Helper function to handle file size printing
std::string ConvertFileSystemSizeToString(std::uintmax_t value)
{
int i = 0;
double mantissa = (double)value;
for (; mantissa >= 1024.0; ++i)
mantissa /= 1024.0;
mantissa = std::ceil(mantissa * 10.0) / 10.0;
return std::to_string(int(mantissa)) + "BKMGTPE"[i];
}
FileDialog::FileDialog() :
m_currentPath(std::filesystem::current_path()), m_type(Type::None), m_selectedItem(""), m_openDialogFlag(false)
{
table_flags = ImGuiTableFlags_BordersH | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_RowBg;
select_flags = ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_DontClosePopups;
}
FileDialog::~FileDialog() {}
//Each type of action has its own render function
std::pair<std::string, FileDialog::Type> FileDialog::RenderFileDialog(const std::string& ext)
{
if (m_openDialogFlag)
{
m_selectedItem = "";
m_openDialogFlag = false;
m_currentPath = std::filesystem::current_path();
ImGui::OpenPopup("File Dialog");
}
//Open & Save Dir are equivalent operations, but can be useful to specify for handling later
std::string result = "";
if (ImGui::BeginPopupModal("File Dialog"))
{
switch (m_type)
{
case Type::OpenFile:
{
result = ImGuiRenderOpenFile(ext);
break;
}
case Type::SaveFile:
{
result = ImGuiRenderSaveFile(ext);
break;
}
case Type::OpenDir:
{
result = ImGuiRenderOpenDir();
break;
}
case Type::SaveDir:
{
result = ImGuiRenderOpenDir();
break;
}
case Type::None: break;
}
ImGui::EndPopup();
}
return std::make_pair(result, m_type);
}
std::string FileDialog::ImGuiRenderOpenFile(const std::string& ext)
{
std::string result = "";
std::string text = "";
ImGui::Text("%s", ("Current Directory: " + m_currentPath.lexically_normal().string()).c_str());
ImGui::SameLine();
ImGui::Text("%s", ("Extension Filter: " + ext).c_str());
ImGui::InputText("Selected", &m_selectedItem);
if (ImGui::Button("Ok"))
{
std::filesystem::path filepath = m_currentPath / m_selectedItem;
result = filepath.string();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
ImGui::CloseCurrentPopup();
if (ImGui::BeginTable("File System", 2, table_flags, ImVec2(-1, -1)))
{
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Size");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(" ..", false, select_flags))
{
m_selectedItem.clear();
m_currentPath.append("..");
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
for (auto& entry : std::filesystem::directory_iterator(m_currentPath, std::filesystem::directory_options::skip_permission_denied))
{
if (entry.is_directory())
{
try //Windows C:/ errors can only be try caught.
{
text = std::filesystem::relative(entry.path(), m_currentPath).string();
}
catch (std::exception& e)
{
continue;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
{
m_selectedItem.clear();
m_currentPath /= entry.path();
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
}
else if (entry.path().filename().extension() == ext)
{
ImGui::TableNextRow();
text = entry.path().filename().string();
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
m_selectedItem = entry.path().filename().string();
ImGui::TableNextColumn();
ImGui::Text("%s", ConvertFileSystemSizeToString(entry.file_size()).c_str());
}
}
ImGui::EndTable();
}
return result;
}
std::string FileDialog::ImGuiRenderSaveFile(const std::string& ext)
{
std::string result = "";
std::string text = "";
ImGui::Text("%s", ("Current Directory: " + m_currentPath.lexically_normal().string()).c_str());
ImGui::SameLine();
ImGui::Text("%s", ("Extension Filter: " + ext).c_str());
ImGui::InputText("Selected", &m_selectedItem);
if (ImGui::Button("Ok"))
{
std::filesystem::path filepath = m_currentPath / m_selectedItem;
result = filepath.string();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
ImGui::CloseCurrentPopup();
if (ImGui::BeginTable("File System", 2, table_flags, ImVec2(-1, -1)))
{
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Size");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(" ..", false, select_flags))
{
m_selectedItem.clear();
m_currentPath.append("..");
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
for (auto& entry : std::filesystem::directory_iterator(m_currentPath, std::filesystem::directory_options::skip_permission_denied))
{
if (entry.is_directory())
{
try //Windows C:/ errors can only be try caught.
{
text = std::filesystem::relative(entry.path(), m_currentPath).string();
}
catch (std::exception& e)
{
continue;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
{
m_selectedItem.clear();
m_currentPath /= entry.path();
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
}
else if (entry.path().filename().extension() == ext)
{
ImGui::TableNextRow();
text = entry.path().filename().string();
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
m_selectedItem = entry.path().filename().string();
ImGui::TableNextColumn();
ImGui::Text("%s", ConvertFileSystemSizeToString(entry.file_size()).c_str());
}
}
ImGui::EndTable();
}
return result;
}
std::string FileDialog::ImGuiRenderOpenDir()
{
std::string result = "";
std::string text = "";
ImGui::Text("%s", ("Current Directory: " + m_currentPath.lexically_normal().string()).c_str());
ImGui::InputText("Selected", &m_selectedItem);
if (ImGui::Button("Ok"))
{
result = m_selectedItem;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
ImGui::CloseCurrentPopup();
if (ImGui::BeginTable("File System", 2, table_flags, ImVec2(-1, -1)))
{
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Size");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(" ..", false, select_flags))
{
m_currentPath.append("..");
m_selectedItem = m_currentPath.string();
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
for (auto& entry : std::filesystem::directory_iterator(m_currentPath, std::filesystem::directory_options::skip_permission_denied))
{
ImGui::TableNextRow();
if (entry.is_directory())
{
try //Windows C:/ errors can only be try caught.
{
text = std::filesystem::relative(entry.path(), m_currentPath).string();
}
catch (std::exception& e)
{
continue;
}
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
{
m_currentPath /= entry.path();
m_selectedItem = m_currentPath.string();
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
}
else
{
text = entry.path().filename().string();
ImGui::TableNextColumn();
ImGui::Text("%s", text.c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", ConvertFileSystemSizeToString(entry.file_size()).c_str());
}
}
ImGui::EndTable();
}
return result;
}
}

46
src/Editor/FileDialog.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef FILE_DIALOG_H
#define FILE_DIALOG_H
#include <filesystem>
#include "imgui.h"
namespace Daqromancy {
class FileDialog
{
public:
enum class Type
{
OpenFile,
SaveFile,
OpenDir,
SaveDir,
None
};
FileDialog();
~FileDialog();
inline void OpenDialog(Type type) { m_type = type; m_openDialogFlag = true; }
std::pair<std::string, Type> RenderFileDialog(const std::string& ext = "");
private:
std::string ImGuiRenderOpenFile(const std::string& ext);
std::string ImGuiRenderSaveFile(const std::string& ext);
std::string ImGuiRenderOpenDir();
std::filesystem::path m_currentPath;
Type m_type;
std::string m_selectedItem;
bool m_openDialogFlag;
ImGuiTableFlags table_flags;
ImGuiSelectableFlags select_flags;
};
}
#endif

View File

@ -0,0 +1,71 @@
#include "ScalarPanel.h"
#include "imgui.h"
namespace Daqromancy {
ScalarPanel::ScalarPanel() :
m_elapsedTime(0.0)
{
}
ScalarPanel::~ScalarPanel()
{
}
void ScalarPanel::OnImGuiRender(const std::vector<DigitizerArgs>& argList, double timestep)
{
static bool changedBoard = false;
m_elapsedTime += timestep;
if (m_elapsedTime >= 60.0f) //Update the rates once a minute
{
ScalarDistributor::CalculateRates(m_elapsedTime);
m_elapsedTime = 0.0f; //Reset it
}
if (ImGui::Begin("ScalarPanel"))
{
if (ImGui::BeginCombo("Selected Board", m_selectedArgs.name.c_str()))
{
for (auto& args : argList)
{
if (ImGui::Selectable(args.name.c_str(), m_selectedArgs.name == args.name))
{
m_selectedArgs = args;
changedBoard = true;
}
}
ImGui::EndCombo();
}
if (changedBoard)
{
m_scalarNames.resize(m_selectedArgs.channels);
for (int i = 0; i < m_selectedArgs.channels; i++)
m_scalarNames[i] = m_selectedArgs.name + std::to_string(i);
changedBoard = false;
}
if (ImGui::BeginTable("ScalarTable", 2))
{
ImGui::TableSetupColumn("Channel Number");
ImGui::TableSetupColumn("Throughput Rate");
ImGui::TableHeadersRow();
for (int i = 0; i < m_selectedArgs.channels; i++)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%d", i);
ImGui::TableNextColumn();
ImGui::Text("%f", ScalarDistributor::GetRate(m_scalarNames[i]));
}
ImGui::EndTable();
}
}
ImGui::End();
}
}

27
src/Editor/ScalarPanel.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef SCALAR_PANEL_H
#define SCALAR_PANEL_H
#include "Core/ScalarDistributor.h"
#include "DAQ/DigitizerDefs.h"
namespace Daqromancy {
class ScalarPanel
{
public:
ScalarPanel();
~ScalarPanel();
void ResetScalars() { ScalarDistributor::ClearScalars(); m_elapsedTime = 0.0; }
void OnImGuiRender(const std::vector<DigitizerArgs>& argList, double timestep);
private:
DigitizerArgs m_selectedArgs;
std::vector<std::string> m_scalarNames;
double m_elapsedTime;
};
}
#endif

90
src/Editor/ScopePanel.cpp Normal file
View File

@ -0,0 +1,90 @@
#include "ScopePanel.h"
#include "imgui.h"
#include "implot.h"
namespace Daqromancy {
ScopePanel::ScopePanel(int nboards) :
m_dataReady(false), m_selectedBoard(0), m_selectedChannel(0), m_maxNumBoards(nboards)
{
m_dataHandle = DataDistributor::Connect();
for (int i = 0; i < nboards; i++)
m_boardListForImGui.push_back(fmt::format("{0}", i));
for (int i = 0; i < 16; i++) //bad hardcode, fix later
m_channelListForImGui.push_back(fmt::format("{0}", i));
}
ScopePanel::~ScopePanel()
{
DataDistributor::Disconnect(m_dataHandle);
}
void ScopePanel::OnUpdate()
{
if (!m_dataHandle.dataQueue->IsEmpty())
{
m_buffer = m_dataHandle.dataQueue->Front();
m_dataHandle.dataQueue->PopFront();
for (auto& hit : m_buffer)
{
if (hit.board == m_selectedBoard && hit.channel == m_selectedChannel)
{
m_selectedHit = hit;
m_selectedXAxis.clear();
for (int i = 0; i < hit.waveSize; i++)
m_selectedXAxis.push_back(i);
break;
}
}
}
}
void ScopePanel::OnImGuiRender()
{
static std::string selectedBoardString = fmt::format("{0}", m_selectedBoard);
static std::string selectedChannelString = fmt::format("{0}", m_selectedChannel);
if (ImGui::Begin("Oscilloscope"))
{
if (ImGui::BeginCombo("Board", selectedBoardString.c_str()))
{
for (int board=0; board<m_maxNumBoards; board++)
{
if (ImGui::Selectable(m_boardListForImGui[board].c_str(), board == m_selectedBoard))
{
m_selectedBoard = board;
selectedBoardString = fmt::format("{0}", m_selectedBoard);
m_selectedXAxis.clear();
}
}
ImGui::EndCombo();
}
if (ImGui::BeginCombo("Channel", selectedChannelString.c_str()))
{
for (int channel = 0; channel < 16; channel++) //hardcoded bad, fix later
{
if (ImGui::Selectable(m_channelListForImGui[channel].c_str(), channel == m_selectedChannel))
{
m_selectedChannel = channel;
selectedChannelString = fmt::format("{0}", m_selectedChannel);
m_selectedXAxis.clear();
}
}
ImGui::EndCombo();
}
if (ImPlot::BeginPlot("ScopeView", ImVec2(-1,-1)))
{
if (m_selectedXAxis.size() != 0)
{
ImPlot::PlotLine("AnalogProbe1", (ImU16*)m_selectedXAxis.data(), (ImU16*)m_selectedHit.trace1Samples.data(), m_selectedXAxis.size());
ImPlot::PlotLine("AnalogProbe2", (ImU16*)m_selectedXAxis.data(), (ImU16*)m_selectedHit.trace2Samples.data(), m_selectedXAxis.size());
ImPlot::PlotLine("DigitialProbe1", (ImU16*)m_selectedXAxis.data(), (ImU16*)m_selectedHit.digitalTrace1Samples.data(), m_selectedXAxis.size());
ImPlot::PlotLine("DigitialProbe2", (ImU16*)m_selectedXAxis.data(), (ImU16*)m_selectedHit.digitalTrace2Samples.data(), m_selectedXAxis.size());
}
ImPlot::EndPlot();
}
}
ImGui::End();
}
}

35
src/Editor/ScopePanel.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef SCOPE_PANEL_H
#define SCOPE_PANEL_H
#include "DAQ/DigitizerDefs.h"
#include "Core/DataDistributor.h"
namespace Daqromancy {
class ScopePanel
{
public:
ScopePanel(int nboards);
~ScopePanel();
void OnUpdate();
void OnImGuiRender();
private:
//uint64_t m_consumerID;
DistributorClient m_dataHandle;
std::vector<DYData> m_buffer; //Buffered data retrieved from ring
DYData m_selectedHit; //Hit associated with selected board/channel
std::vector<int16_t> m_selectedXAxis; //X data is not given by DAQ, has to be made based on number of wave samples
bool m_dataReady;
int m_selectedBoard;
int m_selectedChannel;
int m_maxNumBoards;
std::vector<std::string> m_boardListForImGui;
std::vector<std::string> m_channelListForImGui;
};
}
#endif

78
src/Editor/SyncDialog.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "SyncDialog.h"
#include "imgui.h"
namespace Daqromancy {
static std::string SyncMethodToString(SyncMethod method)
{
switch (method)
{
case SyncMethod::SIn_TrigOut: return "SyncIn-TrigOut";
case SyncMethod::None: return "None";
}
return "None";
}
static std::string StartTypeToString(SyncStartType type)
{
switch (type)
{
case SyncStartType::HardwareControlled: return "Hardware-Controlled";
case SyncStartType::SoftwareControlled: return "Software-Controlled";
case SyncStartType::None: return "None";
}
return "None";
}
SyncDialog::SyncDialog() :
m_open(false)
{
}
SyncDialog::~SyncDialog() {}
bool SyncDialog::OnImGuiRender()
{
bool status = false;
if (m_open)
{
ImGui::OpenPopup("SyncDialog");
status = false;
m_open = false;
}
if (ImGui::BeginPopupModal("SyncDialog"))
{
if (ImGui::BeginCombo("Method", SyncMethodToString(m_syncArgs.syncMethod).c_str()))
{
if (ImGui::Selectable("SyncIn-TrigOut", SyncMethod::SIn_TrigOut == m_syncArgs.syncMethod, ImGuiSelectableFlags_DontClosePopups))
m_syncArgs.syncMethod = SyncMethod::SIn_TrigOut;
if (ImGui::Selectable("None", SyncMethod::None == m_syncArgs.syncMethod, ImGuiSelectableFlags_DontClosePopups))
m_syncArgs.syncMethod = SyncMethod::None;
}
if (ImGui::BeginCombo("Start type", StartTypeToString(m_syncArgs.startType).c_str()))
{
if (ImGui::Selectable("Hardware-Controlled", SyncStartType::HardwareControlled == m_syncArgs.startType, ImGuiSelectableFlags_DontClosePopups))
m_syncArgs.startType = SyncStartType::HardwareControlled;
if (ImGui::Selectable("Software-Controlled", SyncStartType::SoftwareControlled == m_syncArgs.startType, ImGuiSelectableFlags_DontClosePopups))
m_syncArgs.startType = SyncStartType::SoftwareControlled;
}
if (ImGui::Button("Ok"))
{
status = true;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
status = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
return status;
}
}

25
src/Editor/SyncDialog.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef SYNC_DIALOG_H
#define SYNC_DIALOG_H
#include "Events/AcqEvent.h"
namespace Daqromancy {
class SyncDialog
{
public:
SyncDialog();
~SyncDialog();
void OpenDialog() { m_open = true; }
bool OnImGuiRender();
AcqSyncArgsEvent GetSyncEvent() { return m_syncArgs; }
private:
bool m_open;
SyncArgs m_syncArgs;
};
}
#endif

111
src/Events/AcqEvent.h Normal file
View File

@ -0,0 +1,111 @@
#ifndef ACQ_EVENT_H
#define ACQ_EVENT_H
#include "Event.h"
#include "DAQ/DigitizerDefs.h"
namespace Daqromancy {
//For now all pure message, not sure what exactly needed yet
class AcqStartEvent : public Event
{
public:
AcqStartEvent(bool writeToDisk, bool writeToServer) :
m_isWriteToDisk(writeToDisk), m_isWriteToServer(writeToServer)
{
}
bool IsWriteToDisk() const { return m_isWriteToDisk; }
bool IsWriteToServer() const { return m_isWriteToServer; }
EVENT_CATEGORY_SETUP(EventCategoryAcq);
EVENT_TYPE_SETUP(AcqStart);
private:
bool m_isWriteToDisk;
bool m_isWriteToServer;
};
class AcqStopEvent : public Event
{
public:
AcqStopEvent() = default;
EVENT_CATEGORY_SETUP(EventCategoryAcq);
EVENT_TYPE_SETUP(AcqStop);
};
class AcqParametersEvent : public Event
{
public:
AcqParametersEvent(DigitizerAccessType type, int boardHandle = -1) :
m_boardHandle(boardHandle)
{
}
int GetBoardHandle() { return m_boardHandle; }
DigitizerAccessType GetAccessType() { return m_type; }
virtual std::string ToString() const override
{
std::stringstream stream;
stream << GetName() << " board handle: " << m_boardHandle;
return stream.str();
}
EVENT_CATEGORY_SETUP(EventCategoryAcq);
EVENT_TYPE_SETUP(AcqParameters);
private:
int m_boardHandle;
DigitizerAccessType m_type;
};
class AcqSyncArgsEvent : public Event
{
public:
AcqSyncArgsEvent(SyncArgs& args) :
m_args(args)
{
}
const SyncArgs& GetArgs() const { return m_args; }
EVENT_CATEGORY_SETUP(EventCategoryAcq);
EVENT_TYPE_SETUP(AcqSyncArgs);
private:
SyncArgs m_args;
};
class AcqDetectBoardsEvent : public Event
{
public:
AcqDetectBoardsEvent() = default;
EVENT_CATEGORY_SETUP(EventCategoryAcq);
EVENT_TYPE_SETUP(AcqDetectBoards);
};
class AcqDisconnectBoardsEvent : public Event
{
public:
AcqDisconnectBoardsEvent() = default;
EVENT_CATEGORY_SETUP(EventCategoryAcq);
EVENT_TYPE_SETUP(AcqDisconnectBoards);
};
class AcqDPPModeEvent : public Event
{
public:
AcqDPPModeEvent() = default;
EVENT_CATEGORY_SETUP(EventCategoryAcq);
EVENT_TYPE_SETUP(AcqDPPMode);
};
}
#endif

53
src/Events/AppEvent.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef APP_EVENT_H
#define APP_EVENT_H
#include "Event.h"
namespace Daqromancy {
//Pure message, no data transferred
class WindowCloseEvent : public Event
{
public:
WindowCloseEvent() = default;
EVENT_CATEGORY_SETUP(EventCategoryApp);
EVENT_TYPE_SETUP(WindowClose);
};
class WindowResizeEvent : public Event
{
public:
WindowResizeEvent(uint32_t width, uint32_t height) :
m_width(width), m_height(height)
{
}
uint32_t GetWidth() const { return m_width; }
uint32_t GetHeight() const { return m_height; }
virtual std::string ToString() const override
{
std::stringstream stream;
stream << GetName() << "(" << m_width << ", " << m_height << ")";
return stream.str();
}
EVENT_CATEGORY_SETUP(EventCategoryApp);
EVENT_TYPE_SETUP(WindowResize);
private:
uint32_t m_width, m_height;
};
class AppUpdateEvent : public Event
{
public:
AppUpdateEvent() = default;
EVENT_CATEGORY_SETUP(EventCategoryApp);
EVENT_TYPE_SETUP(AppUpdate);
};
}
#endif

82
src/Events/Event.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef EVENT_H
#define EVENT_H
#include "Core/DYCore.h"
namespace Daqromancy {
enum class EventType
{
None=0,
WindowClose,WindowResize,
KeyPressed, KeyReleased, KeyTyped,
MouseButtonPressed, MouseButtonReleased, MouseScrolled, MouseMoved,
AppUpdate,
AcqStart, AcqStop, AcqParameters, AcqSyncArgs, AcqDetectBoards, AcqDisconnectBoards, AcqDPPMode
};
enum EventCategory
{
EventCategoryNone=0,
EventCategoryKey = BIT(1),
EventCategoryMouse = BIT(2),
EventCategoryApp = BIT(3),
EventCategoryAcq = BIT(4),
EventCategoryInput = BIT(5)
};
//Some ease of code generation macros
#define EVENT_CATEGORY_SETUP(cat) virtual int GetCategoryFlags() const override { return cat; }
#define EVENT_TYPE_SETUP(type) static EventType GetStaticType() { return EventType::type; } \
virtual EventType GetEventType() const override { return GetStaticType(); } \
virtual const char* GetName() const override { return #type; }
//Event base class
class Event
{
friend class EventDispatcher;
public:
virtual EventType GetEventType() const = 0;
virtual const char* GetName() const = 0;
virtual int GetCategoryFlags() const = 0;
virtual std::string ToString() const { return GetName(); }
bool IsCategory(EventCategory cat) const { return cat & GetCategoryFlags(); }
bool handled = false;
};
//Class which wraps event messaging.
//When an event is created, a dispatcher is also created. The dispatcher takes the created event,
//and passes it to handling callbacks via the Dispatch function. If the event is handled, Dispatch returns true.
//If one wants the event to only be handled by a single case, have the callback return true to set the event internal status as handled.
class EventDispatcher
{
public:
EventDispatcher(Event& e) :
m_event(e)
{
}
template<typename T, typename F>
bool Dispatch(const F& function)
{
if (m_event.GetEventType() == T::GetStaticType())
{
m_event.handled = function(static_cast<T&>(m_event));
return true;
}
else
return false;
}
private:
Event& m_event;
};
//So that events can be logged easily
inline std::ostream& operator<<(std::ostream& os, const Event& e)
{
return os << e.ToString();
}
}
#endif

89
src/Events/KeyEvent.h Normal file
View File

@ -0,0 +1,89 @@
#ifndef KEY_EVENT_H
#define KEY_EVENT_H
#include "Event.h"
namespace Daqromancy {
//Since so many key related events, have a base
class KeyEvent : public Event
{
public:
int GetKeycode() const { return m_keycode; }
EVENT_CATEGORY_SETUP(EventCategoryKey | EventCategoryInput)
protected:
KeyEvent(int code) :
m_keycode(code)
{
}
int m_keycode;
};
class KeyPressedEvent : public KeyEvent
{
public:
KeyPressedEvent(int code, int count) :
KeyEvent(code), m_repeatCount(count)
{
}
EVENT_TYPE_SETUP(KeyPressed)
int GetRepeatCount() const { return m_repeatCount; }
std::string ToString() const override
{
std::stringstream stream;
stream << GetName() << " with code " << m_keycode << " pressed " << m_repeatCount << " times.";
return stream.str();
}
private:
int m_repeatCount;
};
class KeyReleasedEvent : public KeyEvent
{
public:
KeyReleasedEvent(int code) :
KeyEvent(code)
{
};
EVENT_TYPE_SETUP(KeyReleased)
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " with code " << m_keycode;
return ss.str();
}
};
class KeyTypedEvent : public KeyEvent
{
public:
KeyTypedEvent(int code) :
KeyEvent(code)
{
};
EVENT_TYPE_SETUP(KeyTyped)
unsigned int GetCharacter() { return (unsigned int)m_keycode; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " with code " << m_keycode;
return ss.str();
}
};
}
#endif

103
src/Events/MouseEvent.h Normal file
View File

@ -0,0 +1,103 @@
#ifndef MOUSE_EVENT_H
#define MOUSE_EVENT_H
#include "Event.h"
namespace Daqromancy {
class MouseMovedEvent : public Event
{
public:
MouseMovedEvent(float x, float y) :
m_xPos(x), m_yPos(y)
{
}
float GetXPosition() { return m_xPos; }
float GetYPosition() { return m_yPos; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " to position (" << m_xPos << ", " << m_yPos << ")";
return ss.str();
}
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
EVENT_TYPE_SETUP(MouseMoved)
private:
float m_xPos, m_yPos;
};
class MouseScrolledEvent : public Event
{
public:
MouseScrolledEvent(float x, float y) :
m_xOffset(x), m_yOffset(y)
{
}
float GetXOffset() { return m_xOffset; }
float GetYOffset() { return m_yOffset; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " to offset (" << m_xOffset << ", " << m_yOffset << ")";
return ss.str();
}
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
EVENT_TYPE_SETUP(MouseScrolled)
private:
float m_xOffset, m_yOffset;
};
class MouseButtonPressedEvent : public Event
{
public:
MouseButtonPressedEvent(int code) :
m_buttonCode(code)
{
}
int GetButtonCode() { return m_buttonCode; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " with button code " << m_buttonCode;
return ss.str();
}
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
EVENT_TYPE_SETUP(MouseButtonPressed)
private:
int m_buttonCode;
};
class MouseButtonReleasedEvent : public Event
{
public:
MouseButtonReleasedEvent(int code) :
m_buttonCode(code)
{
}
int GetButtonCode() { return m_buttonCode; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " with button code " << m_buttonCode;
return ss.str();
}
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
EVENT_TYPE_SETUP(MouseButtonReleased)
private:
int m_buttonCode;
};
}
#endif

4
src/ImGui/ImGuiBuild.cpp Normal file
View File

@ -0,0 +1,4 @@
#include "backends/imgui_impl_opengl3.cpp"
#include "backends/imgui_impl_glfw.cpp"
#include "misc/cpp/imgui_stdlib.cpp" //Enable cpp stl enhancements

98
src/ImGui/ImGuiLayer.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "ImGuiLayer.h"
#include "Core/Application.h"
#include "imgui.h"
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_opengl3.h"
#include "implot.h"
#include "GLFW/glfw3.h"
#include "glad/glad.h"
namespace Daqromancy {
ImGuiLayer::ImGuiLayer() :
Layer("ImGuiLayer")
{
}
ImGuiLayer::~ImGuiLayer() {}
void ImGuiLayer::OnAttach()
{
IMGUI_CHECKVERSION();
DY_INFO("Creating ImGui Context...");
ImGui::CreateContext();
ImPlot::CreateContext();
//enable docking and viewports
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
ImGui::StyleColorsDark(); //Hacker mode
ImPlot::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
Application& app = Application::GetInstance();
GLFWwindow* window = static_cast<GLFWwindow*>(app.GetWindow().GetNativeWindow());
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init("#version 410"); //GLSL version
DY_INFO("Finished creating ImGui Context");
}
void ImGuiLayer::OnDetach()
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImPlot::DestroyContext();
ImGui::DestroyContext();
}
void ImGuiLayer::Begin()
{
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
}
void ImGuiLayer::End()
{
Application& app = Application::GetInstance();
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2((float)app.GetWindow().GetWidth(), (float)app.GetWindow().GetHeight());
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
GLFWwindow* backup_current_context = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backup_current_context);
}
}
void ImGuiLayer::OnImGuiRender(double timestep)
{
//Demo's used to figure out how to do things.
//Should not be on for actual NavProject for
//real use
//static bool show = true;
//ImGui::ShowDemoWindow(&show);
}
}

24
src/ImGui/ImGuiLayer.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef IMGUI_LAYER_H
#define IMGUI_LAYER_H
#include "Core/Layer.h"
namespace Daqromancy {
class ImGuiLayer : public Layer
{
public:
ImGuiLayer();
virtual ~ImGuiLayer();
virtual void OnAttach() override;
virtual void OnDetach() override;
virtual void OnImGuiRender(double timestep) override;
void Begin();
void End();
};
}
#endif

View File

@ -0,0 +1,33 @@
#include "OpenGLContext.h"
#include "GLFW/glfw3.h"
#include "glad/glad.h"
namespace Daqromancy {
OpenGLContext::OpenGLContext(GLFWwindow* window) :
m_window(window)
{
}
OpenGLContext::~OpenGLContext()
{
}
void OpenGLContext::Init()
{
glfwMakeContextCurrent(m_window);
int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
DY_TRACE("Loaded OpenGL with glad Init status {0}", status);
DY_INFO("Loaded OpenGL renderer");
DY_INFO("Vendor: {0}", glGetString(GL_VENDOR));
DY_INFO("Renderer: {0}", glGetString(GL_RENDERER));
DY_INFO("Version: {0}", glGetString(GL_VERSION));
}
void OpenGLContext::SwapBuffers()
{
glfwSwapBuffers(m_window);
}
}

View File

@ -0,0 +1,24 @@
#ifndef OPENGL_CONTEXT_H
#define OPENGL_CONTEXT_H
#include "Renderer/GraphicsContext.h"
struct GLFWwindow;
namespace Daqromancy {
class OpenGLContext : public GraphicsContext
{
public:
OpenGLContext(GLFWwindow* window);
~OpenGLContext();
virtual void Init() override;
virtual void SwapBuffers() override;
private:
GLFWwindow* m_window;
};
}
#endif

View File

@ -0,0 +1,21 @@
#include "OpenGLRendererAPI.h"
#include "glad/glad.h"
#include "GLFW/glfw3.h"
namespace Daqromancy {
void OpenGLRendererAPI::Clear()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void OpenGLRendererAPI::SetClearColor(float color[4])
{
glClearColor(color[0], color[1], color[2], color[3]);
}
double OpenGLRendererAPI::GetFrameTime()
{
return glfwGetTime();
}
}

View File

@ -0,0 +1,18 @@
#ifndef OPENGL_RENDERER_API_H
#define OPENGL_RENDERER_API_H
#include "Renderer/RendererAPI.h"
namespace Daqromancy {
class OpenGLRendererAPI : public RendererAPI
{
public:
virtual void SetClearColor(float color[4]) override;
virtual void Clear() override;
virtual double GetFrameTime() override;
};
}
#endif

View File

@ -0,0 +1,164 @@
#include "OpenGLWindow.h"
#include "OpenGLContext.h"
#include "Events/AppEvent.h"
#include "Events/KeyEvent.h"
#include "Events/MouseEvent.h"
namespace Daqromancy {
static bool s_glfwInitialized = false;
static void GLFWErrorCallback(int error, const char* description)
{
DY_ERROR("GLFW Error! Code: {0} Message: {1}", error, description);
}
Window* Window::Create(const WindowProperties& props) { return new OpenGLWindow(props); }
OpenGLWindow::OpenGLWindow(const WindowProperties& props)
{
Init(props);
}
OpenGLWindow::~OpenGLWindow()
{
Shutdown();
}
void OpenGLWindow::Init(const WindowProperties& props)
{
m_data.width = props.width;
m_data.height = props.height;
m_data.name = props.title;
DY_INFO("Creating OpenGL Window with width {0}, height {1}, and name {2}", m_data.width, m_data.height, m_data.name);
if (!s_glfwInitialized)
{
int passed = glfwInit();
DY_INFO("Initializing GLFW ... status {0}", passed);
glfwSetErrorCallback(GLFWErrorCallback);
}
//Apple in their effort to force Metal on everyone, has deprecated OpenGL. We have to be
//A little more agressive to make OpenGL and GLFW play well together.
#ifdef BS_APPLE
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
#endif
m_window = glfwCreateWindow((int)m_data.width, (int)m_data.height, m_data.name.c_str(), nullptr, nullptr);
m_context = new OpenGLContext(m_window); //This is the only seriously OpenGL specific code
m_context->Init();
glfwSetWindowUserPointer(m_window, &m_data);
SetVSync(true);
//Set all of the callback functions for the window.
glfwSetWindowSizeCallback(m_window, [](GLFWwindow* window, int width, int height)
{
Data& data = *(Data*)glfwGetWindowUserPointer(window);
data.width = width;
data.height = height;
WindowResizeEvent event(width, height);
data.callbackFunc(event);
});
glfwSetWindowCloseCallback(m_window, [](GLFWwindow* window)
{
Data& data = *(Data*)glfwGetWindowUserPointer(window);
WindowCloseEvent event;
data.callbackFunc(event);
});
glfwSetKeyCallback(m_window, [](GLFWwindow* window, int key, int scancode, int action, int mods)
{
Data& data = *(Data*)glfwGetWindowUserPointer(window);
switch (action)
{
case GLFW_PRESS:
{
KeyPressedEvent event(key, 0);
data.callbackFunc(event);
break;
}
case GLFW_RELEASE:
{
KeyReleasedEvent event(key);
data.callbackFunc(event);
break;
}
case GLFW_REPEAT:
{
//GLFW doesnt have a hold count, so here we just pass 1 to indicate a hold is happening.
KeyPressedEvent event(key, 1);
data.callbackFunc(event);
break;
}
}
});
glfwSetCharCallback(m_window, [](GLFWwindow* window, unsigned int character)
{
Data& data = *(Data*)glfwGetWindowUserPointer(window);
KeyTypedEvent event(character);
data.callbackFunc(event);
});
glfwSetMouseButtonCallback(m_window, [](GLFWwindow* window, int button, int action, int mods)
{
Data& data = *(Data*)glfwGetWindowUserPointer(window);
switch (action)
{
case GLFW_PRESS:
{
MouseButtonPressedEvent event(button);
data.callbackFunc(event);
}
case GLFW_RELEASE:
{
MouseButtonReleasedEvent event(button);
data.callbackFunc(event);
}
}
});
glfwSetScrollCallback(m_window, [](GLFWwindow* window, double xoffset, double yoffset)
{
Data& data = *(Data*)glfwGetWindowUserPointer(window);
MouseScrolledEvent event((float)xoffset, (float)yoffset);
data.callbackFunc(event);
});
glfwSetCursorPosCallback(m_window, [](GLFWwindow* window, double xpos, double ypos)
{
Data& data = *(Data*)glfwGetWindowUserPointer(window);
MouseMovedEvent event((float)xpos, (float)ypos);
data.callbackFunc(event);
});
}
void OpenGLWindow::Shutdown()
{
glfwDestroyWindow(m_window);
}
void OpenGLWindow::OnUpdate()
{
glfwPollEvents();
m_context->SwapBuffers();
}
void OpenGLWindow::SetVSync(bool enabled)
{
if (enabled)
glfwSwapInterval(1);
else
glfwSwapInterval(0);
m_data.isVSync = enabled;
}
}

View File

@ -0,0 +1,47 @@
#ifndef OPENGL_WINDOW_H
#define OPENGL_WINDOW_H
#include "Core/Window.h"
#include "Renderer/GraphicsContext.h"
#include "GLFW/glfw3.h"
namespace Daqromancy {
class OpenGLWindow : public Window
{
public:
OpenGLWindow(const WindowProperties& props);
virtual ~OpenGLWindow();
virtual void OnUpdate() override;
virtual uint32_t GetWidth() override { return m_data.width; }
virtual uint32_t GetHeight() override { return m_data.height; }
virtual std::string GetName() override { return m_data.name; }
virtual void SetEventCallback(EventCallbackFunc function) override { m_data.callbackFunc = function; }
virtual void SetVSync(bool enabled) override;
virtual bool IsVSync() override { return m_data.isVSync; }
virtual void* GetNativeWindow() const override { return m_window; } //dirty
private:
virtual void Init(const WindowProperties& props);
virtual void Shutdown();
GraphicsContext* m_context;
GLFWwindow* m_window;
struct Data
{
int width, height;
std::string name;
bool isVSync;
EventCallbackFunc callbackFunc;
};
Data m_data;
};
}
#endif

View File

@ -0,0 +1,14 @@
#ifndef GRAPHICS_CONTEXT_H
#define GRAPHICS_CONTEXT_H
namespace Daqromancy {
class GraphicsContext
{
public:
virtual void Init() = 0;
virtual void SwapBuffers() = 0;
};
}
#endif

View File

@ -0,0 +1,8 @@
#include "RenderCommand.h"
#include "Platform/OpenGL/OpenGLRendererAPI.h"
namespace Daqromancy {
RendererAPI* RenderCommand::s_api = new OpenGLRendererAPI();
}

View File

@ -0,0 +1,21 @@
#ifndef RENDER_COMMAND_H
#define RENDER_COMMAND_H
#include "RendererAPI.h"
namespace Daqromancy {
class RenderCommand
{
public:
static void SetClearColor(float color[4]) { s_api->SetClearColor(color); }
static void Clear() { s_api->Clear(); }
static double GetFrameTime() { return s_api->GetFrameTime(); }
private:
static RendererAPI* s_api;
};
}
#endif

View File

@ -0,0 +1,7 @@
#include "RendererAPI.h"
namespace Daqromancy {
RendererAPI::API RendererAPI::s_api = RendererAPI::API::OpenGL;
}

View File

@ -0,0 +1,26 @@
#ifndef RENDERER_API_H
#define RENDERER_API_H
namespace Daqromancy {
class RendererAPI
{
public:
enum class API
{
None=0,
OpenGL
};
virtual void Clear() = 0;
virtual void SetClearColor(float color[4]) = 0;
virtual double GetFrameTime() = 0;
static API GetAPI() { return s_api; }
private:
static API s_api;
};
}
#endif

21
src/dypch.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef BXSCPCH_H
#define BXSCPCH_H
#include <iostream>
#include <fstream>
#include <memory>
#include <utility>
#include <algorithm>
#include <functional>
#include <string>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <cstdint>
#include "Core/Logger.h"
#endif

11
vendor/glad/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,11 @@
project(Glad)
add_library(glad STATIC)
target_include_directories(glad SYSTEM PUBLIC include)
target_sources(glad
PRIVATE src/glad.c
PRIVATE include/glad/glad.h
PRIVATE include/KHR/khrplatform.h
)

311
vendor/glad/include/KHR/khrplatform.h vendored Normal file
View File

@ -0,0 +1,311 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

3694
vendor/glad/include/glad/glad.h vendored Normal file

File diff suppressed because it is too large Load Diff

1833
vendor/glad/src/glad.c vendored Normal file

File diff suppressed because it is too large Load Diff