Compare commits

...

21 Commits

Author SHA1 Message Date
ce29f05ca0 fixes 2025-01-15 11:55:53 +03:00
a6f15e234f start acquisition tunning (wait for STATUS_ARMED status before trigger
snapshot)
2025-01-15 11:37:11 +03:00
Timur A. Fatkhullin
7497b72b32 rewrite AcquisitionProcess class (use of detached thread)
code clean-ups
2025-01-15 00:12:56 +03:00
cf3e34f88c fix logging 2025-01-14 11:33:05 +03:00
8c319780dc add logging for current acqquisition buffer addresses 2025-01-06 15:42:02 +03:00
Timur A. Fatkhullin
d103ed2887 fixes 2024-12-30 17:16:38 +03:00
c1b7d249da start development branch.
rewrite image buffer managment (dynamic ring buffer, allocation of
buffers at initialization of camera, no allocation of image buffer per
acquisition process)
2024-12-27 18:41:46 +03:00
99e072be78 CMakeLists.txt: replace option STATIC_LIB to RAPROT_SHARED_LIB. The
default behavior is now build a static library
2024-12-27 16:04:12 +03:00
69bba48ce1 fix keywords updating from permanent and current list 2024-12-24 18:10:56 +03:00
ea9b099d81 fix status string, free memory in AcquisitionProcess instance 2024-12-24 13:38:49 +03:00
0ece075bb8 CMakeLists.txt: do not fetch SPDLOG library, but check for system one
CMakeLists.txt: add uninstall target
2024-12-23 11:57:27 +03:00
Timur A. Fatkhullin
f068cbb44b CMakeLists.txt: fetch spdlog, set SPDLOG_USE_STD_FORMAT to use C++20
std::format in the SPDLOG-library (now there is no linking to libfmt)
2024-12-22 17:40:00 +03:00
77131074e7 fix 2024-12-20 15:13:42 +03:00
66e0b6c71e ... 2024-12-20 11:07:29 +03:00
610d09ee98 fixes 2024-12-19 20:04:38 +03:00
bb28f0aa03 ... 2024-12-19 19:43:09 +03:00
34959c632d fix checking file permissions before start exposure 2024-12-19 19:22:55 +03:00
6c45f04448 rewrite CAMERA_STATUS logics
add filesystem permissions checking
2024-12-19 18:43:16 +03:00
b570a4e7ea CMakeLists.txt: fix std::atomic<T> linking (add -latommic) 2024-12-19 08:40:14 +03:00
50e457f046 RaptorEagleCCD::_cameraStatus is now usefull 2024-12-19 08:31:55 +03:00
3fee70b445 add CAMERA_STATUS attribute (still unusable) 2024-12-18 18:15:16 +03:00
7 changed files with 508 additions and 234 deletions

View File

@@ -18,33 +18,50 @@ find_package(cxxopts CONFIG)
set(RAPTOR_EAGLEV_LIB raptor_eaglev) set(RAPTOR_EAGLEV_LIB raptor_eaglev)
option(STATIC_LIB "Create static '${RAPTOR_EAGLEV_LIB}' library" OFF) option(RAPROT_SHARED_LIB "Create shared '${RAPTOR_EAGLEV_LIB}' library" OFF)
set(RAPTOR_EAGLEV_LIB_SRC set(RAPTOR_EAGLEV_LIB_SRC
raptor_eagle_ccd.h raptor_eagle_ccd.cpp raptor_eagle_ccd.h
raptor_eagle_ccd.cpp
raptor_eagle_cameralink.h raptor_eagle_cameralink.h
raptor_eagle_exception.h raptor_eagle_exception.h
raptor_eagle_acqproc.cpp) raptor_eagle_acqproc.cpp)
if (STATIC_LIB) if (RAPROT_SHARED_LIB)
add_library(${RAPTOR_EAGLEV_LIB} STATIC ${RAPTOR_EAGLEV_LIB_SRC})
else()
add_library(${RAPTOR_EAGLEV_LIB} SHARED ${RAPTOR_EAGLEV_LIB_SRC}) add_library(${RAPTOR_EAGLEV_LIB} SHARED ${RAPTOR_EAGLEV_LIB_SRC})
# add_library(${RAPTOR_EAGLEV_LIB} SHARED else()
# raptor_eagle_ccd.h raptor_eagle_ccd.cpp add_library(${RAPTOR_EAGLEV_LIB} STATIC ${RAPTOR_EAGLEV_LIB_SRC})
# raptor_eagle_cameralink.h
# raptor_eagle_exception.h
# raptor_eagle_acqproc.cpp)
endif() endif()
# include(FetchContent)
# include(ExternalProject)
# FetchContent_Declare(spdlog
# # SOURCE_DIR ${CMAKE_BINARY_DIR}/spdlog
# # BINARY_DIR ${CMAKE_BINARY_DIR}/spdlog/build
# GIT_REPOSITORY "https://github.com/gabime/spdlog.git"
# GIT_TAG "v1.15.0"
# GIT_SHALLOW TRUE
# GIT_SUBMODULES ""
# GIT_PROGRESS TRUE
# )
# FetchContent_MakeAvailable(spdlog)
# FetchContent_GetProperties(spdlog SOURCE_DIR spdlog_SOURCE_DIR)
# set(spdlog_INCLUDE_DIR ${spdlog_SOURCE_DIR}/include)
# to activate spdlog-library support in ADC-library # to activate spdlog-library support in ADC-library
# target_compile_definitions(${RAPTOR_EAGLEV_LIB} PRIVATE USE_ASIO_LIBRARY USE_SPDLOG_LIBRARY USE_OPENSSL_WITH_ASIO) # target_compile_definitions(${RAPTOR_EAGLEV_LIB} PRIVATE USE_ASIO_LIBRARY USE_SPDLOG_LIBRARY USE_OPENSSL_WITH_ASIO)
target_compile_definitions(${RAPTOR_EAGLEV_LIB} PUBLIC USE_ASIO_LIBRARY USE_SPDLOG_LIBRARY) target_compile_definitions(${RAPTOR_EAGLEV_LIB} PUBLIC USE_ASIO_LIBRARY USE_SPDLOG_LIBRARY)
target_compile_definitions(${RAPTOR_EAGLEV_LIB} PUBLIC SPDLOG_USE_STD_FORMAT)
# !!!!! TEMPORARY !!!!! # !!!!! TEMPORARY !!!!!
target_include_directories(${RAPTOR_EAGLEV_LIB} PUBLIC "../ADC/") target_include_directories(${RAPTOR_EAGLEV_LIB} PUBLIC "../ADC/")
target_include_directories(${RAPTOR_EAGLEV_LIB} PUBLIC ${XCLIB_INCLUDE_DIR} ${CFITSIO_INCLUDE_DIR}) target_include_directories(${RAPTOR_EAGLEV_LIB} PUBLIC ${XCLIB_INCLUDE_DIR} ${CFITSIO_INCLUDE_DIR})
target_link_libraries(${RAPTOR_EAGLEV_LIB} PUBLIC Threads::Threads spdlog::spdlog_header_only ${XCLIB_LIBRARIES} ${CFITSIO_LIBRARY}) # target_include_directories(${RAPTOR_EAGLEV_LIB} PUBLIC ${spdlog_INCLUDE_DIR})
target_include_directories(${RAPTOR_EAGLEV_LIB} PUBLIC spdlog::spdlog_header_only)
# target_link_libraries(${RAPTOR_EAGLEV_LIB} PUBLIC Threads::Threads spdlog::spdlog_header_only ${XCLIB_LIBRARIES} ${CFITSIO_LIBRARY})
target_link_libraries(${RAPTOR_EAGLEV_LIB} PUBLIC Threads::Threads ${XCLIB_LIBRARIES} ${CFITSIO_LIBRARY})
set(RAPTOR_EAGLEV_SERVER raptor_eaglev_server) set(RAPTOR_EAGLEV_SERVER raptor_eaglev_server)
@@ -52,12 +69,24 @@ add_executable(${RAPTOR_EAGLEV_SERVER} raptor_eaglev_server.cpp)
target_link_libraries(${RAPTOR_EAGLEV_SERVER} PUBLIC ${RAPTOR_EAGLEV_LIB} cxxopts::cxxopts) target_link_libraries(${RAPTOR_EAGLEV_SERVER} PUBLIC ${RAPTOR_EAGLEV_LIB} cxxopts::cxxopts)
include(GNUInstallDirs) include(GNUInstallDirs)
install(TARGETS ${RAPTOR_EAGLEV_LIB} if (RAPROT_SHARED_LIB) # install shared library and server binary
install(TARGETS ${RAPTOR_EAGLEV_LIB} ${RAPTOR_EAGLEV_SERVER}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
) )
# install(TARGETS ${RAPTOR_EAGLEV_LIB} ${RAPTOR_EAGLEV_SERVER} else()
# LIBRARY DESTINATION "/home/obs/TMP" install(TARGETS ${RAPTOR_EAGLEV_SERVER}
# ARCHIVE DESTINATION "/home/obs/TMP" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
# RUNTIME DESTINATION "/home/obs/TMP" )
# ) endif()
# uninstall target
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()

