From db7906250626aed5a72bc87d920d773d75b001f6 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Mon, 2 Dec 2024 01:02:11 +0300 Subject: [PATCH] ... --- raptor_eagle_cameralink.h | 2 +- raptor_eagle_ccd.cpp | 182 +++++++++++++++++++++++++++++++++++++- raptor_eagle_ccd.h | 27 +++++- raptor_eagle_exception.h | 46 +++++++++- 4 files changed, 250 insertions(+), 7 deletions(-) diff --git a/raptor_eagle_cameralink.h b/raptor_eagle_cameralink.h index 6450ee5..eb23f37 100644 --- a/raptor_eagle_cameralink.h +++ b/raptor_eagle_cameralink.h @@ -32,7 +32,7 @@ static constexpr bool CL_DEFAULT_CHK_SUM_ENABLED = true; static constexpr char CL_ETX = 0x50; static constexpr char CL_ETX_SER_TIMEOUT = 0x51; static constexpr char CL_ETX_CK_SUM_ERR = 0x52; -static constexpr char CL_ETXI2C_ERR = 0x53; +static constexpr char CL_ETX_I2C_ERR = 0x53; static constexpr char CL_ETX_UNKNOWN_CMD = 0x54; static constexpr char CL_ETX_DONE_LOW = 0x55; diff --git a/raptor_eagle_ccd.cpp b/raptor_eagle_ccd.cpp index 54bf87a..cdb8aaa 100644 --- a/raptor_eagle_ccd.cpp +++ b/raptor_eagle_ccd.cpp @@ -5,6 +5,7 @@ #include "raptor_eagle_ccd.h" #include "raptor_eagle_exception.h" +#include #define DEFAULT_EPIX_VIDEO_FMT_FILE "raptor_eagle-v.fmt" @@ -12,7 +13,14 @@ RaptorEagleCCD::RaptorEagleCCD(const adc::traits::adc_input_char_range auto& epix_video_fmt_filename, std::shared_ptr logger) - : base_t("EagleCCD"), adc::AdcSpdlogLogger(logger) + : base_t("EagleCCD"), + adc::AdcSpdlogLogger(logger), + _epixFmtVideoFilename(), + _cameraUnitmap(1), // by default only the single camera + _clCommandAckBit(1), // enable by default + _clChecksumBit(1), // enable by default + _expTime(0.0) + { addMarkToPattern("EAGLE-CCD"); @@ -51,7 +59,12 @@ void RaptorEagleCCD::initCamera(int unitmap) _cameraUnitmap = unitmap; - logInfo("Camera with unit '{}' is initialized", _cameraUnitmap); + xclibApiCall(pxd_serialConfigure(_cameraUnitmap, 0, CL_DEFAULT_BAUD_RATE, CL_DEFAULT_DATA_BITS, 0, + CL_DEFAULT_STOP_BIT, 0, 0, 0), + std::format("pxd_serialConfigure({}, 0, {}, {}, 0, {}, 0, 0, 0)", _cameraUnitmap, CL_DEFAULT_BAUD_RATE, + CL_DEFAULT_DATA_BITS, CL_DEFAULT_STOP_BIT)); + + logInfo("Camera with unitmap '{}' is initialized", _cameraUnitmap); } @@ -77,7 +90,170 @@ void RaptorEagleCCD::closePIXCI() } +/* CameraLink-RELATED METHODS */ + +size_t RaptorEagleCCD::clRead(byte_seq_t& bytes) +{ + size_t nbytes; + + // how many byte are available + xclibApiCall(nbytes = pxd_serialRead(_cameraUnitmap, 0, nullptr, 0), + std::format("pxd_serialRead({}, 0, NULL, 0)", _cameraUnitmap)); + + if (!nbytes) { + logWarn("There are no bytes in Rx-buffer! Nothing to do!"); + return 0; + } + + nbytes += _clCommandAckBit + _clChecksumBit; + + if (bytes.size() < nbytes) { + bytes.resize(nbytes); + } + + xclibApiCall(pxd_serialRead(_cameraUnitmap, 0, bytes.data(), nbytes), + std::format("pxd_serialRead({}, 0, {}, {})", _cameraUnitmap, (void*)bytes.data(), nbytes)); + + + if (_loggerSPtr->level() == spdlog::level::trace) { + std::string s; + adc::utils::AdcCharRangeFromValueRange(s, bytes, std::string_view(", ")); + + logTrace("Received from controller: [{}]", s); + } + + return nbytes; +} + +size_t RaptorEagleCCD::clReadAndCheckAck(byte_seq_t& bytes) +{ + auto nbytes = clRead(bytes); + + if (_clCommandAckBit && nbytes) { + auto ack = *(bytes.end() - 1 - _clChecksumBit); + if (ack != CL_ETX) { + throw std::error_code(ack, RaptorEagleControllerErrorCategory::get()); + } + } + + return nbytes; +} + + +size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes) +{ + if (bytes.empty()) { + logWarn("An empty transmitted byte sequence! Nothing to do!"); + return 0; + } + + if (_loggerSPtr->level() == spdlog::level::trace) { + std::string s; + adc::utils::AdcCharRangeFromValueRange(s, bytes, std::string_view(", ")); + + logTrace("Send to controller: [{}]", s); + } + + + size_t nbytes, tr_nbytes = bytes.size(); + + // how many bytes are available in Tx-buffer + xclibApiCall(nbytes = pxd_serialWrite(_cameraUnitmap, 0, nullptr, 0), + std::format("pxd_serialWrite({}, 0, NULL, 0)", _cameraUnitmap)); + + if (nbytes) { + tr_nbytes += _clCommandAckBit + _clChecksumBit; + + if (nbytes < tr_nbytes) { + logWarn( + "Not enough of available space in the internal Tx-buffer (needs = {}, available = {})! Nothing to do!", + tr_nbytes, nbytes); + nbytes = 0; + } else { + xclibApiCall( + nbytes = pxd_serialWrite(_cameraUnitmap, 0, (char*)bytes.data(), bytes.size()), + std::format("pxd_serialWrite({}, 0, {}, {})", _cameraUnitmap, (void*)bytes.data(), bytes.size())); + + if (nbytes != bytes.size()) { + throw std::error_code(RaptorEagleCCDError::ERROR_CAMLINK_WRITE); + } + } + } else { + logWarn("No available space in the internal Tx-buffer! Nothing to do!"); + } + + return nbytes; +} + + +/* CREATE COMMANDS AND ATTRIBUTES */ + void RaptorEagleCCD::initAttrComm() { - addAttribute(CAMERA_ATTR_EXPTIME, []() {}); + /* commands */ + + + /* attributes */ + + addAttribute( + CAMERA_ATTR_CAMLINK_SETUP, + [this]() { + logTrace("Return CameraLink setup bits (current values: command-ack = {}, checksum = {})", _clCommandAckBit, + _clChecksumBit); + + return std::vector({_clCommandAckBit, _clChecksumBit}); + }, + [this](const std::vector& setup) { + logDebug("Try to set CameraLink setup bits ..."); + + if (setup.size() < 2) { + throw std::error_code(RaptorEagleCCDError::ERROR_INVALID_ATTR_VALUE); + } + + _clCommandAckBit = setup[0] ? 1 : 0; + _clChecksumBit = + (setup[1] && _clCommandAckBit) ? 1 : 0; // checksum will be enabled only if command ack is set + + logDebug("Set CameraLink setup bits to: command-ack = {}, checksum = {}", _clCommandAckBit, _clChecksumBit); + }); + + // exposure time + addAttribute( + CAMERA_ATTR_EXPTIME, + [this]() { + logTrace("Return acquision duration (current value is {})", _expTime); + + return _expTime; + }, + [this](const double& exp_time) { + logDebug("Try to set acquisition duration to {} seconds ...", exp_time); + + if (_expTime < 0.0) { + logWarn("Acquisition duration must be non-negative!"); + + _expTime = 0.0; + } else if (_expTime > EAGLE_CAMERA_MAX_EXPTIME) { + logWarn("Acquisition duration must not be greater than {} seconds!", EAGLE_CAMERA_MAX_EXPTIME); + + _expTime = EAGLE_CAMERA_MAX_EXPTIME; + } else { + _expTime = exp_time; + } + + logDebug("Acquisition duration is set to {} second", _expTime); + }); + + // number of exposures + addAttribute( + CAMERA_ATTR_NEXP, + [this]() { + logTrace("Return number of frames in acquisition sequence (current value is {})", _frameNumbers); + + return _frameNumbers; + }, + [this](const size_t& nframes) { + _frameNumbers = nframes; + + logDebug("Number of frames in acquisition sequence is set to {}", _frameNumbers); + }); } diff --git a/raptor_eagle_ccd.h b/raptor_eagle_ccd.h index e86ecf0..8379011 100644 --- a/raptor_eagle_ccd.h +++ b/raptor_eagle_ccd.h @@ -18,6 +18,9 @@ class RaptorEagleCCD : public adc::AdcGenericDevice, adc::AdcDeviceCommand<>> base_t; public: + /* some Eagle V camera constants */ + static constexpr double EAGLE_CAMERA_MAX_EXPTIME = 27487.7906944; // in seconds (0xFFFFFFFFFF * 25nsec) + static constexpr std::string_view CAMERA_ATTR_HBIN{"HBIN"}; static constexpr std::string_view CAMERA_ATTR_VBIN{"VBIN"}; static constexpr std::string_view CAMERA_ATTR_ROI_LEFT{"ROI_LEFT"}; @@ -33,6 +36,7 @@ public: static constexpr std::string_view CAMERA_ATTR_NEXP{"NEXP"}; static constexpr std::string_view CAMERA_ATTR_SHUTTER_STATE{"SHUTTER_STATE"}; static constexpr std::string_view CAMERA_ATTR_CCDDIM{"CCDDIM"}; + static constexpr std::string_view CAMERA_ATTR_CAMLINK_SETUP{"CAMLINK_SETUP"}; static constexpr std::string_view CAMERA_CMD_START_EXP{"START_EXP"}; static constexpr std::string_view CAMERA_CMD_STOP_EXP{"STOP_EXP"}; @@ -49,20 +53,39 @@ public: ~RaptorEagleCCD(); private: + typedef std::vector byte_seq_t; + std::string _epixFmtVideoFilename; int _cameraUnitmap; + // CameraLink read/write setup flags + uint8_t _clCommandAckBit; + uint8_t _clChecksumBit; + + // attributes inner variables + double _expTime; + size_t _frameNumbers; + void initAttrComm(); - void initCamera(int unitmap); + void initCamera(int unitmap = 1); void openPIXCI(); void closePIXCI(); + // CameraLink-related low-level methods + + size_t clRead(byte_seq_t& bytes); + size_t clReadAndCheckAck(byte_seq_t& bytes); + + size_t clWrite(const byte_seq_t& bytes); + + // logging helper methods + template void xclibApiCall(int err, const adc::traits::adc_input_char_range auto& func_name) { - if (err) { + if (err < 0) { if (std::ranges::size(func_name)) { logError("XCLIB API call ('{}') returns: {} ({})", func_name, err, pxd_mesgErrorCode(err)); } else { diff --git a/raptor_eagle_exception.h b/raptor_eagle_exception.h index ee41de9..252e75c 100644 --- a/raptor_eagle_exception.h +++ b/raptor_eagle_exception.h @@ -3,7 +3,9 @@ #include #include -enum class RaptorEagleCCDError : int { ERROR_OK, ERROR_INVALID_UNITMAP }; +#include "raptor_eagle_cameralink.h" + +enum class RaptorEagleCCDError : int { ERROR_OK, ERROR_INVALID_UNITMAP, ERROR_INVALID_ATTR_VALUE, ERROR_CAMLINK_WRITE }; namespace std @@ -33,6 +35,10 @@ struct RaptorEagleCCDErrorCategory : std::error_category { return "OK"; case RaptorEagleCCDError::ERROR_INVALID_UNITMAP: return "invalid EPIX-library unitmap"; + case RaptorEagleCCDError::ERROR_INVALID_ATTR_VALUE: + return "invalid camera attribute value"; + case RaptorEagleCCDError::ERROR_CAMLINK_WRITE: + return "not all data were transmitted via CameraLink connection"; default: return "UNKNOWN ERROR"; } @@ -46,6 +52,44 @@ struct RaptorEagleCCDErrorCategory : std::error_category { }; +/* Error category for XCLIB errors */ +struct RaptorEagleControllerErrorCategory : std::error_category { + RaptorEagleControllerErrorCategory() : std::error_category() {} + + const char* name() const noexcept + { + return "EAGLE_CCD_CONTROLLER_ERROR_CATEGORY"; + } + + std::string message(int ec) const + { + switch (ec) { + case CL_ETX: + return "controller OK"; + case CL_ETX_SER_TIMEOUT: + return "partial controller command received (camera timed out)"; + case CL_ETX_CK_SUM_ERR: + return "controller command checksum mismatch"; + case CL_ETX_I2C_ERR: + return "controller I2C internal failure"; + case CL_ETX_UNKNOWN_CMD: + return "controller unknown command"; + case CL_ETX_DONE_LOW: + return "controller EPROM is busy (FPGA reboot)"; + default: + return "UNKNOWN ERROR"; + } + } + + static const RaptorEagleControllerErrorCategory& get() + { + static const RaptorEagleControllerErrorCategory constInst; + return constInst; + } +}; + + + /* Error category for XCLIB errors */ struct XCLIBErrorCategory : std::error_category { XCLIBErrorCategory() : std::error_category() {}