diff --git a/.gitignore b/.gitignore index 4a0b530..31b39c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # This file is used to ignore files which are generated # ---------------------------------------------------------------------------- +build + *~ *.autosave *.a diff --git a/raptor_eagle_cameralink.h b/raptor_eagle_cameralink.h index 4fa49eb..59ff57c 100644 --- a/raptor_eagle_cameralink.h +++ b/raptor_eagle_cameralink.h @@ -41,18 +41,58 @@ static constexpr char CL_ETX_DONE_LOW = 0x55; // system state -static constexpr char CL_SYSTEM_STATE_CK_SUM = 0x40; // 6-th bit -static constexpr char CL_SYSTEM_STATE_ACK = 0x10; // 4-th bit -static constexpr char CL_SYSTEM_STATE_FPGA_BOOT_OK = 0x4; // 2-nd bit -static constexpr char CL_SYSTEM_STATE_FPGA_RST_HOLD = 0x2; // 1-st bit -static constexpr char CL_SYSTEM_STATE_FPGA_EEPROM_COMMS = 0x1; // 0-th bit +static constexpr size_t CL_SYSTEM_STATUS_FPGA_EEPROM_COMMS_BIT = 0; +static constexpr size_t CL_SYSTEM_STATUS_FPGA_RST_HOLD_BIT = 1; +static constexpr size_t CL_SYSTEM_STATUS_FPGA_BOOT_OK_BIT = 2; +static constexpr size_t CL_SYSTEM_STATUS_ACK_BIT = 4; +static constexpr size_t CL_SYSTEM_STATUS_CK_SUM_BIT = 6; + +static constexpr char CL_SYSTEM_STATUS_CK_SUM = 0x40; // 6-th bit +static constexpr char CL_SYSTEM_STATUS_ACK = 0x10; // 4-th bit +static constexpr char CL_SYSTEM_STATUS_FPGA_BOOT_OK = 0x4; // 2-nd bit +static constexpr char CL_SYSTEM_STATUS_FPGA_RST_HOLD = 0x2; // 1-st bit +static constexpr char CL_SYSTEM_STATUS_FPGA_EEPROM_COMMS = 0x1; // 0-th bit + +namespace details +{ + +static constexpr std::string_view cl_system_status_bit(const size_t pos) +{ + return pos == CL_SYSTEM_STATUS_FPGA_EEPROM_COMMS_BIT ? "FPGA COMMS EPROM ENABLED" + : pos == CL_SYSTEM_STATUS_FPGA_RST_HOLD_BIT ? "HOLD FPGA RST" + : pos == CL_SYSTEM_STATUS_FPGA_BOOT_OK_BIT ? "FPGA BOOTED OK" + : pos == CL_SYSTEM_STATUS_ACK_BIT ? "COMM ACK ENABLED" + : pos == CL_SYSTEM_STATUS_CK_SUM_BIT ? "CHECKSUM ENABLED" + : "UNKNOWN"; +} + +} // namespace details + // FPGA CTRL register +static constexpr size_t CL_FPGA_CTRL_REG_HIGH_GAIN_BIT = 7; // 7-th bit (0 if high pre-amp gain) +static constexpr size_t CL_FPGA_CTRL_REG_TEMP_TRIP_RST_BIT = 1; // 1-st bit +static constexpr size_t CL_FPGA_CTRL_REG_ENABLE_TEC_BIT = 0; // 0-th bit + static constexpr char CL_FPGA_CTRL_REG_HIGH_GAIN = 0x80; // 7-th bit (0 if high pre-amp gain) static constexpr char CL_FPGA_CTRL_REG_TMP_TRIP_RST = 0x2; // 1-st bit static constexpr char CL_FPGA_CTRL_REG_ENABLE_TEC = 0x1; // 0-th bit + +namespace details +{ + +static constexpr std::string_view cl_fpga_ctrl_reg_bit(const size_t pos) +{ + return pos == CL_FPGA_CTRL_REG_HIGH_GAIN_BIT ? "HIGH GAIN ENABLED" + : pos == CL_FPGA_CTRL_REG_TEMP_TRIP_RST_BIT ? "OVERTEMP TRIPPED" + : pos == CL_FPGA_CTRL_REG_ENABLE_TEC_BIT ? "TEC ENABLED" + : "UNKNOWN"; +} + +} // namespace details + // trigger mode static constexpr char CL_TRIGGER_MODE_ENABLE_RISING_EDGE = 0x80; // 7-th bit @@ -96,11 +136,14 @@ static constexpr double DAC_CALIBRATION_POINT_2 = 40.0; // at +40 Celcius degre static constexpr std::initializer_list CL_EXPTIME_ADDR = {0xED, 0xEE, 0xEF, 0xF0, 0xF1}; static constexpr std::initializer_list CL_FRAMERATE_ADDR = {0xDC, 0xDD, 0xDE, 0xDF, 0xE0}; -static constexpr std::initializer_list CL_ROILEFT_ADDR = {0xB6, 0xB7}; -static constexpr std::initializer_list CL_ROITOP_ADDR = {0xBA, 0xBB}; +static constexpr std::initializer_list CL_ROI_STARTX_ADDR = {0xB6, 0xB7}; +static constexpr std::initializer_list CL_ROI_STARTY_ADDR = {0xBA, 0xBB}; static constexpr std::initializer_list CL_ROIWIDTH_ADDR = {0xB4, 0xB5}; static constexpr std::initializer_list CL_ROIHEIGHT_ADDR = {0xB8, 0xB9}; +static constexpr std::initializer_list CL_XBIN_ADDR = {0xA1}; +static constexpr std::initializer_list CL_YBIN_ADDR = {0xA2}; + /* COMMANDS */ static unsigned char CL_COMMAND_SET_ADDRESS[] = { diff --git a/raptor_eagle_ccd.cpp b/raptor_eagle_ccd.cpp index 9fa4dda..1b9bcdb 100644 --- a/raptor_eagle_ccd.cpp +++ b/raptor_eagle_ccd.cpp @@ -150,6 +150,148 @@ RaptorEagleCCD::~RaptorEagleCCD() } + +/* PUBLIC METHODS */ + + +/* system state get/set */ + +std::bitset<8> RaptorEagleCCD::getSystemState() +{ + std::lock_guard lock_guard(_camlinkMutex); + + byte_seq_t ans; + + clWrite({0x49}); + clReadAndCheckAck(ans); + + std::bitset<8> bits{ans[0]}; + + logDebug("Get system state as 0b{} bits", bits.to_string()); + + return bits; +} + + +void RaptorEagleCCD::setSystemState(const std::bitset<8>& bits) +{ + std::lock_guard lock_guard(_camlinkMutex); + + logDebug("Try to set system state to 0b{} bits", bits.to_string()); + + uint8_t status = static_cast(bits.to_ulong()); + + clWrite({0x4F, status}); + clReadAndCheckAck(); +} + + +void RaptorEagleCCD::setSystemStateBit(const size_t pos) +{ + std::lock_guard lock_guard(_camlinkMutex); + + auto bits = getSystemState(); + + logDebug("Set system state bit {}", details::cl_system_status_bit(pos)); + bits.set(pos); + + setSystemState(bits); +} + + +void RaptorEagleCCD::clearSystemStateBit(const size_t pos) +{ + std::lock_guard lock_guard(_camlinkMutex); + + auto bits = getSystemState(); + + logDebug("Clear system state bit {}", details::cl_system_status_bit(pos)); + bits.reset(pos); + + setSystemState(bits); +} + + +void RaptorEagleCCD::flipSystemStateBit(const size_t pos) +{ + std::lock_guard lock_guard(_camlinkMutex); + + auto bits = getSystemState(); + + logDebug("Flip system state bit {}", details::cl_system_status_bit(pos)); + bits.flip(pos); + + setSystemState(bits); +} + + +// FPGS control register get/set + +std::bitset<8> RaptorEagleCCD::getFPGAState() +{ + std::lock_guard log_guard(_camlinkMutex); + + auto ans = readRegisters({0x00}); + + std::bitset<8> bits{ans[0]}; + + logDebug("Get FPGS control register as 0b{} bits", bits.to_string()); + + return bits; +} + + +void RaptorEagleCCD::setFPGAState(const std::bitset<8>& bits) +{ + std::lock_guard lock_guard(_camlinkMutex); + + logDebug("Try to set FPGA control register to 0b{} bits", bits.to_string()); + + uint8_t status = static_cast(bits.to_ulong()); + + writeRegisters({0x00}, {status}); +} + + +void RaptorEagleCCD::setFPGAStateBit(const size_t pos) +{ + std::lock_guard lock_guard(_camlinkMutex); + + auto bits = getFPGAState(); + + logDebug("Set FPGA control register bit {}", details::cl_fpga_ctrl_reg_bit(pos)); + bits.set(pos); + + setFPGAState(bits); +} + + +void RaptorEagleCCD::clearFPGAStateBit(const size_t pos) +{ + std::lock_guard lock_guard(_camlinkMutex); + + auto bits = getFPGAState(); + + logDebug("Clear FPGA control register bit {}", details::cl_fpga_ctrl_reg_bit(pos)); + bits.reset(pos); + + setFPGAState(bits); +} + + +void RaptorEagleCCD::flipFPGAStateBit(const size_t pos) +{ + std::lock_guard lock_guard(_camlinkMutex); + + auto bits = getFPGAState(); + + logDebug("Flip FPGA control register bit {}", details::cl_fpga_ctrl_reg_bit(pos)); + bits.flip(pos); + + setFPGAState(bits); +} + + /* PRIVATE METHODS */ void RaptorEagleCCD::initCamera(int unitmap) @@ -193,10 +335,67 @@ void RaptorEagleCCD::closePIXCI() } +/* SYSTEM STATUS */ + + +/*------------*/ + +const RaptorEagleCCD::SystemStatus RaptorEagleCCD::getSystemStatus() +{ + std::lock_guard lock_guard(_camlinkMutex); + + byte_seq_t ans; + + clWrite({0x49}); + clReadAndCheckAck(ans); + + SystemStatus status; + status.ackEnabled = ans[0] & CL_SYSTEM_STATUS_ACK; + status.checkSumEnabled = ans[0] & CL_SYSTEM_STATUS_CK_SUM; + status.bootedFPGA = ans[0] & CL_SYSTEM_STATUS_FPGA_BOOT_OK; + status.commsFPGAEnabled = ans[0] & CL_SYSTEM_STATUS_FPGA_EEPROM_COMMS; + status.holdFPGAInReset = !(ans[0] & CL_SYSTEM_STATUS_FPGA_RST_HOLD); // hold in RESET if bit is 0 + + return status; +} + +void RaptorEagleCCD::setSystemStatus(const RaptorEagleCCD::SystemStatus& status) +{ + uint8_t st = 0; + + if (status.ackEnabled) + st |= CL_SYSTEM_STATUS_ACK; + + if (status.checkSumEnabled) + st |= CL_SYSTEM_STATUS_CK_SUM; + + if (status.bootedFPGA) + st |= CL_SYSTEM_STATUS_FPGA_BOOT_OK; + + if (status.commsFPGAEnabled) + st |= CL_SYSTEM_STATUS_FPGA_EEPROM_COMMS; + + if (status.holdFPGAInReset) + st &= ~CL_SYSTEM_STATUS_FPGA_RST_HOLD; + + setSystemStatus(st); +} + +void RaptorEagleCCD::setSystemStatus(const uint8_t& status) +{ + std::lock_guard lock_guard(_camlinkMutex); + + clWrite({0x4F, status}); + clReadAndCheckAck(); +} + + /* CameraLink-RELATED METHODS */ size_t RaptorEagleCCD::clRead(byte_seq_t& bytes) { + std::lock_guard lock_guard(_camlinkMutex); + size_t nbytes; // how many byte are available @@ -230,6 +429,8 @@ size_t RaptorEagleCCD::clRead(byte_seq_t& bytes) size_t RaptorEagleCCD::clReadAndCheckAck(byte_seq_t& bytes) { + std::lock_guard lock_guard(_camlinkMutex); + auto nbytes = clRead(bytes); if (_clCommandAckBit && nbytes) { @@ -253,6 +454,8 @@ size_t RaptorEagleCCD::clReadAndCheckAck() // 'bytes' must contain only data without trailing ETX and checksum bytes! size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes) { + std::lock_guard lock_guard(_camlinkMutex); + static unsigned char etx_checksum_bytes[] = {CL_ETX, 0xFF}; if (bytes.empty()) { @@ -322,7 +525,7 @@ RaptorEagleCCD::byte_seq_t RaptorEagleCCD::readRegisters(const RaptorEagleCCD::b byte_seq_t set_addr_cmd) { // to protect in multi-threading environment (multiple read-write operations, see below) - std::lock_guard lock_guard(_camlinkMutex); + std::lock_guard lock_guard(_camlinkMutex); byte_seq_t reg_vals, ans(3); @@ -359,7 +562,7 @@ RaptorEagleCCD::byte_seq_t RaptorEagleCCD::readRegisters(const RaptorEagleCCD::b void RaptorEagleCCD::writeRegisters(const byte_seq_t& addrs, const byte_seq_t& values) { // to protect in multi-threading environment (multiple read-write operations, see below) - std::lock_guard lock_guard(_camlinkMutex); + std::lock_guard lock_guard(_camlinkMutex); if (addrs.empty() || values.empty()) { logWarn("Registers addresses or values array is an empty! Nothing to do!"); @@ -499,7 +702,8 @@ void RaptorEagleCCD::initAttrComm() }); - auto create12BitAttr = [this](std::string_view name, auto reg_addrs, std::string_view log_mark) { + // helper to setup 12-bit register attributes + auto create12BitAttr = [this](std::string_view name, auto reg_addrs, auto&& validator, std::string_view log_mark) { return RaptorEagleCCD::attribute_t( name, [this, reg_addrs, log_mark]() { @@ -510,19 +714,152 @@ void RaptorEagleCCD::initAttrComm() return v; }, - [this, reg_addrs, log_mark](const uint16_t& val) { logDebug("Try to set {} ...", log_mark); }); + [this, reg_addrs, log_mark, + wrapper = adc::traits::adc_pf_wrapper(std::forward(validator))]( + const uint16_t& val) mutable { + logDebug("Try to set {} to {} ...", log_mark, val); + + // call perfectly-forwarded validator + auto v_res = std::forward>(std::get<0>(wrapper))(val); + if (v_res.second.size()) { // warning + logWarn("{}", v_res.second); + } + + auto bytes = details::convertUIntTo12Bit(v_res.first); + writeRegisters(reg_addrs, bytes); + + logDebug("{} is set to {}", log_mark, v_res.first); + }); }; // ROI left - addAttribute( - CAMERA_ATTR_ROI_LEFT, - [this]() { - auto bytes = readRegisters(CL_ROILEFT_ADDR); - uint16_t v = details::convert12BitToUInt(bytes); + addAttribute(create12BitAttr( + CAMERA_ATTR_ROI_STARTX, CL_ROI_STARTX_ADDR, + [this](const uint16_t& val) { // validator + std::pair res{val, ""}; - logTrace("Return ROI X-offset (current value: {})", v); + if (val < 1) { + res.first = 1; + res.second = "The ROI X-offset must start from 1 (FITS notation)"; + } else if (val > _dimCCD[0]) { + res.first = _dimCCD[0]; + res.second = std::format( + "The ROI X-offset must not be greater than CCD X-dimension of {} pixels (FITS notation)", + _dimCCD[0]); + } - return v; + return res; }, - [this](const uint16_t& val) {}); + "ROI X-offset")); + + + // ROI top + addAttribute(create12BitAttr( + CAMERA_ATTR_ROI_STARTY, CL_ROI_STARTY_ADDR, + [this](const uint16_t& val) { // validator + std::pair res{val, ""}; + + if (val < 1) { + res.first = 1; + res.second = "The ROI Y-offset must start from 1 (FITS notation)"; + } else if (val > _dimCCD[1]) { + res.first = _dimCCD[1]; + res.second = std::format( + "The ROI Y-offset must not be greater than CCD Y-dimension of {} pixels (FITS notation)", + _dimCCD[1]); + } + + return res; + }, + "ROI Y-offset")); + + + // ROI width + addAttribute(create12BitAttr( + CAMERA_ATTR_ROI_WIDTH, CL_ROIWIDTH_ADDR, + [this](const uint16_t& val) { // validator + std::pair res{val, ""}; + + if (val < 1) { + res.first = 1; + res.second = "The ROI width must start from 1"; + } else if (val > _dimCCD[0]) { + res.first = _dimCCD[0]; + res.second = std::format( + "The ROI width must not be greater than CCD dimension of {} pixels (FITS notation)", _dimCCD[0]); + } + + return res; + }, + "ROI width")); + + // ROI height + addAttribute(create12BitAttr( + CAMERA_ATTR_ROI_HEIGHT, CL_ROIHEIGHT_ADDR, + [this](const uint16_t& val) { // validator + std::pair res{val, ""}; + + // ROI height can be 0 (see Eagle V 4240 instruction manual) + if (val > _dimCCD[1]) { + res.first = _dimCCD[1]; + res.second = std::format( + "The ROI height must not be greater than CCD dimension of {} pixels (FITS notation)", _dimCCD[1]); + } + + return res; + }, + "ROI height")); + + // X-bin + addAttribute(create12BitAttr( + CAMERA_ATTR_XBIN, CL_XBIN_ADDR, + [this](const uint16_t& val) { // validator (1-32, 64) + std::pair res{val, ""}; + + 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]; + + 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 res; + }, + "XBIN")); + + + // Y-bin + addAttribute(create12BitAttr( + CAMERA_ATTR_YBIN, CL_YBIN_ADDR, + [this](const uint16_t& val) { // validator (1-32, 64) + std::pair res{val, ""}; + + 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]; + + 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 res; + }, + "YBIN")); } diff --git a/raptor_eagle_ccd.h b/raptor_eagle_ccd.h index f53cf6e..1b08dfc 100644 --- a/raptor_eagle_ccd.h +++ b/raptor_eagle_ccd.h @@ -12,10 +12,15 @@ #include "raptor_eagle_exception.h" -class RaptorEagleCCD : public adc::AdcGenericDevice, adc::AdcDeviceCommand<>>, +class RaptorEagleCCD : public adc::AdcGenericDevice, + adc::AdcDeviceCommand>, adc::AdcSpdlogLogger { - typedef adc::AdcGenericDevice, adc::AdcDeviceCommand<>> base_t; + typedef adc::AdcGenericDevice, + adc::AdcDeviceCommand> + base_t; public: /* some Eagle V camera constants */ @@ -24,11 +29,13 @@ public: static constexpr size_t EAGLE_CAMERA_MAX_FRAMERATE = 0xFFFFFFFFFF * 40; // in MHz (0xFFFFFFFFFF * 40MHz) static constexpr uint16_t EAGLE_CAMERA_CCDDIM[] = {2048, 2048}; + static constexpr uint16_t EAGLE_CAMERA_MAX_XBIN[] = {32, 64}; + static constexpr uint16_t EAGLE_CAMERA_MAX_YBIN[] = {32, 64}; - 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"}; - static constexpr std::string_view CAMERA_ATTR_ROI_TOP{"ROI_TOP"}; + static constexpr std::string_view CAMERA_ATTR_XBIN{"XBIN"}; + static constexpr std::string_view CAMERA_ATTR_YBIN{"YBIN"}; + static constexpr std::string_view CAMERA_ATTR_ROI_STARTX{"ROI_STARTX"}; + static constexpr std::string_view CAMERA_ATTR_ROI_STARTY{"ROI_STARTY"}; static constexpr std::string_view CAMERA_ATTR_ROI_WIDTH{"ROI_WIDTH"}; static constexpr std::string_view CAMERA_ATTR_ROI_HEIGHT{"ROI_HEIGHT"}; static constexpr std::string_view CAMERA_ATTR_GAIN{"GAIN"}; @@ -70,17 +77,35 @@ public: ~RaptorEagleCCD(); + std::bitset<8> getSystemState(); + void setSystemState(const std::bitset<8>& bits); + void setSystemStateBit(const size_t bit_pos); + void clearSystemStateBit(const size_t bit_pos); + void flipSystemStateBit(const size_t bit_pos); + + std::bitset<8> getFPGAState(); + void setFPGAState(const std::bitset<8>& bits); + void setFPGAStateBit(const size_t bit_pos); + void clearFPGAStateBit(const size_t bit_pos); + void flipFPGAStateBit(const size_t bit_pos); + + const SystemStatus getSystemStatus(); + void setSystemStatus(const SystemStatus&); + void setSystemStatus(const uint8_t&); + private: typedef std::vector byte_seq_t; std::string _epixFmtVideoFilename; int _cameraUnitmap; + uint16_t _dimCCD[2] = {2048, 2048}; // init to E2V 4240 CCD dimension + // CameraLink read/write setup flags uint8_t _clCommandAckBit; uint8_t _clChecksumBit; - std::mutex _camlinkMutex; + std::recursive_mutex _camlinkMutex; // attributes inner variables size_t _frameNumbers;