19
cmake_uninstall.cmake.in Normal file
View File

@@ -0,0 +1,19 @@
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
endif()
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program("@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif(NOT "${rm_retval}" STREQUAL 0)
else()
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif()
endforeach()

View File

@@ -1,5 +1,6 @@
#include <fitsio.h> #include <fitsio.h>
#include <cmath> #include <cmath>
#include <filesystem>
#include "raptor_eagle_ccd.h" #include "raptor_eagle_ccd.h"
@@ -39,11 +40,11 @@ double JulianDay(const std::chrono::utc_clock::time_point& tm)
} // namespace details } // namespace details
RaptorEagleCCD::AcquisitionProcess::AcquisitionProcess(RaptorEagleCCD* manager) : _manager(manager) RaptorEagleCCD::AcquisitionProcess::AcquisitionProcess()
{ {
std::stringstream st; std::stringstream st;
st << std::this_thread::get_id(); st << std::this_thread::get_id();
_manager->logDebug("Create acquisition process (thread id: {})", st.str()); serverPtr->logDebug("Create acquisition process (thread id: {})", st.str());
} }
@@ -51,23 +52,22 @@ RaptorEagleCCD::AcquisitionProcess::~AcquisitionProcess()
{ {
std::stringstream st; std::stringstream st;
st << std::this_thread::get_id(); st << std::this_thread::get_id();
_manager->logDebug("Delete acquisition process (thread id: {})", st.str()); serverPtr->logDebug("Delete acquisition process (thread id: {})", st.str());
} }
void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_t>& params) void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_t>& params)
{ {
// if (isAcqInProgress) {
// throw std::system_error(RaptorEagleCCDError::ERROR_ACQUISITION_IN_PROGRESS);
// }
isAcqInProgress = true; isAcqInProgress = true;
// _acqParams = std::move(params);
_acqParams = params; _acqParams = params;
// _acqParams->abortTime = std::chrono::utc_clock::time_point(); // to ensure the time point is in past
_snapAndCopyFuture = std::async(std::launch::async, [self = shared_from_this(), this]() { std::thread([self = shared_from_this(), this]() {
{
std::lock_guard lock_guard(serverPtr->_acqProcessesMutex);
acqProcSptr.insert(self);
}
std::chrono::milliseconds timeout = std::chrono::milliseconds timeout =
CAMERA_CAPTURE_TIMEOUT_ADD_CONSTANT + CAMERA_CAPTURE_TIMEOUT_ADD_CONSTANT +
std::chrono::milliseconds(static_cast<std::chrono::milliseconds::rep>(_acqParams->expTime * 1000)); std::chrono::milliseconds(static_cast<std::chrono::milliseconds::rep>(_acqParams->expTime * 1000));
@@ -76,126 +76,116 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
std::stringstream st; std::stringstream st;
st << std::this_thread::get_id(); st << std::this_thread::get_id();
_manager->logDebug("Arm grabber and wait for acquisition starting trigger (thread id: {}) ...", st.str()); serverPtr->logDebug("Arm grabber and wait for acquisition starting trigger (thread id: {}) ...", st.str());
_manager->xclibApiCall(pxd_doSnap(_manager->_cameraUnitmap, 1, timeout.count()), _status = STATUS_ARMED; // change to STATUS_ACQ will be made in RaptorEagleCCD::startAcquisition!!!
std::format("pxd_doSnap({}, 1, {})", _manager->_cameraUnitmap, timeout.count()));
_manager->logDebug("Capture is finished (thread id: {})!", st.str()); serverPtr->xclibApiCall(pxd_doSnap(serverPtr->_cameraUnitmap, 1, timeout.count()),
std::format("pxd_doSnap({}, 1, {})", serverPtr->_cameraUnitmap, timeout.count()));
serverPtr->logDebug("Capture is finished (thread id: {})!", st.str());
if ((_acqParams->abortTime > _acqParams->startTime) && if ((_acqParams->abortTime > _acqParams->startTime) &&
!_acqParams->saveInAbort) { // abort acquisition was occured! just exit !_acqParams->saveInAbort) { // abort acquisition was occured! just exit
_manager->logInfo("It seems the acquisition was aborted! Do not save acquired image!");
isAcqInProgress = false; isAcqInProgress = false;
_status = STATUS_IDLE;
serverPtr->logInfo("It seems the acquisition was aborted! Do not save acquired image!");
return; return;
} }
_manager->logDebug("Image ROI [{}, {}, {}, {}] (binned {}x{})", _acqParams->roiStartX, _acqParams->roiStartY, serverPtr->logDebug("Image ROI [{}, {}, {}, {}] (binned {}x{})", _acqParams->roiStartX, _acqParams->roiStartY,
_acqParams->roiWidth, _acqParams->roiHeight, _acqParams->binX, _acqParams->binY); _acqParams->roiWidth, _acqParams->roiHeight, _acqParams->binX, _acqParams->binY);
_manager->logDebug("Copy image from grabber to buffer (thread id: {}) ...", st.str()); serverPtr->logDebug("Copy image from grabber to buffer (thread id: {}) ...", st.str());
// compute image dimension // compute image dimension
// NOTE: _acqParams->roiWidth and _acqParams->roiHeight are expected in CCD pixels (not binned)!!! // NOTE: _acqParams->roiWidth and _acqParams->roiHeight are expected in CCD pixels (not binned)!!!
// auto div = std::div(_acqParams->roiWidth - _acqParams->roiStartX, _acqParams->binX);
auto div = std::div(_acqParams->roiWidth, _acqParams->binX); auto div = std::div(_acqParams->roiWidth, _acqParams->binX);
auto dimx = div.quot + (div.rem ? 1 : 0); auto dimx = div.quot + (div.rem ? 1 : 0);
// div = std::div(_acqParams->roiHeight - _acqParams->roiStartY, _acqParams->binY);
div = std::div(_acqParams->roiHeight, _acqParams->binY); div = std::div(_acqParams->roiHeight, _acqParams->binY);
auto dimy = div.quot + (div.rem ? 1 : 0); auto dimy = div.quot + (div.rem ? 1 : 0);
// LONGLONG npix = _acqParams->roiWidth * _acqParams->roiHeight;
LONGLONG npix = dimx * dimy; LONGLONG npix = dimx * dimy;
// NOTE: // NOTE:
auto ldiv = std::lldiv(npix, _manager->_dimCCD[0]); auto ldiv = std::lldiv(npix, serverPtr->_dimCCD[0]);
_imageBufferRows = ldiv.quot + (ldiv.rem ? 1 : 0); auto im_buffer_rows = ldiv.quot + (ldiv.rem ? 1 : 0);
bool gap = false; bool gap = false;
if ((_acqParams->roiWidth + _acqParams->roiStartX) < _manager->_dimCCD[0]) { if ((_acqParams->roiWidth + _acqParams->roiStartX) < serverPtr->_dimCCD[0]) {
/* IT SEEMS IF MAX ROI X-COORDINATE IS LESS THAN CCD DIM /* IT SEEMS IF MAX ROI X-COORDINATE IS LESS THAN CCD DIM
* EAGLE CAMERA CONTROLLER SETUP READING FOR WIDTH+1 ROI!!! */ * EAGLE CAMERA CONTROLLER SETUP READING FOR WIDTH+1 ROI!!! */
gap = true; gap = true;
++_imageBufferRows; ++im_buffer_rows;
} }
// _imageBufferRows = static_cast<size_t>(std::ceil(npix / _manager->_dimCCD[0]));
// read size // read size
size_t sz = _imageBufferRows * _manager->_dimCCD[0]; auto im_buffer_size = im_buffer_rows * serverPtr->_dimCCD[0];
try { auto log_str = std::format("pxd_readushort({}, 1, 0, 0, -1, {}, {}, {}, \"{}\")", serverPtr->_cameraUnitmap,
// sz = _manager->_dimCCD[0] * _manager->_dimCCD[1]; im_buffer_rows, (void*)_acqParams->imageBufferPtr, im_buffer_size, color_space);
if (_imageBufferSize < sz) {
_manager->logDebug("Reallocate image buffer to {} elements", sz);
_imageBufferSize = sz;
_imageBuffer.reset(new ushort[sz]); // may thow std::bad_alloc here!
}
auto log_str = std::format("pxd_readushort({}, 1, 0, 0, -1, {}, {}, {}, \"{}\")", _manager->_cameraUnitmap, serverPtr->xclibApiCall(pxd_readushort(serverPtr->_cameraUnitmap, 1, 0, 0, -1, im_buffer_rows,
_imageBufferRows, (void*)_imageBuffer.get(), _imageBufferSize, color_space); _acqParams->imageBufferPtr, im_buffer_size, (char*)color_space),
// _manager->xclibApiCall(pxd_readushort(_manager->_cameraUnitmap, 1, 0, 0, -1, -1,
// (ushort*)_imageBuffer.get(), _imageBufferSize, (char*)color_space),
// log_str);
_manager->xclibApiCall(pxd_readushort(_manager->_cameraUnitmap, 1, 0, 0, -1, _imageBufferRows,
(ushort*)_imageBuffer.get(), _imageBufferSize, (char*)color_space),
log_str); log_str);
isAcqInProgress = false; isAcqInProgress = false;
} catch (...) {
isAcqInProgress = false;
throw;
}
// ------- save to FITS file -------- // ------- save to FITS file --------
if (_acqParams->filename.empty()) { if (_acqParams->filename.empty()) {
_manager->logWarn("An empty FITS filename is given! Do not save acquired image!"); serverPtr->logWarn("An empty FITS filename is given! Do not save acquired image!");
_status = STATUS_IDLE;
return; return;
} }
_status = STATUS_SAVE;
std::string fname{"!"}; // to overwrite existing file std::string fname{"!"}; // to overwrite existing file
fname += _acqParams->filename; fname += _acqParams->filename;
_manager->logInfo("Try to save FITS file with name '{}'", _acqParams->filename); serverPtr->logInfo("Try to save FITS file with name '{}'", _acqParams->filename);
_manager->logTrace("Actual saving filename: {}", fname); serverPtr->logTrace("Actual saving filename: {}", fname);
fitsfile* fitsFilePtr; fitsfile* fitsFilePtr;
int status = 0; int status = 0;
const int naxis = 2; const int naxis = 2;
long naxes[naxis] = {dimx, dimy}; long naxes[naxis] = {dimx, dimy};
// long naxes[2];
// naxes[0] = _acqParams->roiWidth;
// naxes[1] = _acqParams->roiHeight;
char err_str[100]; char err_str[100];
fits_clear_errmsg(); fits_clear_errmsg();
_manager->logDebug("Create an empty FITS file ..."); serverPtr->logDebug("Create an empty FITS file ...");
fits_create_file(&fitsFilePtr, fname.c_str(), &status); fits_create_file(&fitsFilePtr, fname.c_str(), &status);
_manager->logDebug("Create primary FITS HDU (dim = [{}, {}])", naxes[0], naxes[1]); serverPtr->logDebug("Create primary FITS HDU (dim = [{}, {}])", naxes[0], naxes[1]);
fits_create_img(fitsFilePtr, USHORT_IMG, 2, naxes, &status); fits_create_img(fitsFilePtr, USHORT_IMG, 2, naxes, &status);
_manager->logDebug("Write {} pixels to the HDU ...", npix); serverPtr->logDebug("Write {} pixels to the HDU ...", npix);
if (gap) { if (gap) {
/* IT SEEMS IF MAX ROI X-COORDINATE IS LESS THAN CCD DIM /* IT SEEMS IF MAX ROI X-COORDINATE IS LESS THAN CCD DIM
* EAGLE CAMERA CONTROLLER SETUP READING FOR WIDTH+1 ROI!!! * EAGLE CAMERA CONTROLLER SETUP READING FOR WIDTH+1 ROI!!!
* SO ONE NEEDS TO WRITE IMAGE PER ROW WITH SKIPPING EXTRA PIXEL * SO ONE NEEDS TO WRITE IMAGE PER ROW WITH SKIPPING EXTRA PIXEL
* AT THE END OF EACH ROW * AT THE END OF EACH ROW
*/ */
auto ptr = _imageBuffer.get(); for (int i = 0; i < dimy; ++i) {
for (size_t i = 0; i < dimy; ++i) { fits_write_img(fitsFilePtr, TUSHORT, i * dimx + 1, dimx, _acqParams->imageBufferPtr, &status);
fits_write_img(fitsFilePtr, TUSHORT, i * dimx + 1, dimx, _imageBuffer.get(), &status);
} }
} else { } else {
fits_write_img(fitsFilePtr, TUSHORT, 1, npix, (void*)_imageBuffer.get(), &status); fits_write_img(fitsFilePtr, TUSHORT, 1, npix, _acqParams->imageBufferPtr, &status);
} }
@@ -218,7 +208,9 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
fits_update_key_str(fitsFilePtr, "ORIGIN", "SAO RAS", NULL, &status); fits_update_key_str(fitsFilePtr, "ORIGIN", "SAO RAS", NULL, &status);
fits_update_key_str(fitsFilePtr, "CREATOR", "RaptorEagleV control software", NULL, &status); fits_update_key_str(fitsFilePtr, "CREATOR", "RaptorEagleV control software", NULL, &status);
fits_update_key_str(fitsFilePtr, "FILE", _acqParams->filename.c_str(), "Original filename", &status); // fits_update_key_str(fitsFilePtr, "FILE", _acqParams->filename.c_str(), "Original filename", &status);
std::filesystem::path pt = _acqParams->filename;
fits_update_key_str(fitsFilePtr, "FILE", pt.filename().c_str(), "Original filename", &status);
fits_write_date(fitsFilePtr, &status); fits_write_date(fitsFilePtr, &status);
@@ -233,6 +225,9 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
if (_acqParams->startTime < _acqParams->abortTime) { // acquisition was aborted if (_acqParams->startTime < _acqParams->abortTime) { // acquisition was aborted
std::chrono::duration<double> real_exp = _acqParams->abortTime - _acqParams->startTime; std::chrono::duration<double> real_exp = _acqParams->abortTime - _acqParams->startTime;
serverPtr->logTrace("Acq. start time: {}; acq. abort time: {}", _acqParams->startTime,
_acqParams->abortTime);
serverPtr->logDebug("Exposure was stopped! Recompute the exposure duration to {} secs", real_exp.count());
_acqParams->expTime = real_exp.count(); _acqParams->expTime = real_exp.count();
} }
@@ -284,11 +279,11 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
if (!status) { if (!status) {
// keywords from user template file // keywords from user template file
if (_acqParams->templateFilename.size() && !status) { if (_acqParams->templateFilename.size() && !status) {
_manager->logDebug("Copy keywords from '{}' template file", _acqParams->templateFilename); serverPtr->logDebug("Copy keywords from '{}' template file", _acqParams->templateFilename);
fits_write_key_template(fitsFilePtr, _acqParams->templateFilename.c_str(), &status); fits_write_key_template(fitsFilePtr, _acqParams->templateFilename.c_str(), &status);
if (status) { // ignore possible errors if (status) { // ignore possible errors
fits_get_errstatus(status, err_str); fits_get_errstatus(status, err_str);
_manager->logWarn( serverPtr->logWarn(
"An error occured while copy keywords from the template file '{}' (err = {}, msg = {})! " "An error occured while copy keywords from the template file '{}' (err = {}, msg = {})! "
"Ignore!", "Ignore!",
_acqParams->templateFilename, status, err_str); _acqParams->templateFilename, status, err_str);
@@ -297,19 +292,25 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
} }
} }
// permanent keywords (may update keywords from template file!)
char card[80]; auto save_keywords = [&status, &err_str, &fitsFilePtr](auto& kwd_list, const std::string& mark) {
char kname[8]; char card[81];
char kname[9] = " ";
int k_type; int k_type;
for (auto& s : _manager->_permanentFitsKeywords) { if (kwd_list.size()) {
serverPtr->logDebug("Copy {} {} keywords", mark, kwd_list.size());
} else {
serverPtr->logDebug("There is no one permanent keyword! Skip!");
}
for (auto& s : kwd_list) {
fits_parse_template(s.data(), card, &k_type, &status); fits_parse_template(s.data(), card, &k_type, &status);
if (status) { // ignore possible errors if (status) { // ignore possible errors
fits_get_errstatus(status, err_str); fits_get_errstatus(status, err_str);
_manager->logWarn( serverPtr->logWarn(
"An error occured while writing permanent keyword card [{}] (err = {}, msg = {})! " "An error occured while writing {} keyword card [{}] (err = {}, msg = {})! "
"Ignore!", "Ignore!",
s, status, err_str); mark, s, status, err_str);
status = 0; status = 0;
} else { } else {
@@ -317,75 +318,127 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
kname[i] = card[i]; kname[i] = card[i];
} }
} }
serverPtr->logTrace("Try to update [{}] FITS card (name = '{}')", card, kname);
fits_update_card(fitsFilePtr, kname, card, &status); fits_update_card(fitsFilePtr, kname, card, &status);
if (status) {
fits_get_errstatus(status, err_str);
serverPtr->logWarn(
"An error occured while updating FITS card (name = '{}') (err = {}, msg = {})! Skip!",
kname, status, err_str);
} else {
serverPtr->logTrace("The FITS card (name = '{}') was updated successfully", kname);
} }
}
};
// permanent keywords (may update keywords from template file!)
save_keywords(_acqParams->permanentKeywords, "permanent");
// keyword from user (may update template file and permanent keywords!) // keyword from user (may update template file and permanent keywords!)
for (auto& s : _manager->_currentFitsKeywords) { save_keywords(_acqParams->currentKeywords, "current");
fits_parse_template(s.data(), card, &k_type, &status); }
if (status) { // ignore possible errors
fits_get_errstatus(status, err_str);
_manager->logWarn(
"An error occured while writing user keyword card [{}] (err = {}, msg = {})! "
"Ignore!",
s, status, err_str);
status = 0;
} else {
for (int i = 0; i < 8; ++i) {
kname[i] = card[i];
}
}
fits_update_card(fitsFilePtr, kname, card, &status);
}
}
// hardcoded camera hardware version info keywords // hardcoded camera hardware version info keywords
fits_update_key_ulng(fitsFilePtr, "SERNUM", (ULONGLONG)_manager->_cameraSerialNumber, "Camera serial number", fits_update_key_ulng(fitsFilePtr, "SERNUM", (ULONGLONG)serverPtr->_cameraSerialNumber, "Camera serial number",
&status); &status);
str = std::format("{}.{}", _manager->_microVersion[0], _manager->_microVersion[1]); str = std::format("{}.{}", serverPtr->_microVersion[0], serverPtr->_microVersion[1]);
fits_update_key_str(fitsFilePtr, "MICROVER", str.c_str(), "Camera microcontroller version", &status); fits_update_key_str(fitsFilePtr, "MICROVER", str.c_str(), "Camera microcontroller version", &status);
str = std::format("{}.{}", _manager->_FPGAVersion[0], _manager->_FPGAVersion[1]); str = std::format("{}.{}", serverPtr->_FPGAVersion[0], serverPtr->_FPGAVersion[1]);
fits_update_key_str(fitsFilePtr, "FPGAVER", str.c_str(), "Camera FPGA version", &status); fits_update_key_str(fitsFilePtr, "FPGAVER", str.c_str(), "Camera FPGA version", &status);
str = std::format("{}", _manager->_buildDate); str = std::format("{}", serverPtr->_buildDate);
fits_update_key_str(fitsFilePtr, "BUILDDAT", str.c_str(), "Camera build date, YY-MM-DD", &status); fits_update_key_str(fitsFilePtr, "BUILDDAT", str.c_str(), "Camera build date, YY-MM-DD", &status);
fits_update_key_str(fitsFilePtr, "BUILDCOD", sv2cstr(_manager->_buildCode), "Camera build code", &status); fits_update_key_str(fitsFilePtr, "BUILDCOD", sv2cstr(serverPtr->_buildCode), "Camera build code", &status);
fits_close_file(fitsFilePtr, &status); fits_close_file(fitsFilePtr, &status);
_status = STATUS_IDLE;
fits_get_errstatus(status, err_str); fits_get_errstatus(status, err_str);
if (status) { if (status) {
_manager->logError("An error occured while writing FITS file '{}'! FITS status = {} ({})", serverPtr->logError("An error occured while writing FITS file '{}'! FITS status = {} ({})",
_acqParams->filename, status, err_str); _acqParams->filename, status, err_str);
} else { } else {
_manager->logInfo("FITS file '{}' is saved", _acqParams->filename); serverPtr->logInfo("FITS file '{}' is saved", _acqParams->filename);
} }
});
std::lock_guard lock_guard(serverPtr->_acqProcessesMutex);
serverPtr->_acqRingFreeBufferPtrs.push(_acqParams->imageBufferPtr); // return buffer pointer to queue of free
serverPtr->logDebug("Return buffer address {} to queue of free", (void*)_acqParams->imageBufferPtr);
acqProcSptr.erase(self);
}).detach();
} }
void RaptorEagleCCD::AcquisitionProcess::stop(bool save) void RaptorEagleCCD::AcquisitionProcess::stop(bool save)
{ {
if (isAcqInProgress) { if (isAcqInProgress) {
_manager->logInfo("Abort current acquisition process!"); serverPtr->logInfo("Abort current acquisition process!");
_acqParams->saveInAbort = save; _acqParams->saveInAbort = save;
auto bytes = _manager->readRegisters({0xD4}); // curent trigger mode register // auto bytes = serverPtr->readRegisters({0xD4}); // current trigger mode register
bytes[0] |= CL_ABORT_CURRENT_EXP; // set abort acquisition bit // bytes[0] |= CL_ABORT_CURRENT_EXP; // set abort acquisition bit
_acqParams->abortTime = std::chrono::utc_clock::now(); // _acqParams->abortTime = std::chrono::utc_clock::now();
_manager->writeRegisters({0xD4}, bytes); // serverPtr->writeRegisters({0xD4}, bytes);
_acqParams->abortTime = serverPtr->setTriggerRegisterBit(CL_TRIGGER_MODE_ABORT_CURRENT_EXP_BIT);
} else { } else {
_manager->logWarn("There was no active acquisition process! Ignore!"); serverPtr->logWarn("There was no active acquisition process! Ignore!");
} }
} }
std::string RaptorEagleCCD::AcquisitionProcess::status()
{
std::lock_guard lock(_statusMutex);
std::string stat_str;
switch (_status) {
case STATUS_IDLE:
stat_str = std::format("{}", CAMERA_ATTR_CAMERA_STATUS_IDLE);
break;
case STATUS_ARMED: // actually it should not be! but it presents here for debug reasons
stat_str = std::format("{}", CAMERA_ATTR_CAMERA_STATUS_ARMED);
break;
case STATUS_ACQ: {
std::chrono::duration<double> curr_exp = std::chrono::utc_clock::now() - _acqParams->startTime;
auto remain_exp = _acqParams->expTime - curr_exp.count();
if (remain_exp < 0) {
_status = STATUS_READ;
stat_str = std::format("{} {}", CAMERA_ATTR_CAMERA_STATUS_READ, _acqParams->filename);
} else {
stat_str = std::format("{} {}", CAMERA_ATTR_CAMERA_STATUS_ACQ, remain_exp);
}
break;
}
case STATUS_READ:
stat_str = std::format("{} {}", CAMERA_ATTR_CAMERA_STATUS_READ, _acqParams->filename);
break;
case STATUS_SAVE:
stat_str = std::format("{} {}", CAMERA_ATTR_CAMERA_STATUS_SAVE, _acqParams->filename);
break;
default:
stat_str = "UNKNOWN"; // is should not be!!!
}
return stat_str;
}

