Compare commits
23 Commits
4690af8878
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| ce29f05ca0 | |||
| a6f15e234f | |||
|
|
7497b72b32 | ||
| cf3e34f88c | |||
| 8c319780dc | |||
|
|
d103ed2887 | ||
| c1b7d249da | |||
| 99e072be78 | |||
| 69bba48ce1 | |||
| ea9b099d81 | |||
| 0ece075bb8 | |||
|
|
f068cbb44b | ||
| 77131074e7 | |||
| 66e0b6c71e | |||
| 610d09ee98 | |||
| bb28f0aa03 | |||
| 34959c632d | |||
| 6c45f04448 | |||
| b570a4e7ea | |||
| 50e457f046 | |||
| 3fee70b445 | |||
| 86eb2b8ba7 | |||
| 192636aeda |
@@ -18,33 +18,50 @@ find_package(cxxopts CONFIG)
|
||||
|
||||
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
|
||||
raptor_eagle_ccd.h raptor_eagle_ccd.cpp
|
||||
raptor_eagle_ccd.h
|
||||
raptor_eagle_ccd.cpp
|
||||
raptor_eagle_cameralink.h
|
||||
raptor_eagle_exception.h
|
||||
raptor_eagle_acqproc.cpp)
|
||||
|
||||
if (STATIC_LIB)
|
||||
add_library(${RAPTOR_EAGLEV_LIB} STATIC ${RAPTOR_EAGLEV_LIB_SRC})
|
||||
else()
|
||||
if (RAPROT_SHARED_LIB)
|
||||
add_library(${RAPTOR_EAGLEV_LIB} SHARED ${RAPTOR_EAGLEV_LIB_SRC})
|
||||
# add_library(${RAPTOR_EAGLEV_LIB} SHARED
|
||||
# raptor_eagle_ccd.h raptor_eagle_ccd.cpp
|
||||
# raptor_eagle_cameralink.h
|
||||
# raptor_eagle_exception.h
|
||||
# raptor_eagle_acqproc.cpp)
|
||||
else()
|
||||
add_library(${RAPTOR_EAGLEV_LIB} STATIC ${RAPTOR_EAGLEV_LIB_SRC})
|
||||
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
|
||||
# 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 SPDLOG_USE_STD_FORMAT)
|
||||
|
||||
# !!!!! TEMPORARY !!!!!
|
||||
target_include_directories(${RAPTOR_EAGLEV_LIB} PUBLIC "../ADC/")
|
||||
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)
|
||||
@@ -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)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS ${RAPTOR_EAGLEV_LIB}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
# install(TARGETS ${RAPTOR_EAGLEV_LIB} ${RAPTOR_EAGLEV_SERVER}
|
||||
# LIBRARY DESTINATION "/home/obs/TMP"
|
||||
# ARCHIVE DESTINATION "/home/obs/TMP"
|
||||
# RUNTIME DESTINATION "/home/obs/TMP"
|
||||
# )
|
||||
if (RAPROT_SHARED_LIB) # install shared library and server binary
|
||||
install(TARGETS ${RAPTOR_EAGLEV_LIB} ${RAPTOR_EAGLEV_SERVER}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
else()
|
||||
install(TARGETS ${RAPTOR_EAGLEV_SERVER}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
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
19
cmake_uninstall.cmake.in
Normal 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()
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <fitsio.h>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include "raptor_eagle_ccd.h"
|
||||
|
||||
|
||||
@@ -39,11 +40,11 @@ double JulianDay(const std::chrono::utc_clock::time_point& tm)
|
||||
|
||||
} // namespace details
|
||||
|
||||
RaptorEagleCCD::AcquisitionProcess::AcquisitionProcess(RaptorEagleCCD* manager) : _manager(manager)
|
||||
RaptorEagleCCD::AcquisitionProcess::AcquisitionProcess()
|
||||
{
|
||||
std::stringstream st;
|
||||
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;
|
||||
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)
|
||||
{
|
||||
// if (isAcqInProgress) {
|
||||
// throw std::system_error(RaptorEagleCCDError::ERROR_ACQUISITION_IN_PROGRESS);
|
||||
// }
|
||||
|
||||
isAcqInProgress = true;
|
||||
|
||||
// _acqParams = std::move(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 =
|
||||
CAMERA_CAPTURE_TIMEOUT_ADD_CONSTANT +
|
||||
std::chrono::milliseconds(static_cast<std::chrono::milliseconds::rep>(_acqParams->expTime * 1000));
|
||||
@@ -76,85 +76,117 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
|
||||
std::stringstream st;
|
||||
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()),
|
||||
std::format("pxd_doSnap({}, 1, {})", _manager->_cameraUnitmap, timeout.count()));
|
||||
_status = STATUS_ARMED; // change to STATUS_ACQ will be made in RaptorEagleCCD::startAcquisition!!!
|
||||
|
||||
_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) &&
|
||||
!_acqParams->saveInAbort) { // abort acquisition was occured! just exit
|
||||
_manager->logInfo("It seems the acquisition was aborted! Do not save acquired image!");
|
||||
|
||||
isAcqInProgress = false;
|
||||
_status = STATUS_IDLE;
|
||||
|
||||
serverPtr->logInfo("It seems the acquisition was aborted! Do not save acquired image!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_manager->logDebug("Image ROI [{}, {}, {}, {}] (binned {}x{})", _acqParams->roiStartX, _acqParams->roiStartY,
|
||||
_acqParams->roiWidth, _acqParams->roiHeight, _acqParams->binX, _acqParams->binY);
|
||||
_manager->logDebug("Copy image from grabber to buffer (thread id: {}) ...", st.str());
|
||||
serverPtr->logDebug("Image ROI [{}, {}, {}, {}] (binned {}x{})", _acqParams->roiStartX, _acqParams->roiStartY,
|
||||
_acqParams->roiWidth, _acqParams->roiHeight, _acqParams->binX, _acqParams->binY);
|
||||
serverPtr->logDebug("Copy image from grabber to buffer (thread id: {}) ...", st.str());
|
||||
|
||||
size_t npix = _acqParams->roiWidth * _acqParams->roiHeight;
|
||||
// compute image dimension
|
||||
// NOTE: _acqParams->roiWidth and _acqParams->roiHeight are expected in CCD pixels (not binned)!!!
|
||||
|
||||
_imageBufferRows = static_cast<size_t>(std::ceil(npix / _manager->_dimCCD[0]));
|
||||
size_t sz = _imageBufferRows * _manager->_dimCCD[0];
|
||||
|
||||
try {
|
||||
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 div = std::div(_acqParams->roiWidth, _acqParams->binX);
|
||||
auto dimx = div.quot + (div.rem ? 1 : 0);
|
||||
|
||||
auto log_str = std::format("pxd_readushort({}, 1, 0, 0, -1, {}, {}, {}, \"{}\")", _manager->_cameraUnitmap,
|
||||
_imageBufferRows, (void*)_imageBuffer.get(), _imageBufferSize, color_space);
|
||||
div = std::div(_acqParams->roiHeight, _acqParams->binY);
|
||||
auto dimy = div.quot + (div.rem ? 1 : 0);
|
||||
|
||||
_manager->xclibApiCall(pxd_readushort(_manager->_cameraUnitmap, 1, 0, 0, -1, _imageBufferRows,
|
||||
_imageBuffer.get(), _imageBufferSize, (char*)color_space),
|
||||
log_str);
|
||||
|
||||
isAcqInProgress = false;
|
||||
} catch (...) {
|
||||
isAcqInProgress = false;
|
||||
throw;
|
||||
LONGLONG npix = dimx * dimy;
|
||||
|
||||
// NOTE:
|
||||
auto ldiv = std::lldiv(npix, serverPtr->_dimCCD[0]);
|
||||
auto im_buffer_rows = ldiv.quot + (ldiv.rem ? 1 : 0);
|
||||
|
||||
bool gap = false;
|
||||
if ((_acqParams->roiWidth + _acqParams->roiStartX) < serverPtr->_dimCCD[0]) {
|
||||
/* IT SEEMS IF MAX ROI X-COORDINATE IS LESS THAN CCD DIM
|
||||
* EAGLE CAMERA CONTROLLER SETUP READING FOR WIDTH+1 ROI!!! */
|
||||
gap = true;
|
||||
++im_buffer_rows;
|
||||
}
|
||||
|
||||
|
||||
// read size
|
||||
auto im_buffer_size = im_buffer_rows * serverPtr->_dimCCD[0];
|
||||
|
||||
auto log_str = std::format("pxd_readushort({}, 1, 0, 0, -1, {}, {}, {}, \"{}\")", serverPtr->_cameraUnitmap,
|
||||
im_buffer_rows, (void*)_acqParams->imageBufferPtr, im_buffer_size, color_space);
|
||||
|
||||
serverPtr->xclibApiCall(pxd_readushort(serverPtr->_cameraUnitmap, 1, 0, 0, -1, im_buffer_rows,
|
||||
_acqParams->imageBufferPtr, im_buffer_size, (char*)color_space),
|
||||
log_str);
|
||||
|
||||
|
||||
isAcqInProgress = false;
|
||||
|
||||
|
||||
// ------- save to FITS file --------
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
_status = STATUS_SAVE;
|
||||
|
||||
std::string fname{"!"}; // to overwrite existing file
|
||||
fname += _acqParams->filename;
|
||||
|
||||
_manager->logInfo("Try to save FITS file with name '{}'", _acqParams->filename);
|
||||
_manager->logTrace("Actual saving filename: {}", fname);
|
||||
serverPtr->logInfo("Try to save FITS file with name '{}'", _acqParams->filename);
|
||||
serverPtr->logTrace("Actual saving filename: {}", fname);
|
||||
|
||||
fitsfile* fitsFilePtr;
|
||||
int status = 0;
|
||||
|
||||
// int naxis = 2;
|
||||
long naxes[2];
|
||||
naxes[0] = _acqParams->roiWidth;
|
||||
naxes[1] = _acqParams->roiHeight;
|
||||
const int naxis = 2;
|
||||
long naxes[naxis] = {dimx, dimy};
|
||||
|
||||
char err_str[100];
|
||||
|
||||
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);
|
||||
|
||||
_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);
|
||||
|
||||
_manager->logDebug("Write {} pixels to the HDU ...", npix);
|
||||
fits_write_img(fitsFilePtr, TUSHORT, 1, (LONGLONG)npix, _imageBuffer.get(), &status);
|
||||
serverPtr->logDebug("Write {} pixels to the HDU ...", npix);
|
||||
if (gap) {
|
||||
/* IT SEEMS IF MAX ROI X-COORDINATE IS LESS THAN CCD DIM
|
||||
* EAGLE CAMERA CONTROLLER SETUP READING FOR WIDTH+1 ROI!!!
|
||||
* SO ONE NEEDS TO WRITE IMAGE PER ROW WITH SKIPPING EXTRA PIXEL
|
||||
* AT THE END OF EACH ROW
|
||||
*/
|
||||
for (int i = 0; i < dimy; ++i) {
|
||||
fits_write_img(fitsFilePtr, TUSHORT, i * dimx + 1, dimx, _acqParams->imageBufferPtr, &status);
|
||||
}
|
||||
} else {
|
||||
fits_write_img(fitsFilePtr, TUSHORT, 1, npix, _acqParams->imageBufferPtr, &status);
|
||||
}
|
||||
|
||||
|
||||
// helper to convert std::string_view to C-lang null-terminated string
|
||||
@@ -176,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, "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);
|
||||
|
||||
|
||||
@@ -185,12 +219,15 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
|
||||
double jd = details::JulianDay(_acqParams->startTime);
|
||||
|
||||
fits_update_key_str(fitsFilePtr, "DATE-OBS", str.c_str(), "Start of the exposure in UTC", &status);
|
||||
fits_update_key_dbl(fitsFilePtr, "JD", jd, -12, "Julian day of exposure start time", &status);
|
||||
fits_update_key_dbl(fitsFilePtr, "MJD", jd - 2400000.5, -12, "Modified Julian day of exposure start time",
|
||||
fits_update_key_dbl(fitsFilePtr, "JD", jd, -15, "Julian day of exposure start time", &status);
|
||||
fits_update_key_dbl(fitsFilePtr, "MJD", jd - 2400000.5, -15, "Modified Julian day of exposure start time",
|
||||
&status);
|
||||
|
||||
if (_acqParams->startTime < _acqParams->abortTime) { // acquisition was aborted
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -236,17 +273,17 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
|
||||
fits_update_key_dbl(fitsFilePtr, "CCDTEMP", _acqParams->ccdTemp, -2, "CCD chip temperature in Celsius",
|
||||
&status);
|
||||
fits_update_key_dbl(fitsFilePtr, "PCBTEMP", _acqParams->pcbTemp, -2, "PCB temperature in Celsius", &status);
|
||||
fits_update_key_dbl(fitsFilePtr, "TECSETPT", _acqParams->tecSetPoint, -2,
|
||||
fits_update_key_dbl(fitsFilePtr, "TECSETPT", _acqParams->tecSetPoint, -5,
|
||||
"TEC set point temperature in Celsius", &status);
|
||||
|
||||
if (!status) {
|
||||
// keywords from user template file
|
||||
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);
|
||||
if (status) { // ignore possible errors
|
||||
fits_get_errstatus(status, err_str);
|
||||
_manager->logWarn(
|
||||
serverPtr->logWarn(
|
||||
"An error occured while copy keywords from the template file '{}' (err = {}, msg = {})! "
|
||||
"Ignore!",
|
||||
_acqParams->templateFilename, status, err_str);
|
||||
@@ -255,95 +292,153 @@ void RaptorEagleCCD::AcquisitionProcess::start(const std::shared_ptr<acq_params_
|
||||
}
|
||||
}
|
||||
|
||||
// permanent keywords (may update keywords from template file!)
|
||||
char card[80];
|
||||
char kname[8];
|
||||
int k_type;
|
||||
|
||||
for (auto& s : _manager->_permanentFitsKeywords) {
|
||||
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 permanent keyword card [{}] (err = {}, msg = {})! "
|
||||
"Ignore!",
|
||||
s, status, err_str);
|
||||
auto save_keywords = [&status, &err_str, &fitsFilePtr](auto& kwd_list, const std::string& mark) {
|
||||
char card[81];
|
||||
char kname[9] = " ";
|
||||
int k_type;
|
||||
|
||||
status = 0;
|
||||
if (kwd_list.size()) {
|
||||
serverPtr->logDebug("Copy {} {} keywords", mark, kwd_list.size());
|
||||
} else {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
kname[i] = card[i];
|
||||
serverPtr->logDebug("There is no one permanent keyword! Skip!");
|
||||
}
|
||||
for (auto& s : kwd_list) {
|
||||
fits_parse_template(s.data(), card, &k_type, &status);
|
||||
if (status) { // ignore possible errors
|
||||
fits_get_errstatus(status, err_str);
|
||||
serverPtr->logWarn(
|
||||
"An error occured while writing {} keyword card [{}] (err = {}, msg = {})! "
|
||||
"Ignore!",
|
||||
mark, s, status, err_str);
|
||||
|
||||
status = 0;
|
||||
} else {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
kname[i] = card[i];
|
||||
}
|
||||
}
|
||||
|
||||
serverPtr->logTrace("Try to update [{}] FITS card (name = '{}')", card, kname);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
fits_update_card(fitsFilePtr, kname, card, &status);
|
||||
}
|
||||
};
|
||||
|
||||
// permanent keywords (may update keywords from template file!)
|
||||
save_keywords(_acqParams->permanentKeywords, "permanent");
|
||||
|
||||
// keyword from user (may update template file and permanent keywords!)
|
||||
for (auto& s : _manager->_currentFitsKeywords) {
|
||||
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);
|
||||
}
|
||||
save_keywords(_acqParams->currentKeywords, "current");
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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, "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);
|
||||
|
||||
_status = STATUS_IDLE;
|
||||
|
||||
fits_get_errstatus(status, err_str);
|
||||
|
||||
if (status) {
|
||||
_manager->logError("An error occured while writing FITS file '{}'! FITS status = {} ({})",
|
||||
_acqParams->filename, status, err_str);
|
||||
serverPtr->logError("An error occured while writing FITS file '{}'! FITS status = {} ({})",
|
||||
_acqParams->filename, status, err_str);
|
||||
} 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)
|
||||
{
|
||||
if (isAcqInProgress) {
|
||||
_manager->logInfo("Abort current acquisition process!");
|
||||
serverPtr->logInfo("Abort current acquisition process!");
|
||||
|
||||
_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();
|
||||
_manager->writeRegisters({0xD4}, bytes);
|
||||
// _acqParams->abortTime = std::chrono::utc_clock::now();
|
||||
// serverPtr->writeRegisters({0xD4}, bytes);
|
||||
|
||||
_acqParams->abortTime = serverPtr->setTriggerRegisterBit(CL_TRIGGER_MODE_ABORT_CURRENT_EXP_BIT);
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
@@ -137,6 +137,7 @@ static constexpr char CL_SHUTTER_EXP = 0x2;
|
||||
|
||||
// readout rate (registers values)
|
||||
|
||||
static constexpr std::initializer_list<unsigned char> CL_READOUT_CLOCK_RATE_ADDR = {0xA3, 0xA4};
|
||||
static constexpr unsigned char CL_READOUT_CLOCK_RATE_A3_2MHZ = 0x02;
|
||||
static constexpr unsigned char CL_READOUT_CLOCK_RATE_A4_2MHZ = 0x02;
|
||||
static constexpr unsigned char CL_READOUT_CLOCK_RATE_A3_75KHZ = 0x43;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <cmath>
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
||||
#include "raptor_eagle_cameralink.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);
|
||||
}
|
||||
|
||||
AcquisitionProcess::serverPtr = this;
|
||||
|
||||
initAttrComm();
|
||||
|
||||
openPIXCI();
|
||||
@@ -175,8 +179,28 @@ RaptorEagleCCD::RaptorEagleCCD(std::shared_ptr<spdlog::logger> logger)
|
||||
|
||||
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();
|
||||
|
||||
|
||||
logDebug("DTOR: Delete RaptorEagleCCD class instance");
|
||||
}
|
||||
|
||||
@@ -364,7 +388,7 @@ std::chrono::utc_clock::time_point RaptorEagleCCD::setTriggerRegisterBit(const s
|
||||
|
||||
auto bits = getTriggerRegister();
|
||||
|
||||
logDebug("Try to set '{}' trigger register", details::cl_trigger_register_bit(bit_pos));
|
||||
logDebug("Try to set '{}' trigger register bit", details::cl_trigger_register_bit(bit_pos));
|
||||
|
||||
bits.set(bit_pos);
|
||||
|
||||
@@ -378,7 +402,7 @@ std::chrono::utc_clock::time_point RaptorEagleCCD::clearTriggerRegisterBit(const
|
||||
|
||||
auto bits = getTriggerRegister();
|
||||
|
||||
logDebug("Try to clear '{}' trigger register", details::cl_trigger_register_bit(bit_pos));
|
||||
logDebug("Try to clear '{}' trigger register bit", details::cl_trigger_register_bit(bit_pos));
|
||||
|
||||
bits.reset(bit_pos);
|
||||
|
||||
@@ -392,7 +416,7 @@ std::chrono::utc_clock::time_point RaptorEagleCCD::flipTriggerRegisterBit(const
|
||||
|
||||
auto bits = getTriggerRegister();
|
||||
|
||||
logDebug("Try to flip '{}' trigger register", details::cl_trigger_register_bit(bit_pos));
|
||||
logDebug("Try to flip '{}' trigger register bit", details::cl_trigger_register_bit(bit_pos));
|
||||
|
||||
bits.flip(bit_pos);
|
||||
|
||||
@@ -410,6 +434,14 @@ bool RaptorEagleCCD::initCamera(int 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;
|
||||
|
||||
// configure CameraLink serial connection
|
||||
@@ -442,6 +474,28 @@ bool RaptorEagleCCD::initCamera(int unitmap)
|
||||
xclibApiCall(_bitsPerPixel = pxd_imageBdim(), "pxd_imageBdim()");
|
||||
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 full-frame dimension [{}, {}] pixels", _dimCCD[0], _dimCCD[1]);
|
||||
logDebug("CCD bits per pixel: {}", _bitsPerPixel);
|
||||
@@ -450,18 +504,41 @@ bool RaptorEagleCCD::initCamera(int unitmap)
|
||||
|
||||
|
||||
logDebug("Set initial state (IDLE, full frame, binning to 1x1) ...");
|
||||
(*this)[CAMERA_ATTR_READ_MODE] = CAMERA_ATTR_READ_MODE_NORMAL;
|
||||
(*this)[CAMERA_ATTR_READ_RATE] = CAMERA_ATTR_READ_RATE_FAST;
|
||||
(*this)[CAMERA_ATTR_ROI_STARTX] = 1; // in FITS notation (started from 1)!!!
|
||||
(*this)[CAMERA_ATTR_ROI_STARTY] = 1; // in FITS notation (started from 1)!!!
|
||||
(*this)[CAMERA_ATTR_ROI_WIDTH] = _dimCCD[0];
|
||||
(*this)[CAMERA_ATTR_ROI_HEIGHT] = _dimCCD[1];
|
||||
|
||||
(*this)[CAMERA_ATTR_XBIN] = 1;
|
||||
(*this)[CAMERA_ATTR_YBIN] = 1;
|
||||
(*this)[CAMERA_ATTR_ROI_STARTX] = 0; // in CCD notation (started from 0)!!!
|
||||
(*this)[CAMERA_ATTR_ROI_STARTY] = 0; // in CCD notation (started from 0)!!!
|
||||
|
||||
(*this)[CAMERA_ATTR_ROI_HEIGHT] = 0; // ??!!!!
|
||||
|
||||
(*this)[CAMERA_ATTR_ROI_WIDTH] = _dimCCD[0];
|
||||
(*this)[CAMERA_ATTR_ROI_HEIGHT] = _dimCCD[1];
|
||||
|
||||
(*this)[CAMERA_ATTR_READ_MODE] = CAMERA_ATTR_READ_MODE_NORMAL;
|
||||
(*this)[CAMERA_ATTR_READ_RATE] = CAMERA_ATTR_READ_RATE_FAST;
|
||||
|
||||
// IDLE mode
|
||||
(*this)[CAMERA_ATTR_TRIGGER_MODE] = CAMERA_ATTR_TRIGGER_MODE_IDLE;
|
||||
|
||||
// clearTriggerRegisterBit(CL_TRIGGER_MODE_EXT_TRIGGER_BIT);
|
||||
// clearTriggerRegisterBit(CL_TRIGGER_MODE_CONTINUOUS_SEQ_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);
|
||||
|
||||
return true;
|
||||
@@ -478,8 +555,8 @@ void RaptorEagleCCD::openPIXCI()
|
||||
} else {
|
||||
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 "raptor_eagle-v.fmt" // 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
|
||||
pxd_videoFormatAsIncludedInit(0);
|
||||
xclibApiCall(pxd_videoFormatAsIncluded(0), "pxd_videoFormatAsIncluded(0)");
|
||||
}
|
||||
@@ -508,10 +585,11 @@ size_t RaptorEagleCCD::clRead(byte_seq_t& bytes)
|
||||
|
||||
// how many byte are available
|
||||
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) {
|
||||
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) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
nbytes = pxd_serialRead(_cameraUnitmap, 0, nullptr, 0);
|
||||
@@ -521,13 +599,14 @@ size_t RaptorEagleCCD::clRead(byte_seq_t& bytes)
|
||||
}
|
||||
|
||||
if (!nbytes) {
|
||||
logWarn("Cameralink reading operation timeout!");
|
||||
return 0;
|
||||
// logWarn("Cameralink reading operation timeout!");
|
||||
// return 0;
|
||||
throw std::system_error(RaptorEagleCCDError::ERROR_CAMLINK_READ_TIMEOUT);
|
||||
}
|
||||
|
||||
// here the call is only for logging purpose
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -539,7 +618,8 @@ size_t RaptorEagleCCD::clRead(byte_seq_t& bytes)
|
||||
|
||||
xclibApiCall(pxd_serialRead(_cameraUnitmap, 0, (char*)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) {
|
||||
@@ -602,7 +682,7 @@ size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
|
||||
|
||||
// how many bytes are available in Tx-buffer
|
||||
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 < (bytes.size() + tr_nbytes)) {
|
||||
@@ -617,7 +697,8 @@ size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
|
||||
|
||||
xclibApiCall(nbytes = pxd_serialWrite(_cameraUnitmap, 0, (char*)bytes.data(), bytes.size()),
|
||||
std::format("pxd_serialWrite({}, 0, [{}], {})", _cameraUnitmap, details::formatByteArr(bytes),
|
||||
bytes.size()));
|
||||
bytes.size()),
|
||||
spdlog::level::trace);
|
||||
// xclibApiCall(
|
||||
// nbytes = pxd_serialWrite(_cameraUnitmap, 0, (char*)bytes.data(), bytes.size()),
|
||||
// std::format("pxd_serialWrite({}, 0, {}, {})", _cameraUnitmap, (void*)bytes.data(), bytes.size()));
|
||||
@@ -625,16 +706,19 @@ size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
|
||||
// send trailing ETX and possible checksum bytes
|
||||
|
||||
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 {
|
||||
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),
|
||||
std::format("pxd_serialWrite({}, 0, [{}], {})", _cameraUnitmap,
|
||||
details::formatByteArr(std::string_view((const char*)etx_checksum_bytes, 2) |
|
||||
std::views::take(tr_nbytes)),
|
||||
tr_nbytes));
|
||||
tr_nbytes),
|
||||
spdlog::level::trace);
|
||||
// xclibApiCall(
|
||||
// pxd_serialWrite(_cameraUnitmap, 0, (char*)etx_checksum_bytes, tr_nbytes),
|
||||
// std::format("pxd_serialWrite({}, 0, {}, {})", _cameraUnitmap, (void*)etx_checksum_bytes, tr_nbytes));
|
||||
@@ -926,77 +1010,120 @@ void RaptorEagleCCD::startAquisition()
|
||||
}
|
||||
|
||||
|
||||
logInfo("Start acquisition process ...");
|
||||
if (_acqRingFreeBufferPtrs.empty()) {
|
||||
logDebug("There is no free image buffers! Try to shrink ring buffer ...");
|
||||
|
||||
std::lock_guard lock_guard(_acqProcessesMutex);
|
||||
|
||||
auto acq_pars = std::make_shared<acq_params_t>(acq_params_t({
|
||||
.startTime = std::chrono::utc_clock::time_point(),
|
||||
.abortTime = std::chrono::utc_clock::time_point(),
|
||||
.saveInAbort = false,
|
||||
.expTime = (*this)[CAMERA_ATTR_EXPTIME],
|
||||
.roiStartX = (*this)[CAMERA_ATTR_ROI_STARTX], // in CCD pixels (start from 0)
|
||||
.roiStartY = (*this)[CAMERA_ATTR_ROI_STARTY], // in CCD pixels (start from 0)
|
||||
.roiWidth = (*this)[CAMERA_ATTR_ROI_WIDTH], // in binned pixels
|
||||
.roiHeight = (*this)[CAMERA_ATTR_ROI_HEIGHT], // in binned pixels
|
||||
.binX = (*this)[CAMERA_ATTR_XBIN],
|
||||
.binY = (*this)[CAMERA_ATTR_YBIN],
|
||||
.shutterState = (*this)[CAMERA_ATTR_SHUTTER_STATE],
|
||||
.readRate = (*this)[CAMERA_ATTR_READ_RATE],
|
||||
.readMode = (*this)[CAMERA_ATTR_READ_MODE],
|
||||
.gain = (*this)[CAMERA_ATTR_GAIN],
|
||||
.ccdTemp = (*this)[CAMERA_ATTR_CCD_TEMP],
|
||||
.tecSetPoint = (*this)[CAMERA_ATTR_TECPOINT],
|
||||
.tecState = (*this)[CAMERA_ATTR_TECSTATE] == CAMERA_ATTR_TECSTATE_ON ? true : false,
|
||||
.pcbTemp = (*this)[CAMERA_ATTR_PCB_TEMP],
|
||||
.filename = (*this)[CAMERA_ATTR_FITS_FILENAME],
|
||||
.templateFilename = (*this)[CAMERA_ATTR_FITS_TEMPLATE],
|
||||
.permanentKeywords = _permanentFitsKeywords, // copy
|
||||
.currentKeywords = std::move(_currentFitsKeywords) // move!!!
|
||||
}));
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
std::lock_guard lock_guard(_acqProcessesMutex);
|
||||
|
||||
auto sptr = std::make_shared<AcquisitionProcess>(this);
|
||||
for (auto it = _acqProcesses.begin(); it != _acqProcesses.end();) {
|
||||
if (it->expired()) {
|
||||
it = _acqProcesses.erase(it);
|
||||
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 {
|
||||
break;
|
||||
logError("Ring buffer exceeded maximum size!");
|
||||
throw std::system_error(RaptorEagleCCDError::ERROR_NO_FREE_BUFFER);
|
||||
}
|
||||
}
|
||||
|
||||
_acqProcesses.emplace_back(sptr);
|
||||
// arm grabber here
|
||||
sptr->start(acq_pars);
|
||||
// check filesystem permissions
|
||||
std::string fname = (*this)[CAMERA_ATTR_FITS_FILENAME];
|
||||
|
||||
// bits.set(CL_TRIGGER_MODE_SNAPSHOT_BIT); // start snapshot bit
|
||||
// bytes[0] = static_cast<uint8_t>(bits.to_ulong());
|
||||
std::error_code ec;
|
||||
std::filesystem::path pt(fname);
|
||||
|
||||
// acq_pars->startTime = std::chrono::utc_clock::now();
|
||||
// writeRegisters({0xD4}, bytes); // write to trigger mode register (start snapshot)
|
||||
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 ...");
|
||||
|
||||
|
||||
auto acq_pars = std::make_shared<acq_params_t>(
|
||||
acq_params_t({.startTime = std::chrono::utc_clock::time_point(),
|
||||
.abortTime = std::chrono::utc_clock::time_point(),
|
||||
.saveInAbort = false,
|
||||
.expTime = (*this)[CAMERA_ATTR_EXPTIME],
|
||||
.roiStartX = (*this)[CAMERA_ATTR_ROI_STARTX], // in CCD pixels (start from 0)
|
||||
.roiStartY = (*this)[CAMERA_ATTR_ROI_STARTY], // in CCD pixels (start from 0)
|
||||
.roiWidth = (*this)[CAMERA_ATTR_ROI_WIDTH], // in binned pixels
|
||||
.roiHeight = (*this)[CAMERA_ATTR_ROI_HEIGHT], // in binned pixels
|
||||
.binX = (*this)[CAMERA_ATTR_XBIN],
|
||||
.binY = (*this)[CAMERA_ATTR_YBIN],
|
||||
.shutterState = (*this)[CAMERA_ATTR_SHUTTER_STATE],
|
||||
.readRate = (*this)[CAMERA_ATTR_READ_RATE],
|
||||
.readMode = (*this)[CAMERA_ATTR_READ_MODE],
|
||||
.gain = (*this)[CAMERA_ATTR_GAIN],
|
||||
.ccdTemp = (*this)[CAMERA_ATTR_CCD_TEMP],
|
||||
.tecSetPoint = (*this)[CAMERA_ATTR_TECPOINT],
|
||||
.tecState = (*this)[CAMERA_ATTR_TECSTATE] == CAMERA_ATTR_TECSTATE_ON ? true : false,
|
||||
.pcbTemp = (*this)[CAMERA_ATTR_PCB_TEMP],
|
||||
// .filename = (*this)[CAMERA_ATTR_FITS_FILENAME],
|
||||
.filename = fname,
|
||||
.templateFilename = (*this)[CAMERA_ATTR_FITS_TEMPLATE],
|
||||
.permanentKeywords = _permanentFitsKeywords, // copy
|
||||
.currentKeywords = std::move(_currentFitsKeywords), // move!!!
|
||||
.imageBufferPtr = _acqRingFreeBufferPtrs.front()}));
|
||||
|
||||
logDebug("Current acquisition buffer address: {}", (void*)_acqRingFreeBufferPtrs.front());
|
||||
|
||||
_acqRingFreeBufferPtrs.pop();
|
||||
|
||||
|
||||
// adjust geometry
|
||||
|
||||
auto xmax = acq_pars->roiStartX + acq_pars->roiWidth;
|
||||
auto ymax = acq_pars->roiStartY + acq_pars->roiHeight;
|
||||
|
||||
if (xmax > _dimCCD[0]) {
|
||||
acq_pars->roiWidth = _dimCCD[0] - acq_pars->roiStartX;
|
||||
logDebug("Adjust ROI width to {}", acq_pars->roiWidth);
|
||||
}
|
||||
if (ymax > _dimCCD[1]) {
|
||||
acq_pars->roiHeight = _dimCCD[1] - acq_pars->roiStartY;
|
||||
logDebug("Adjust ROI height to {}", acq_pars->roiHeight);
|
||||
}
|
||||
|
||||
|
||||
auto sptr = std::make_shared<AcquisitionProcess>();
|
||||
sptr->start(acq_pars); // asynchronous call!!!
|
||||
|
||||
// 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);
|
||||
sptr->_status = AcquisitionProcess::STATUS_ACQ;
|
||||
|
||||
int status;
|
||||
xclibApiCall(status = pxd_goneLive(_cameraUnitmap, 0), std::format("pxd_goneLive({}, 0)", _cameraUnitmap));
|
||||
@@ -1010,13 +1137,10 @@ void RaptorEagleCCD::stopAcquisition(bool save_acq)
|
||||
{
|
||||
std::lock_guard lock_guard(_acqProcessesMutex);
|
||||
|
||||
for (auto it = _acqProcesses.begin(); it != _acqProcesses.end();) {
|
||||
if (it->expired()) {
|
||||
it = _acqProcesses.erase(it);
|
||||
} else {
|
||||
auto sptr = it->lock();
|
||||
for (auto& sptr : AcquisitionProcess::acqProcSptr) {
|
||||
if (sptr->_status == AcquisitionProcess::STATUS_ACQ) {
|
||||
sptr->stop(save_acq);
|
||||
return; // there was only the single active aquisition, so exit here!
|
||||
return; // the only one active acquisition process
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1147,6 +1271,39 @@ void RaptorEagleCCD::initAttrComm()
|
||||
|
||||
/* ------- 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 */
|
||||
|
||||
addAttribute(
|
||||
@@ -1168,11 +1325,13 @@ void RaptorEagleCCD::initAttrComm()
|
||||
addAttribute(
|
||||
CAMERA_ATTR_FITS_TEMPLATE,
|
||||
[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;
|
||||
},
|
||||
[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;
|
||||
});
|
||||
@@ -1438,58 +1597,84 @@ void RaptorEagleCCD::initAttrComm()
|
||||
},
|
||||
"ROI height"));
|
||||
|
||||
|
||||
// binning validator (1-32, 64)
|
||||
auto bin_validator = []<typename T>(const T& val, std::string_view log_name) {
|
||||
std::pair<T, std::string> res{val, ""};
|
||||
|
||||
T* limits = (T*)EAGLE_CAMERA_MAX_XBIN;
|
||||
if (log_name == "YBIN") {
|
||||
limits = (T*)EAGLE_CAMERA_MAX_YBIN;
|
||||
}
|
||||
|
||||
if (val < 1) {
|
||||
res.first = 1;
|
||||
res.second = std::format("The {} must start from 1", log_name);
|
||||
} else if ((val > limits[0]) && (val < limits[1])) {
|
||||
// set to the closest
|
||||
res.first = (val - limits[0]) < (limits[1] - val) ? limits[0] : limits[1];
|
||||
|
||||
res.second = std::format("The {} must not be within {} and {}", log_name, limits[0], limits[1]);
|
||||
} else if (val > limits[1]) {
|
||||
res.first = limits[1];
|
||||
res.second = std::format("The {} must not be greater than {}", log_name, limits[1]);
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
// X-bin
|
||||
addAttribute(create8BitAttr(
|
||||
CAMERA_ATTR_XBIN, CL_XBIN_ADDR,
|
||||
[]<typename T>(const T& val) { // validator (1-32, 64)
|
||||
std::pair<T, std::string> res{val, ""};
|
||||
addAttribute(attribute_t::makeArithAttr(
|
||||
CAMERA_ATTR_XBIN,
|
||||
[this]() {
|
||||
auto bytes = readRegisters(CL_XBIN_ADDR);
|
||||
|
||||
if (val < 1) {
|
||||
res.first = 1;
|
||||
res.second = "The XBIN must start from 1";
|
||||
} else if ((val > EAGLE_CAMERA_MAX_XBIN[0]) && (val < EAGLE_CAMERA_MAX_XBIN[1])) {
|
||||
// set to the closest
|
||||
res.first = (val - EAGLE_CAMERA_MAX_XBIN[0]) < (EAGLE_CAMERA_MAX_XBIN[1] - val)
|
||||
? EAGLE_CAMERA_MAX_XBIN[0]
|
||||
: EAGLE_CAMERA_MAX_XBIN[1];
|
||||
// inner register value of binning starts from 0!!!
|
||||
logDebug("Return XBIN as {} (inner register value: {})", bytes[0] + 1, bytes[0]);
|
||||
|
||||
res.second = std::format("The XBIN must not be within {} and {}", EAGLE_CAMERA_MAX_XBIN[0],
|
||||
EAGLE_CAMERA_MAX_XBIN[1]);
|
||||
} else if (val > EAGLE_CAMERA_MAX_XBIN[1]) {
|
||||
res.first = EAGLE_CAMERA_MAX_XBIN[1];
|
||||
res.second = std::format("The XBIN must not be greater than {}", EAGLE_CAMERA_MAX_XBIN[1]);
|
||||
return bytes[0] + 1;
|
||||
},
|
||||
[&bin_validator, this](const uchar& val) {
|
||||
logDebug("Try to set XBIN to {} ...", val);
|
||||
|
||||
auto v_res = bin_validator(val, "XBIN");
|
||||
if (v_res.second.size()) {
|
||||
logWarn(v_res.second);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
"XBIN"));
|
||||
// inner register value of binning starts from 0!!!
|
||||
v_res.first -= 1;
|
||||
writeRegisters(CL_XBIN_ADDR, {v_res.first});
|
||||
|
||||
logDebug("XBIN is set to {} (inner register value {})", v_res.first + 1, v_res.first);
|
||||
}));
|
||||
|
||||
// Y-bin
|
||||
addAttribute(create8BitAttr(
|
||||
CAMERA_ATTR_YBIN, CL_YBIN_ADDR,
|
||||
[]<typename T>(const T& val) { // validator (1-32, 64)
|
||||
std::pair<T, std::string> res{val, ""};
|
||||
addAttribute(attribute_t::makeArithAttr(
|
||||
CAMERA_ATTR_YBIN,
|
||||
[this]() {
|
||||
auto bytes = readRegisters(CL_YBIN_ADDR);
|
||||
|
||||
if (val < 1) {
|
||||
res.first = 1;
|
||||
res.second = "The YBIN must start from 1";
|
||||
} else if ((val > EAGLE_CAMERA_MAX_YBIN[0]) && (val < EAGLE_CAMERA_MAX_YBIN[1])) {
|
||||
// set to the closest
|
||||
res.first = (val - EAGLE_CAMERA_MAX_YBIN[0]) < (EAGLE_CAMERA_MAX_YBIN[1] - val)
|
||||
? EAGLE_CAMERA_MAX_YBIN[0]
|
||||
: EAGLE_CAMERA_MAX_YBIN[1];
|
||||
// inner register value of binning starts from 0!!!
|
||||
logDebug("Return YBIN as {} (inner register value: {})", bytes[0] + 1, bytes[0]);
|
||||
|
||||
res.second = std::format("The YBIN must not be within {} and {}", EAGLE_CAMERA_MAX_YBIN[0],
|
||||
EAGLE_CAMERA_MAX_YBIN[1]);
|
||||
} else if (val > EAGLE_CAMERA_MAX_YBIN[1]) {
|
||||
res.first = EAGLE_CAMERA_MAX_YBIN[1];
|
||||
res.second = std::format("The YBIN must not be greater than {}", EAGLE_CAMERA_MAX_XBIN[1]);
|
||||
return bytes[0] + 1;
|
||||
},
|
||||
[&bin_validator, this](const uchar& val) {
|
||||
logDebug("Try to set YBIN to {} ...", val);
|
||||
|
||||
auto v_res = bin_validator(val, "YBIN");
|
||||
if (v_res.second.size()) {
|
||||
logWarn(v_res.second);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
"YBIN"));
|
||||
// inner register value of binning starts from 0!!!
|
||||
v_res.first -= 1;
|
||||
writeRegisters(CL_YBIN_ADDR, {v_res.first});
|
||||
|
||||
logDebug("YBIN is set to {} (inner register value {})", v_res.first + 1, v_res.first);
|
||||
}));
|
||||
|
||||
|
||||
/* TEC SET POINT AND STATE */
|
||||
@@ -1665,7 +1850,7 @@ void RaptorEagleCCD::initAttrComm()
|
||||
addAttribute(
|
||||
CAMERA_ATTR_READ_RATE,
|
||||
[this]() {
|
||||
auto bytes = readRegisters(CL_FRAMERATE_ADDR);
|
||||
auto bytes = readRegisters(CL_READOUT_CLOCK_RATE_ADDR);
|
||||
std::string_view val;
|
||||
|
||||
if ((bytes[0] == CL_READOUT_CLOCK_RATE_A3_2MHZ) && (bytes[1] == CL_READOUT_CLOCK_RATE_A4_2MHZ)) {
|
||||
@@ -1678,7 +1863,7 @@ void RaptorEagleCCD::initAttrComm()
|
||||
val = CAMERA_ATTR_STR_INVALID;
|
||||
}
|
||||
|
||||
logTrace("Return readout rate as '()' string", val);
|
||||
logTrace("Return readout rate as '{}' string", val);
|
||||
|
||||
return val;
|
||||
},
|
||||
@@ -1695,7 +1880,7 @@ void RaptorEagleCCD::initAttrComm()
|
||||
logInfo("Set readout rate to {}", CAMERA_ATTR_READ_RATE_FAST);
|
||||
}
|
||||
|
||||
writeRegisters(CL_FRAMERATE_ADDR, bytes);
|
||||
writeRegisters(CL_READOUT_CLOCK_RATE_ADDR, bytes);
|
||||
|
||||
logDebug("Readout rate is set to [0x{:02X}, 0x{:02X}] bytes", bytes[0], bytes[1]);
|
||||
},
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#ifdef USE_SPDLOG_LIBRARY
|
||||
#include <spdlog/sinks/null_sink.h>
|
||||
#endif
|
||||
@@ -33,6 +35,7 @@ public:
|
||||
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_MAX_SIZE = 100;
|
||||
|
||||
/* some Eagle V camera constants */
|
||||
// static constexpr double EAGLE_CAMERA_MAX_EXPTIME = 27487.7906944; // in seconds (0xFFFFFFFFFF * 25nsec)
|
||||
@@ -68,6 +71,9 @@ public:
|
||||
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)
|
||||
// this constant will be added to exposure to compute actual timeout
|
||||
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_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_START_EXP{"STARTEXP"};
|
||||
static constexpr std::string_view CAMERA_CMD_STOP_EXP{"STOPEXP"};
|
||||
@@ -144,6 +152,15 @@ public:
|
||||
// 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,
|
||||
std::shared_ptr<spdlog::logger> logger = spdlog::null_logger_mt("EAGLE_CCD_NULL_LOGGER"));
|
||||
|
||||
@@ -187,6 +204,7 @@ private:
|
||||
// std::unique_ptr<ushort> imageBuffer;
|
||||
// size_t imageBufferSize;
|
||||
// size_t imageBufferRows;
|
||||
ushort* imageBufferPtr;
|
||||
};
|
||||
|
||||
class AcquisitionProcess : public std::enable_shared_from_this<AcquisitionProcess>
|
||||
@@ -194,23 +212,26 @@ private:
|
||||
friend class RaptorEagleCCD;
|
||||
|
||||
public:
|
||||
AcquisitionProcess(RaptorEagleCCD*);
|
||||
AcquisitionProcess();
|
||||
~AcquisitionProcess();
|
||||
|
||||
void start(const std::shared_ptr<acq_params_t>& params); // asynchronous method!
|
||||
void stop(bool save = true);
|
||||
std::string status();
|
||||
|
||||
// static std::string acqProcStatus();
|
||||
private:
|
||||
inline static RaptorEagleCCD* serverPtr = nullptr;
|
||||
|
||||
inline static std::atomic_bool isAcqInProgress = false;
|
||||
|
||||
RaptorEagleCCD* _manager;
|
||||
std::shared_ptr<acq_params_t> _acqParams{};
|
||||
std::unique_ptr<ushort[]> _imageBuffer{};
|
||||
size_t _imageBufferSize = 0;
|
||||
size_t _imageBufferRows = 0;
|
||||
inline static std::set<std::shared_ptr<AcquisitionProcess>> acqProcSptr{};
|
||||
|
||||
std::future<void> _snapAndCopyFuture;
|
||||
// std::future<void> _saveFitsFileFuture;
|
||||
std::shared_ptr<acq_params_t> _acqParams{};
|
||||
|
||||
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;
|
||||
@@ -236,13 +257,13 @@ private:
|
||||
|
||||
// attributes inner variables
|
||||
std::atomic_size_t _frameNumbers;
|
||||
std::string _currentFitsFile; // current acquisition FITS filename
|
||||
std::string _currentTemplateFile; // CFITSIO template filename
|
||||
std::vector<std::string> _currentFitsKeywords; // current acquisition FITS keywords
|
||||
std::vector<std::string> _permanentFitsKeywords; // permanent user FITS keywords
|
||||
std::string _currentFitsFile; // current acquisition FITS filename
|
||||
std::string _currentTemplateFile; // CFITSIO template filename
|
||||
std::vector<std::string> _currentFitsKeywords{}; // current acquisition FITS keywords
|
||||
std::vector<std::string> _permanentFitsKeywords{}; // permanent user FITS keywords
|
||||
|
||||
// std::list<std::unique_ptr<ushort>> _acqRingBuffer;
|
||||
std::list<std::pair<std::unique_ptr<ushort>, size_t>> _acqRingBuffer;
|
||||
std::vector<std::unique_ptr<ushort[]>> _acqRingBuffer{};
|
||||
std::queue<ushort*> _acqRingFreeBufferPtrs{};
|
||||
|
||||
|
||||
// hardware version info
|
||||
@@ -264,7 +285,7 @@ private:
|
||||
|
||||
// acquisition process members
|
||||
std::mutex _acqProcessesMutex;
|
||||
std::list<std::weak_ptr<AcquisitionProcess>> _acqProcesses{};
|
||||
// std::list<std::weak_ptr<AcquisitionProcess>> _acqProcesses{};
|
||||
|
||||
// std::future<void> _doSnapAndCopyFuture;
|
||||
// std::future<void> _saveFitsFile;
|
||||
@@ -338,7 +359,9 @@ private:
|
||||
// logging helper methods
|
||||
|
||||
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) {
|
||||
auto s_err = pxd_mesgErrorCode(err);
|
||||
@@ -354,17 +377,19 @@ private:
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
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>
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,11 +10,16 @@ enum class RaptorEagleCCDError : int {
|
||||
ERROR_INVALID_UNITMAP,
|
||||
ERROR_INVALID_ATTR_VALUE,
|
||||
ERROR_CAMLINK_WRITE,
|
||||
ERROR_CAMLINK_READ_TIMEOUT,
|
||||
ERROR_CANNOT_INIT_CAMERA,
|
||||
ERROR_CANNOT_RESET_MICRO,
|
||||
ERROR_CANNOT_RESET_FPGA,
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
@@ -49,6 +54,8 @@ struct RaptorEagleCCDErrorCategory : std::error_category {
|
||||
return "invalid camera attribute value";
|
||||
case RaptorEagleCCDError::ERROR_CAMLINK_WRITE:
|
||||
return "not all data were transmitted via CameraLink connection";
|
||||
case RaptorEagleCCDError::ERROR_CAMLINK_READ_TIMEOUT:
|
||||
return "CameraLink reading operation timeout";
|
||||
case RaptorEagleCCDError::ERROR_CANNOT_INIT_CAMERA:
|
||||
return "cannot initialize camera hardware";
|
||||
case RaptorEagleCCDError::ERROR_CANNOT_RESET_MICRO:
|
||||
@@ -59,6 +66,14 @@ struct RaptorEagleCCDErrorCategory : std::error_category {
|
||||
return "try to use software trigger while external trigger mode is enabled";
|
||||
case RaptorEagleCCDError::ERROR_ACQUISITION_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:
|
||||
return "UNKNOWN ERROR";
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ int main(int argc, char* argv[])
|
||||
int exit_code = 0;
|
||||
asio::io_context ctx;
|
||||
std::vector<std::thread> threads;
|
||||
std::size_t Nthreads = 2;
|
||||
std::size_t Nthreads = 3;
|
||||
|
||||
/* COMMANDLINE OPTS */
|
||||
cxxopts::Options options(argv[0], "ADC-library test device network server (ASIO implementation)\n");
|
||||
|
||||
Reference in New Issue
Block a user