View File

@@ -1,5 +1,7 @@
#include <cmath> #include <cmath>
#include <csignal>
#include <cstring> #include <cstring>
#include <filesystem>
#include "raptor_eagle_cameralink.h" #include "raptor_eagle_cameralink.h"
#include "raptor_eagle_ccd.h" #include "raptor_eagle_ccd.h"
@@ -161,6 +163,8 @@ RaptorEagleCCD::RaptorEagleCCD(const adc::traits::adc_input_char_range auto& epi
logInfo("Set video format filename: {}", _epixFmtVideoFilename); logInfo("Set video format filename: {}", _epixFmtVideoFilename);
} }
AcquisitionProcess::serverPtr = this;
initAttrComm(); initAttrComm();
openPIXCI(); openPIXCI();
@@ -175,8 +179,28 @@ RaptorEagleCCD::RaptorEagleCCD(std::shared_ptr<spdlog::logger> logger)
RaptorEagleCCD::~RaptorEagleCCD() RaptorEagleCCD::~RaptorEagleCCD()
{ {
if (AcquisitionProcess::isAcqInProgress) {
(*this)[CAMERA_CMD_ABORT_EXP];
}
// wait for detached acquisition threads?
if (AcquisitionProcess::acqProcSptr.size()) {
logInfo("DTOR: waiting for the end of the acquisition process {} ...",
AcquisitionProcess::acqProcSptr.size() > 1 ? "threads" : "thread");
auto start = std::chrono::utc_clock::now();
while (AcquisitionProcess::acqProcSptr.size()) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
if ((std::chrono::utc_clock::now() - start) >= std::chrono::milliseconds(2000)) {
break;
}
}
}
closePIXCI(); closePIXCI();
logDebug("DTOR: Delete RaptorEagleCCD class instance"); logDebug("DTOR: Delete RaptorEagleCCD class instance");
} }
@@ -410,6 +434,14 @@ bool RaptorEagleCCD::initCamera(int unitmap)
throw std::system_error(RaptorEagleCCDError::ERROR_INVALID_UNITMAP); throw std::system_error(RaptorEagleCCDError::ERROR_INVALID_UNITMAP);
} }
std::string status = (*this)[CAMERA_ATTR_CAMERA_STATUS];
if (status != CAMERA_ATTR_CAMERA_STATUS_IDLE) {
logWarn("It seems camera current status is not IDLE! Abort possible exposure!");
(*this)[CAMERA_CMD_ABORT_EXP];
// _cameraStatus = CAMERA_ATTR_CAMERA_STATUS_IDLE;
}
AcquisitionProcess::isAcqInProgress = false; // ??????!!!!!!!!!!!
_cameraUnitmap = unitmap; _cameraUnitmap = unitmap;
// configure CameraLink serial connection // configure CameraLink serial connection
@@ -442,6 +474,28 @@ bool RaptorEagleCCD::initCamera(int unitmap)
xclibApiCall(_bitsPerPixel = pxd_imageBdim(), "pxd_imageBdim()"); xclibApiCall(_bitsPerPixel = pxd_imageBdim(), "pxd_imageBdim()");
xclibApiCall(_imageFrameBuffNumber = pxd_imageZdim(), "pxd_imageZdim()"); xclibApiCall(_imageFrameBuffNumber = pxd_imageZdim(), "pxd_imageZdim()");
// memory
if (_acqRingBuffer.empty()) { // the first init. allocate ring buffer
logDebug("Allocate memory for acquisition ring buffer");
_acqRingBuffer.resize(DEFAULT_ACQ_RING_BUFFER_SIZE);
try {
for (auto& uptr : _acqRingBuffer) {
uptr.reset(new ushort[_dimCCD[0] * _dimCCD[1]]); // full CCD frame
_acqRingFreeBufferPtrs.push(uptr.get());
}
} catch (const std::bad_alloc&) {
logError("Cannot allocate memory for ring buffer!");
return false;
}
} else { // re-initialization. just resize ring buffer to initial default size
_acqRingBuffer.resize(DEFAULT_ACQ_RING_BUFFER_SIZE);
_acqRingFreeBufferPtrs = std::queue<ushort*>();
for (auto& uptr : _acqRingBuffer) {
_acqRingFreeBufferPtrs.push(uptr.get());
}
}
logDebug("------- CCD and grabber hardware info -------"); logDebug("------- CCD and grabber hardware info -------");
logDebug("CCD full-frame dimension [{}, {}] pixels", _dimCCD[0], _dimCCD[1]); logDebug("CCD full-frame dimension [{}, {}] pixels", _dimCCD[0], _dimCCD[1]);
logDebug("CCD bits per pixel: {}", _bitsPerPixel); logDebug("CCD bits per pixel: {}", _bitsPerPixel);
@@ -472,6 +526,19 @@ bool RaptorEagleCCD::initCamera(int unitmap)
// setTriggerRegisterBit(CL_TRIGGER_MODE_ABORT_CURRENT_EXP_BIT); // setTriggerRegisterBit(CL_TRIGGER_MODE_ABORT_CURRENT_EXP_BIT);
_permanentFitsKeywords.clear();
_currentFitsKeywords.clear();
// xclibApiCall<true>(pxd_eventFieldClose(_cameraUnitmap, SIGUSR2),
// std::format("pxd_eventFieldClose({}, {})", _cameraUnitmap, SIGUSR2));
// xclibApiCall(pxd_eventFieldCreate(_cameraUnitmap, SIGUSR2, NULL),
// std::format("pxd_eventFieldCreate({}, {}, NULL)", _cameraUnitmap, SIGUSR2));
// std::signal(SIGUSR2, [this](int signo) { logDebug("SIGUSR2 SIGNAL IS RECEIVED!!!");
// });
logInfo("Camera with unitmap '{}' is initialized", _cameraUnitmap); logInfo("Camera with unitmap '{}' is initialized", _cameraUnitmap);
return true; return true;
@@ -488,8 +555,8 @@ void RaptorEagleCCD::openPIXCI()
} else { } else {
xclibApiCall(pxd_PIXCIopen("", "DEFAULT", ""), "pxd_PIXCIopen(\"\", \"DEFAULT\", \"\")"); xclibApiCall(pxd_PIXCIopen("", "DEFAULT", ""), "pxd_PIXCIopen(\"\", \"DEFAULT\", \"\")");
// #include DEFAULT_EPIX_VIDEO_FMT_FILE // exported from XCAP (Linux 64-bit!): bin 1x1, full CCD frame #include DEFAULT_EPIX_VIDEO_FMT_FILE // exported from XCAP (Linux 64-bit!): bin 1x1, full CCD frame
#include "raptor_eagle-v.fmt" // exported from XCAP (Linux 64-bit!): bin 1x1, full CCD frame // #include "raptor_eagle-v.fmt" // exported from XCAP (Linux 64-bit!): bin 1x1, full CCD frame
pxd_videoFormatAsIncludedInit(0); pxd_videoFormatAsIncludedInit(0);
xclibApiCall(pxd_videoFormatAsIncluded(0), "pxd_videoFormatAsIncluded(0)"); xclibApiCall(pxd_videoFormatAsIncluded(0), "pxd_videoFormatAsIncluded(0)");
} }
@@ -518,10 +585,11 @@ size_t RaptorEagleCCD::clRead(byte_seq_t& bytes)
// how many byte are available // how many byte are available
xclibApiCall(nbytes = pxd_serialRead(_cameraUnitmap, 0, nullptr, 0), xclibApiCall(nbytes = pxd_serialRead(_cameraUnitmap, 0, nullptr, 0),
std::format("pxd_serialRead({}, 0, NULL, 0)", _cameraUnitmap)); std::format("pxd_serialRead({}, 0, NULL, 0)", _cameraUnitmap), spdlog::level::trace);
if (!nbytes) { if (!nbytes) {
logWarn("There are no bytes in Rx-buffer! Polling buffer during {} ...", CL_DEFAULT_TIMEOUT); // logWarn("There are no bytes in Rx-buffer! Polling buffer during {} ...", CL_DEFAULT_TIMEOUT);
logTrace("There are no bytes in Rx-buffer! Polling buffer during {} ...", CL_DEFAULT_TIMEOUT);
while ((std::chrono::steady_clock::now() - start_tp) <= CL_DEFAULT_TIMEOUT) { while ((std::chrono::steady_clock::now() - start_tp) <= CL_DEFAULT_TIMEOUT) {
std::this_thread::sleep_for(std::chrono::milliseconds(5)); std::this_thread::sleep_for(std::chrono::milliseconds(5));
nbytes = pxd_serialRead(_cameraUnitmap, 0, nullptr, 0); nbytes = pxd_serialRead(_cameraUnitmap, 0, nullptr, 0);
@@ -538,7 +606,7 @@ size_t RaptorEagleCCD::clRead(byte_seq_t& bytes)
// here the call is only for logging purpose // here the call is only for logging purpose
xclibApiCall(nbytes = pxd_serialRead(_cameraUnitmap, 0, nullptr, 0), xclibApiCall(nbytes = pxd_serialRead(_cameraUnitmap, 0, nullptr, 0),
std::format("pxd_serialRead({}, 0, NULL, 0)", _cameraUnitmap)); std::format("pxd_serialRead({}, 0, NULL, 0)", _cameraUnitmap), spdlog::level::trace);
} }
@@ -550,7 +618,8 @@ size_t RaptorEagleCCD::clRead(byte_seq_t& bytes)
xclibApiCall(pxd_serialRead(_cameraUnitmap, 0, (char*)bytes.data(), nbytes), xclibApiCall(pxd_serialRead(_cameraUnitmap, 0, (char*)bytes.data(), nbytes),
std::format("pxd_serialRead({}, 0, {}, {}) -> [{}]", _cameraUnitmap, (void*)bytes.data(), nbytes, std::format("pxd_serialRead({}, 0, {}, {}) -> [{}]", _cameraUnitmap, (void*)bytes.data(), nbytes,
details::formatByteArr(bytes | std::views::take(nbytes)))); details::formatByteArr(bytes | std::views::take(nbytes))),
spdlog::level::trace);
// if (_loggerSPtr->level() == spdlog::level::trace) { // if (_loggerSPtr->level() == spdlog::level::trace) {
@@ -613,7 +682,7 @@ size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
// how many bytes are available in Tx-buffer // how many bytes are available in Tx-buffer
xclibApiCall(nbytes = pxd_serialWrite(_cameraUnitmap, 0, nullptr, 0), xclibApiCall(nbytes = pxd_serialWrite(_cameraUnitmap, 0, nullptr, 0),
std::format("pxd_serialWrite({}, 0, NULL, 0)", _cameraUnitmap)); std::format("pxd_serialWrite({}, 0, NULL, 0)", _cameraUnitmap), spdlog::level::trace);
if (nbytes) { if (nbytes) {
if (nbytes < (bytes.size() + tr_nbytes)) { if (nbytes < (bytes.size() + tr_nbytes)) {
@@ -628,7 +697,8 @@ size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
xclibApiCall(nbytes = pxd_serialWrite(_cameraUnitmap, 0, (char*)bytes.data(), bytes.size()), xclibApiCall(nbytes = pxd_serialWrite(_cameraUnitmap, 0, (char*)bytes.data(), bytes.size()),
std::format("pxd_serialWrite({}, 0, [{}], {})", _cameraUnitmap, details::formatByteArr(bytes), std::format("pxd_serialWrite({}, 0, [{}], {})", _cameraUnitmap, details::formatByteArr(bytes),
bytes.size())); bytes.size()),
spdlog::level::trace);
// xclibApiCall( // xclibApiCall(
// nbytes = pxd_serialWrite(_cameraUnitmap, 0, (char*)bytes.data(), bytes.size()), // nbytes = pxd_serialWrite(_cameraUnitmap, 0, (char*)bytes.data(), bytes.size()),
// std::format("pxd_serialWrite({}, 0, {}, {})", _cameraUnitmap, (void*)bytes.data(), bytes.size())); // std::format("pxd_serialWrite({}, 0, {}, {})", _cameraUnitmap, (void*)bytes.data(), bytes.size()));
@@ -636,16 +706,19 @@ size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
// send trailing ETX and possible checksum bytes // send trailing ETX and possible checksum bytes
if (tr_nbytes > 1) { if (tr_nbytes > 1) {
logDebug("Write trailing ETX and checksum bytes"); // logDebug("Write trailing ETX and checksum bytes");
logTrace("Write trailing ETX and checksum bytes");
} else { } else {
logDebug("Write trailing ETX byte"); // logDebug("Write trailing ETX byte");
logTrace("Write trailing ETX byte");
} }
xclibApiCall(pxd_serialWrite(_cameraUnitmap, 0, (char*)etx_checksum_bytes, tr_nbytes), xclibApiCall(pxd_serialWrite(_cameraUnitmap, 0, (char*)etx_checksum_bytes, tr_nbytes),
std::format("pxd_serialWrite({}, 0, [{}], {})", _cameraUnitmap, std::format("pxd_serialWrite({}, 0, [{}], {})", _cameraUnitmap,
details::formatByteArr(std::string_view((const char*)etx_checksum_bytes, 2) | details::formatByteArr(std::string_view((const char*)etx_checksum_bytes, 2) |
std::views::take(tr_nbytes)), std::views::take(tr_nbytes)),
tr_nbytes)); tr_nbytes),
spdlog::level::trace);
// xclibApiCall( // xclibApiCall(
// pxd_serialWrite(_cameraUnitmap, 0, (char*)etx_checksum_bytes, tr_nbytes), // pxd_serialWrite(_cameraUnitmap, 0, (char*)etx_checksum_bytes, tr_nbytes),
// std::format("pxd_serialWrite({}, 0, {}, {})", _cameraUnitmap, (void*)etx_checksum_bytes, tr_nbytes)); // std::format("pxd_serialWrite({}, 0, {}, {})", _cameraUnitmap, (void*)etx_checksum_bytes, tr_nbytes));
@@ -937,11 +1010,62 @@ void RaptorEagleCCD::startAquisition()
} }
if (_acqRingFreeBufferPtrs.empty()) {
logDebug("There is no free image buffers! Try to shrink ring buffer ...");
std::lock_guard lock_guard(_acqProcessesMutex);
if (_acqRingBuffer.size() < DEFAULT_ACQ_RING_BUFFER_MAX_SIZE) { // shrink
size_t N = 0;
for (auto i = _acqRingBuffer.size(); i < DEFAULT_ACQ_RING_BUFFER_MAX_SIZE; ++i, ++N) {
if (N == DEFAULT_ACQ_RING_BUFFER_SIZE) {
break;
}
_acqRingBuffer.emplace_back(new ushort[_dimCCD[0] * _dimCCD[1]]);
_acqRingFreeBufferPtrs.push(_acqRingBuffer.back().get());
}
logDebug("{} new image buffers were allocated", N);
} else {
logError("Ring buffer exceeded maximum size!");
throw std::system_error(RaptorEagleCCDError::ERROR_NO_FREE_BUFFER);
}
}
// check filesystem permissions
std::string fname = (*this)[CAMERA_ATTR_FITS_FILENAME];
std::error_code ec;
std::filesystem::path pt(fname);
if (pt.filename().empty()) {
fname = "";
} else {
auto pt_p = pt.parent_path();
if (pt_p.empty()) {
pt_p = ".";
}
auto pt_cn = std::filesystem::canonical(pt_p, ec);
if (ec) {
logError("Invalid FITS-image filename path: {}", pt.c_str());
throw std::system_error(RaptorEagleCCDError::ERROR_INVALID_PATH);
}
// still only for POSIX OSes
int res = access(pt_cn.c_str(), W_OK | X_OK);
if (res == -1) {
logError("Invalid FITS-image path! Insufficient filesystem permissions!");
throw std::system_error(RaptorEagleCCDError::ERROR_INSUFFICIENT_FILESYSTEM_PERMISSIONS);
}
fname = pt_cn / pt.filename(); // use canonical file path
}
logInfo("Start acquisition process ..."); logInfo("Start acquisition process ...");
auto acq_pars = std::make_shared<acq_params_t>(acq_params_t({ auto acq_pars = std::make_shared<acq_params_t>(
.startTime = std::chrono::utc_clock::time_point(), acq_params_t({.startTime = std::chrono::utc_clock::time_point(),
.abortTime = std::chrono::utc_clock::time_point(), .abortTime = std::chrono::utc_clock::time_point(),
.saveInAbort = false, .saveInAbort = false,
.expTime = (*this)[CAMERA_ATTR_EXPTIME], .expTime = (*this)[CAMERA_ATTR_EXPTIME],
@@ -959,30 +1083,19 @@ void RaptorEagleCCD::startAquisition()
.tecSetPoint = (*this)[CAMERA_ATTR_TECPOINT], .tecSetPoint = (*this)[CAMERA_ATTR_TECPOINT],
.tecState = (*this)[CAMERA_ATTR_TECSTATE] == CAMERA_ATTR_TECSTATE_ON ? true : false, .tecState = (*this)[CAMERA_ATTR_TECSTATE] == CAMERA_ATTR_TECSTATE_ON ? true : false,
.pcbTemp = (*this)[CAMERA_ATTR_PCB_TEMP], .pcbTemp = (*this)[CAMERA_ATTR_PCB_TEMP],
.filename = (*this)[CAMERA_ATTR_FITS_FILENAME], // .filename = (*this)[CAMERA_ATTR_FITS_FILENAME],
.filename = fname,
.templateFilename = (*this)[CAMERA_ATTR_FITS_TEMPLATE], .templateFilename = (*this)[CAMERA_ATTR_FITS_TEMPLATE],
.permanentKeywords = _permanentFitsKeywords, // copy .permanentKeywords = _permanentFitsKeywords, // copy
.currentKeywords = std::move(_currentFitsKeywords) // move!!! .currentKeywords = std::move(_currentFitsKeywords), // move!!!
})); .imageBufferPtr = _acqRingFreeBufferPtrs.front()}));
logDebug("Current acquisition buffer address: {}", (void*)_acqRingFreeBufferPtrs.front());
_acqRingFreeBufferPtrs.pop();
// adjust geometry // adjust geometry
// auto dv = std::div(_dimCCD[0] - acq_pars->roiStartX, acq_pars->binX);
// auto width_max = dv.quot + (dv.rem ? 1 : 0);
// dv = std::div(_dimCCD[1] - acq_pars->roiStartY, acq_pars->binY);
// auto height_max = dv.quot + (dv.rem ? 1 : 0);
// if (acq_pars->roiWidth > width_max) {
// // acq_pars->roiWidth = width_max;
// logDebug("Adjust ROI width to {}", acq_pars->roiWidth);
// // (*this)[CAMERA_ATTR_ROI_WIDTH] = width_max;
// }
// if (acq_pars->roiHeight > height_max) {
// // acq_pars->roiHeight = height_max;
// logDebug("Adjust ROI height to {}", acq_pars->roiHeight);
// // (*this)[CAMERA_ATTR_ROI_HEIGHT] = height_max;
// }
auto xmax = acq_pars->roiStartX + acq_pars->roiWidth; auto xmax = acq_pars->roiStartX + acq_pars->roiWidth;
auto ymax = acq_pars->roiStartY + acq_pars->roiHeight; auto ymax = acq_pars->roiStartY + acq_pars->roiHeight;
@@ -997,29 +1110,20 @@ void RaptorEagleCCD::startAquisition()
} }
std::lock_guard lock_guard(_acqProcessesMutex); auto sptr = std::make_shared<AcquisitionProcess>();
sptr->start(acq_pars); // asynchronous call!!!
auto sptr = std::make_shared<AcquisitionProcess>(this);
for (auto it = _acqProcesses.begin(); it != _acqProcesses.end();) {
if (it->expired()) {
it = _acqProcesses.erase(it);
} else {
break;
}
}
_acqProcesses.emplace_back(sptr);
// arm grabber here
sptr->start(acq_pars);
// bits.set(CL_TRIGGER_MODE_SNAPSHOT_BIT); // start snapshot bit
// bytes[0] = static_cast<uint8_t>(bits.to_ulong());
// acq_pars->startTime = std::chrono::utc_clock::now();
// writeRegisters({0xD4}, bytes); // write to trigger mode register (start snapshot)
// start acquisition here // start acquisition here
auto start = std::chrono::utc_clock::now();
while (sptr->_status != AcquisitionProcess::STATUS_ARMED) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if ((std::chrono::utc_clock::now() - start) > CAMERA_START_ACQ_TIMEOUT) {
logError("CANNOT START ACQUIRING!!!");
throw std::system_error(RaptorEagleCCDError::ERROR_CANNOT_START_ACQUISITION);
}
}
acq_pars->startTime = setTriggerRegisterBit(CL_TRIGGER_MODE_SNAPSHOT_BIT); acq_pars->startTime = setTriggerRegisterBit(CL_TRIGGER_MODE_SNAPSHOT_BIT);
sptr->_status = AcquisitionProcess::STATUS_ACQ;
int status; int status;
xclibApiCall(status = pxd_goneLive(_cameraUnitmap, 0), std::format("pxd_goneLive({}, 0)", _cameraUnitmap)); xclibApiCall(status = pxd_goneLive(_cameraUnitmap, 0), std::format("pxd_goneLive({}, 0)", _cameraUnitmap));
@@ -1033,13 +1137,10 @@ void RaptorEagleCCD::stopAcquisition(bool save_acq)
{ {
std::lock_guard lock_guard(_acqProcessesMutex); std::lock_guard lock_guard(_acqProcessesMutex);
for (auto it = _acqProcesses.begin(); it != _acqProcesses.end();) { for (auto& sptr : AcquisitionProcess::acqProcSptr) {
if (it->expired()) { if (sptr->_status == AcquisitionProcess::STATUS_ACQ) {
it = _acqProcesses.erase(it);
} else {
auto sptr = it->lock();
sptr->stop(save_acq); sptr->stop(save_acq);
return; // there was only the single active aquisition, so exit here! return; // the only one active acquisition process
} }
} }
@@ -1170,6 +1271,39 @@ void RaptorEagleCCD::initAttrComm()
/* ------- ATTRIBUTES ------- */ /* ------- ATTRIBUTES ------- */
/* CAMERA CURRENT STATUS (READ-ONLY) */
addAttribute(CAMERA_ATTR_CAMERA_STATUS, [this]() {
std::lock_guard lock_guard(_acqProcessesMutex);
std::string s;
if (AcquisitionProcess::isAcqInProgress) {
s = CAMERA_ATTR_CAMERA_STATUS_ACQ;
s += ",";
}
if (AcquisitionProcess::acqProcSptr.size()) {
for (auto& sptr : AcquisitionProcess::acqProcSptr) {
if (sptr->_status != AcquisitionProcess::STATUS_ACQ) {
s += sptr->status() + ",";
}
}
}
if (!s.empty()) {
s.resize(s.size() - 1); // delete trailing ","
} else {
s = std::string{CAMERA_ATTR_CAMERA_STATUS_IDLE.begin(), CAMERA_ATTR_CAMERA_STATUS_IDLE.end()};
}
logTrace("Return current camera status as {}", s);
return s;
});
/* CURRENT FITS IMAGE FILENAME AND ITS HEADER TEMPLATE, PERMANENT AND CURRENT USER FITS KEYWORDS */ /* CURRENT FITS IMAGE FILENAME AND ITS HEADER TEMPLATE, PERMANENT AND CURRENT USER FITS KEYWORDS */
addAttribute( addAttribute(
@@ -1191,11 +1325,13 @@ void RaptorEagleCCD::initAttrComm()
addAttribute( addAttribute(
CAMERA_ATTR_FITS_TEMPLATE, CAMERA_ATTR_FITS_TEMPLATE,
[this]() { [this]() {
logTrace("Return current FITS-image header template filename as {}", _currentTemplateFile); logTrace("Return current FITS-image header template filename as {}",
_currentTemplateFile.size() ? _currentTemplateFile : "<empty string>");
return _currentTemplateFile; return _currentTemplateFile;
}, },
[this](const std::string& filename) { [this](const std::string& filename) {
logDebug("Set current FITS-image header template filename to {}", filename); logDebug("Set current FITS-image header template filename to {}",
filename.size() ? filename : "<empty string>");
_currentTemplateFile = filename; _currentTemplateFile = filename;
}); });
@@ -1727,7 +1863,7 @@ void RaptorEagleCCD::initAttrComm()
val = CAMERA_ATTR_STR_INVALID; val = CAMERA_ATTR_STR_INVALID;
} }
logTrace("Return readout rate as '()' string", val); logTrace("Return readout rate as '{}' string", val);
return val; return val;
}, },

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <queue>
#include <set>
#ifdef USE_SPDLOG_LIBRARY #ifdef USE_SPDLOG_LIBRARY
#include <spdlog/sinks/null_sink.h> #include <spdlog/sinks/null_sink.h>
#endif #endif
@@ -33,6 +35,7 @@ public:
static constexpr std::string_view USER_FITS_KEY_SEP_SEQ{"\t"}; static constexpr std::string_view USER_FITS_KEY_SEP_SEQ{"\t"};
static constexpr size_t DEFAULT_ACQ_RING_BUFFER_SIZE = 3; static constexpr size_t DEFAULT_ACQ_RING_BUFFER_SIZE = 3;
static constexpr size_t DEFAULT_ACQ_RING_BUFFER_MAX_SIZE = 100;
/* some Eagle V camera constants */ /* some Eagle V camera constants */
// static constexpr double EAGLE_CAMERA_MAX_EXPTIME = 27487.7906944; // in seconds (0xFFFFFFFFFF * 25nsec) // static constexpr double EAGLE_CAMERA_MAX_EXPTIME = 27487.7906944; // in seconds (0xFFFFFFFFFF * 25nsec)
@@ -68,6 +71,9 @@ public:
std::chrono::milliseconds(1000)}; std::chrono::milliseconds(1000)};
// wait timeout to start acquisition (deadlock?!!)
static constexpr std::chrono::milliseconds CAMERA_START_ACQ_TIMEOUT{1000}; // 1 second
// additive constant to timeout for capture process (see pxd_doSnap XCLIB function) // additive constant to timeout for capture process (see pxd_doSnap XCLIB function)
// this constant will be added to exposure to compute actual timeout // this constant will be added to exposure to compute actual timeout
static constexpr std::chrono::milliseconds CAMERA_CAPTURE_TIMEOUT_ADD_CONSTANT{240000}; // 4 mins static constexpr std::chrono::milliseconds CAMERA_CAPTURE_TIMEOUT_ADD_CONSTANT{240000}; // 4 mins
@@ -103,6 +109,8 @@ public:
static constexpr std::string_view CAMERA_ATTR_PERM_KEYW{"PERM_FITS_KEY"}; static constexpr std::string_view CAMERA_ATTR_PERM_KEYW{"PERM_FITS_KEY"};
static constexpr std::string_view CAMERA_ATTR_CURR_KEYW{"CURR_FITS_KEY"}; static constexpr std::string_view CAMERA_ATTR_CURR_KEYW{"CURR_FITS_KEY"};
static constexpr std::string_view CAMERA_ATTR_CAMERA_STATUS{"CAMERA_STATUS"};
static constexpr std::string_view CAMERA_CMD_INITCAM{"INITCAM"}; static constexpr std::string_view CAMERA_CMD_INITCAM{"INITCAM"};
static constexpr std::string_view CAMERA_CMD_START_EXP{"STARTEXP"}; static constexpr std::string_view CAMERA_CMD_START_EXP{"STARTEXP"};
static constexpr std::string_view CAMERA_CMD_STOP_EXP{"STOPEXP"}; static constexpr std::string_view CAMERA_CMD_STOP_EXP{"STOPEXP"};
@@ -144,6 +152,15 @@ public:
// idle // idle
static constexpr std::string_view CAMERA_ATTR_TRIGGER_MODE_IDLE{"IDLE"}; static constexpr std::string_view CAMERA_ATTR_TRIGGER_MODE_IDLE{"IDLE"};
// camera status
static constexpr std::string_view CAMERA_ATTR_CAMERA_STATUS_IDLE{"IDLE"};
static constexpr std::string_view CAMERA_ATTR_CAMERA_STATUS_ARMED{"ARMED"};
static constexpr std::string_view CAMERA_ATTR_CAMERA_STATUS_ACQ{"ACQ"}; // camera is acquiring
static constexpr std::string_view CAMERA_ATTR_CAMERA_STATUS_READ{"READING"}; // camera is reading from CCD
static constexpr std::string_view CAMERA_ATTR_CAMERA_STATUS_SAVE{"SAVING"}; // camera is saving to FITS file
RaptorEagleCCD(const adc::traits::adc_input_char_range auto& epix_video_fmt_filename, RaptorEagleCCD(const adc::traits::adc_input_char_range auto& epix_video_fmt_filename,
std::shared_ptr<spdlog::logger> logger = spdlog::null_logger_mt("EAGLE_CCD_NULL_LOGGER")); std::shared_ptr<spdlog::logger> logger = spdlog::null_logger_mt("EAGLE_CCD_NULL_LOGGER"));
@@ -187,6 +204,7 @@ private:
// std::unique_ptr<ushort> imageBuffer; // std::unique_ptr<ushort> imageBuffer;
// size_t imageBufferSize; // size_t imageBufferSize;
// size_t imageBufferRows; // size_t imageBufferRows;
ushort* imageBufferPtr;
}; };
class AcquisitionProcess : public std::enable_shared_from_this<AcquisitionProcess> class AcquisitionProcess : public std::enable_shared_from_this<AcquisitionProcess>
@@ -194,23 +212,26 @@ private:
friend class RaptorEagleCCD; friend class RaptorEagleCCD;
public: public:
AcquisitionProcess(RaptorEagleCCD*); AcquisitionProcess();
~AcquisitionProcess(); ~AcquisitionProcess();
void start(const std::shared_ptr<acq_params_t>& params); // asynchronous method! void start(const std::shared_ptr<acq_params_t>& params); // asynchronous method!
void stop(bool save = true); void stop(bool save = true);
std::string status();
// static std::string acqProcStatus();
private: private:
inline static RaptorEagleCCD* serverPtr = nullptr;
inline static std::atomic_bool isAcqInProgress = false; inline static std::atomic_bool isAcqInProgress = false;
RaptorEagleCCD* _manager; inline static std::set<std::shared_ptr<AcquisitionProcess>> acqProcSptr{};
std::shared_ptr<acq_params_t> _acqParams{};
std::unique_ptr<ushort[]> _imageBuffer{};
size_t _imageBufferSize = 0;
size_t _imageBufferRows = 0;
std::future<void> _snapAndCopyFuture; std::shared_ptr<acq_params_t> _acqParams{};
// std::future<void> _saveFitsFileFuture;
enum int8_t { STATUS_IDLE, STATUS_ARMED, STATUS_ACQ, STATUS_READ, STATUS_SAVE };
std::atomic_int8_t _status = STATUS_IDLE;
std::mutex _statusMutex;
}; };
std::string _epixFmtVideoFilename; std::string _epixFmtVideoFilename;
@@ -238,11 +259,11 @@ private:
std::atomic_size_t _frameNumbers; std::atomic_size_t _frameNumbers;
std::string _currentFitsFile; // current acquisition FITS filename std::string _currentFitsFile; // current acquisition FITS filename
std::string _currentTemplateFile; // CFITSIO template filename std::string _currentTemplateFile; // CFITSIO template filename
std::vector<std::string> _currentFitsKeywords; // current acquisition FITS keywords std::vector<std::string> _currentFitsKeywords{}; // current acquisition FITS keywords
std::vector<std::string> _permanentFitsKeywords; // permanent user FITS keywords std::vector<std::string> _permanentFitsKeywords{}; // permanent user FITS keywords
// std::list<std::unique_ptr<ushort>> _acqRingBuffer; std::vector<std::unique_ptr<ushort[]>> _acqRingBuffer{};
std::list<std::pair<std::unique_ptr<ushort>, size_t>> _acqRingBuffer; std::queue<ushort*> _acqRingFreeBufferPtrs{};
// hardware version info // hardware version info
@@ -264,7 +285,7 @@ private:
// acquisition process members // acquisition process members
std::mutex _acqProcessesMutex; std::mutex _acqProcessesMutex;
std::list<std::weak_ptr<AcquisitionProcess>> _acqProcesses{}; // std::list<std::weak_ptr<AcquisitionProcess>> _acqProcesses{};
// std::future<void> _doSnapAndCopyFuture; // std::future<void> _doSnapAndCopyFuture;
// std::future<void> _saveFitsFile; // std::future<void> _saveFitsFile;
@@ -338,7 +359,9 @@ private:
// logging helper methods // logging helper methods
template <bool NOEXCEPT = false> template <bool NOEXCEPT = false>
void xclibApiCall(int err, const adc::traits::adc_input_char_range auto& func_name) void xclibApiCall(int err,
const adc::traits::adc_input_char_range auto& func_name,
loglevel_t log_level = spdlog::level::debug)
{ {
if (err < 0) { if (err < 0) {
auto s_err = pxd_mesgErrorCode(err); auto s_err = pxd_mesgErrorCode(err);
@@ -354,17 +377,19 @@ private:
} }
} else { } else {
if (std::ranges::size(func_name)) { if (std::ranges::size(func_name)) {
logDebug("XCLIB API call ('{}') finished successfully (exitcode = {})", func_name, err); // logDebug("XCLIB API call ('{}') finished successfully (exitcode = {})", func_name, err);
logMessage(log_level, "XCLIB API call ('{}') finished successfully (exitcode = {})", func_name, err);
} else { } else {
logDebug("XCLIB API call finished successfully (exitcode = {})", err); // logDebug("XCLIB API call finished successfully (exitcode = {})", err);
logMessage(log_level, "XCLIB API call finished successfully (exitcode = {})", err);
} }
} }
} }
template <bool NOEXCEPT = false> template <bool NOEXCEPT = false>
void xclibApiCall(int err) void xclibApiCall(int err, loglevel_t log_level = spdlog::level::debug)
{ {
xclibApiCall<NOEXCEPT>(err, std::string_view("")); xclibApiCall<NOEXCEPT>(err, std::string_view(""), log_level);
} }
}; };

View File

@@ -15,7 +15,11 @@ enum class RaptorEagleCCDError : int {
ERROR_CANNOT_RESET_MICRO, ERROR_CANNOT_RESET_MICRO,
ERROR_CANNOT_RESET_FPGA, ERROR_CANNOT_RESET_FPGA,
ERROR_EXT_TRIGGER_MODE, ERROR_EXT_TRIGGER_MODE,
ERROR_ACQUISITION_IN_PROGRESS ERROR_ACQUISITION_IN_PROGRESS,
ERROR_CANNOT_START_ACQUISITION,
ERROR_INVALID_PATH,
ERROR_INSUFFICIENT_FILESYSTEM_PERMISSIONS,
ERROR_NO_FREE_BUFFER
}; };
@@ -62,6 +66,14 @@ struct RaptorEagleCCDErrorCategory : std::error_category {
return "try to use software trigger while external trigger mode is enabled"; return "try to use software trigger while external trigger mode is enabled";
case RaptorEagleCCDError::ERROR_ACQUISITION_IN_PROGRESS: case RaptorEagleCCDError::ERROR_ACQUISITION_IN_PROGRESS:
return "acquisition is in progress"; return "acquisition is in progress";
case RaptorEagleCCDError::ERROR_CANNOT_START_ACQUISITION:
return "cannot start acquisition";
case RaptorEagleCCDError::ERROR_INVALID_PATH:
return "invalid filesystem path";
case RaptorEagleCCDError::ERROR_INSUFFICIENT_FILESYSTEM_PERMISSIONS:
return "insufficient filesystem permissions";
case RaptorEagleCCDError::ERROR_NO_FREE_BUFFER:
return "there is no free buffer in ring";
default: default:
return "UNKNOWN ERROR"; return "UNKNOWN ERROR";
} }

View File

@@ -16,7 +16,7 @@ int main(int argc, char* argv[])
int exit_code = 0; int exit_code = 0;
asio::io_context ctx; asio::io_context ctx;
std::vector<std::thread> threads; std::vector<std::thread> threads;
std::size_t Nthreads = 2; std::size_t Nthreads = 3;
/* COMMANDLINE OPTS */ /* COMMANDLINE OPTS */
cxxopts::Options options(argv[0], "ADC-library test device network server (ASIO implementation)\n"); cxxopts::Options options(argv[0], "ADC-library test device network server (ASIO implementation)\n");