#pragma once #ifdef USE_SPDLOG_LIBRARY #include #endif #include #include #include #include #include #include #include #include "raptor_eagle_exception.h" class RaptorEagleCCD : public adc::AdcGenericDevice, adc::AdcDeviceCommand>, public adc::AdcSpdlogLogger { typedef adc::AdcGenericDevice, adc::AdcDeviceCommand> base_t; public: /* class constants */ // separator char sequence of serialized representation of user FITS keywords array static constexpr std::string_view USER_FITS_KEY_SEP_SEQ{"\t"}; static constexpr size_t DEFAULT_ACQ_RING_BUFFER_SIZE = 3; /* 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 = 2.5E-8 * 0xFFFFFFFFFF; // in seconds (0xFFFFFFFFFF * 25nsec) 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 uchar EAGLE_CAMERA_MAX_XBIN[] = {32, 64}; static constexpr uchar EAGLE_CAMERA_MAX_YBIN[] = {32, 64}; static constexpr double ADC_CALIBRATION_POINT_1 = 0.0; static constexpr double ADC_CALIBRATION_POINT_2 = 40.0; static constexpr double DAC_CALIBRATION_POINT_1 = 0.0; static constexpr double DAC_CALIBRATION_POINT_2 = 40.0; static constexpr double SHUTTER_DELAY_PERIOD = 1.6384; // in millisecs static constexpr double SHUTTER_MAX_DELAY_PERIOD = SHUTTER_DELAY_PERIOD * 0xFF; // in millisecs static constexpr double SHUTTER_DEFAULT_DELAY_PERIOD = 19.66; // in millisecs static constexpr std::chrono::milliseconds MICRO_RESET_TIME_CONSTANT{100}; // microcontroller reset timeout // NOTE: the timeout must not be lesser then ~100msecs duration of reset process // (see instruction manual rev 1.1) static constexpr std::chrono::milliseconds MICRO_RESET_DEFAULT_TIMEOUT{MICRO_RESET_TIME_CONSTANT + std::chrono::milliseconds(100)}; static constexpr std::chrono::milliseconds FPGA_RESET_TIME_CONSTANT{500}; // FPGA reset timeout // NOTE: the timeout must not be lesser then ~500msecs duration of reset process // (see instruction manual rev 1.1) static constexpr std::chrono::milliseconds FPGA_RESET_DEFAULT_TIMEOUT{FPGA_RESET_TIME_CONSTANT + std::chrono::milliseconds(1000)}; // 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 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"}; static constexpr std::string_view CAMERA_ATTR_TRIGGER_MODE{"TRIGGER_MODE"}; static constexpr std::string_view CAMERA_ATTR_READ_RATE{"READ_RATE"}; static constexpr std::string_view CAMERA_ATTR_READ_MODE{"READ_MODE"}; static constexpr std::string_view CAMERA_ATTR_TECPOINT{"TECPOINT"}; static constexpr std::string_view CAMERA_ATTR_TECSTATE{"TECSTATE"}; static constexpr std::string_view CAMERA_ATTR_TECPOINT_DAC{"TECPOINT_DAC"}; static constexpr std::string_view CAMERA_ATTR_CCD_TEMP{"CCD_TEMP"}; static constexpr std::string_view CAMERA_ATTR_PCB_TEMP{"PCB_TEMP"}; static constexpr std::string_view CAMERA_ATTR_EXPTIME{"EXPTIME"}; static constexpr std::string_view CAMERA_ATTR_FRAME_RATE{"FRAME_RATE"}; 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_SHUTTER_OPENDELAY{"SHUTTER_OPEN_DELAY"}; static constexpr std::string_view CAMERA_ATTR_SHUTTER_CLOSEDELAY{"SHUTTER_CLOSE_DELAY"}; static constexpr std::string_view CAMERA_ATTR_FITS_FILENAME{"FITS_FILENAME"}; static constexpr std::string_view CAMERA_ATTR_FITS_TEMPLATE{"FITS_TEMPLATE"}; 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"}; static constexpr std::string_view CAMERA_CMD_ABORT_EXP{"ABORTEXP"}; static constexpr std::string_view CAMERA_CMD_CLEAR_PERM_KEYW{"CLEAR_PERM_FITS_KEYW"}; static constexpr std::string_view CAMERA_CMD_CLEAR_CURR_KEYW{"CLEAR_CURR_FITS_KEYW"}; static constexpr std::string_view CAMERA_CMD_START_RESET_MICRO{"RESET_MICRO"}; static constexpr std::string_view CAMERA_CMD_START_RESET_FPGA{"RESET_FPGA"}; // some character attributes values static constexpr std::string_view CAMERA_ATTR_STR_INVALID{"INVALID"}; static constexpr std::string_view CAMERA_ATTR_READ_MODE_NORMAL{"NORMAL"}; static constexpr std::string_view CAMERA_ATTR_READ_MODE_TEST{"TEST"}; static constexpr std::string_view CAMERA_ATTR_READ_RATE_SLOW{"SLOW"}; static constexpr std::string_view CAMERA_ATTR_READ_RATE_FAST{"FAST"}; static constexpr std::string_view CAMERA_ATTR_TECSTATE_ON{"ON"}; static constexpr std::string_view CAMERA_ATTR_TECSTATE_OFF{"OFF"}; static constexpr std::string_view CAMERA_ATTR_SHUTTER_STATE_OPEN{"OPEN"}; // always open static constexpr std::string_view CAMERA_ATTR_SHUTTER_STATE_CLOSED{"CLOSED"}; // always closed static constexpr std::string_view CAMERA_ATTR_SHUTTER_STATE_EXP{"EXP"}; // open during acquisition static constexpr std::string_view CAMERA_ATTR_GAIN_HIGH{"HIGH"}; static constexpr std::string_view CAMERA_ATTR_GAIN_LOW{"LOW"}; // external trigger, rising edge enabled static constexpr std::string_view CAMERA_ATTR_TRIGGER_MODE_EXT_RISING{"EXTERNAL_RISING"}; // external trigger, falling edge enabled static constexpr std::string_view CAMERA_ATTR_TRIGGER_MODE_EXT_FALLING{"EXTERNAL_FALLING"}; // internal trigger for continuos acquisition static constexpr std::string_view CAMERA_ATTR_TRIGGER_MODE_ITR{"ITR"}; // fixed frame rate acquisition static constexpr std::string_view CAMERA_ATTR_TRIGGER_MODE_FFR{"FFR"}; // snapshot static constexpr std::string_view CAMERA_ATTR_TRIGGER_MODE_SNAPSHOT{"SNAPSHOT"}; // 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_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 logger = spdlog::null_logger_mt("EAGLE_CCD_NULL_LOGGER")); RaptorEagleCCD(std::shared_ptr logger = spdlog::null_logger_mt("EAGLE_CCD_NULL_LOGGER")); ~RaptorEagleCCD(); private: typedef std::vector byte_seq_t; struct acq_params_t { std::chrono::utc_clock::time_point startTime; std::chrono::utc_clock::time_point abortTime; bool saveInAbort; double expTime; // in seconds uint16_t roiStartX; uint16_t roiStartY; uint16_t roiWidth; uint16_t roiHeight; uint8_t binX; uint8_t binY; std::string_view shutterState; std::string_view readRate; std::string_view readMode; std::string_view gain; double ccdTemp; double tecSetPoint; bool tecState; double pcbTemp; std::string filename; std::string templateFilename; std::vector permanentKeywords; std::vector currentKeywords; // std::unique_ptr imageBuffer; // size_t imageBufferSize; // size_t imageBufferRows; }; class AcquisitionProcess : public std::enable_shared_from_this { friend class RaptorEagleCCD; public: AcquisitionProcess(RaptorEagleCCD*); ~AcquisitionProcess(); void start(const std::shared_ptr& params); // asynchronous method! void stop(bool save = true); private: inline static std::atomic_bool isAcqInProgress = false; RaptorEagleCCD* _manager; std::shared_ptr _acqParams{}; std::unique_ptr _imageBuffer{}; size_t _imageBufferSize = 0; size_t _imageBufferRows = 0; std::future _snapAndCopyFuture; // std::future _saveFitsFileFuture; }; std::string _epixFmtVideoFilename; int _cameraUnitmap; uint16_t _dimCCD[2] = {2048, 2048}; // init to E2V 4240 CCD dimension int _bitsPerPixel = 16; // init to E2V 4240 CCD int _imageFrameBuffNumber = 1; // CCD temperature and TEC set point calibration relation coefficients double _adcCCDTempCalibCoeffs[2] = {0, 0}; // [k, b], Temp(degs C) = k*ADC + b // [k, b, k_rev, b_rev] // Temp = k*DAC + b // DAC = k_rev*Temp + b_rev double _dacTECSetPointCalibCoeffs[4] = {0, 0}; // CameraLink read/write setup flags uint8_t _clCommandAckBit; uint8_t _clChecksumBit; std::recursive_mutex _camlinkMutex; // attributes inner variables std::atomic_size_t _frameNumbers; std::string _currentFitsFile; // current acquisition FITS filename std::string _currentTemplateFile; // CFITSIO template filename std::vector _currentFitsKeywords{}; // current acquisition FITS keywords std::vector _permanentFitsKeywords{}; // permanent user FITS keywords std::string_view _cameraStatus = CAMERA_ATTR_CAMERA_STATUS_IDLE; // camera current status // std::list> _acqRingBuffer; std::list, size_t>> _acqRingBuffer; // hardware version info byte_seq_t _microVersion; // microcontroller version: _microVersion[0] - major, _microVersion[1] - minor byte_seq_t _FPGAVersion; // same for FPGA version // man data is 18-byte array + 2 possible bytes (ETX and checksum) byte_seq_t _manufacturerData = byte_seq_t(20); // extract fields according to instruction manual (WARNING: works only for little-endian platforms!) const uint16_t& _cameraSerialNumber{ *reinterpret_cast(_manufacturerData.data())}; // camera serial number (the first 2 bytes) std::chrono::year_month_day _buildDate; // 3 bytes const std::string_view _buildCode{reinterpret_cast(_manufacturerData.data() + 5), 5}; // 5 bytes // must be interpretated as 2-element array of calibration points [at 0C, at +40C] (4 bytes) const uint16_t* _adcCCDTempCalibData{reinterpret_cast(_manufacturerData.data() + 10)}; // must be interpretated as 2-element array of calibration points [at 0C, at +40C] (4 bytes) const uint16_t* _dacTECSetPointCalibData{reinterpret_cast(_manufacturerData.data() + 14)}; // acquisition process members std::mutex _acqProcessesMutex; std::list> _acqProcesses{}; // std::future _doSnapAndCopyFuture; // std::future _saveFitsFile; // std::atomic_bool _isAcqInProgress; void initAttrComm(); bool 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 clReadAndCheckAck(); size_t clWrite(const byte_seq_t& bytes); // CameraLink-related registers read/write methods // there are two kind of SET-READ_REGISTER-ADDRESS command in the current version of the camera controller firmware: // {0x53, 0xE0, 0x01} and // {0x53, 0xE0, 0x02} // // NOTE: given 'set_addr_cmd' byte sequence must be one-byte longer than the command itself!!! byte_seq_t readRegisters(const byte_seq_t& addrs, byte_seq_t set_addr_cmd = {0x53, 0xE0, 0x01, 0x00}); void writeRegisters(const byte_seq_t& addrs, const byte_seq_t& values); // camera and FPGA control registers getter/setter 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); // set 'Abort' bit in trigger register std::bitset<8> getTriggerRegister(); std::chrono::utc_clock::time_point setTriggerRegister(const std::bitset<8> bits); std::chrono::utc_clock::time_point setTriggerRegisterBit(const size_t bit_pos); std::chrono::utc_clock::time_point clearTriggerRegisterBit(const size_t bit_pos); std::chrono::utc_clock::time_point flipTriggerRegisterBit(const size_t bit_pos); // reset hardware methods // returns true if OK, false - timeout bool resetMicro(const std::chrono::milliseconds& timeout = MICRO_RESET_DEFAULT_TIMEOUT); bool resetFPGA(const std::chrono::milliseconds& timeout = FPGA_RESET_DEFAULT_TIMEOUT); // hardware info methods void getHardwareInfo(); void getMicroVersion(); void getFPGAVersion(); // acquisition process methods void startAquisition(); void stopAcquisition(bool save_acq); // logging helper methods template 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); if (std::ranges::size(func_name)) { logError("XCLIB API call ('{}') returns: {} ({})", func_name, err, s_err); } else { logError("XCLIB API call returns: {} ({})", err, s_err); } if constexpr (!NOEXCEPT) { throw std::system_error(err, XCLIBErrorCategory::get()); } } else { if (std::ranges::size(func_name)) { // 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); logMessage(log_level, "XCLIB API call finished successfully (exitcode = {})", err); } } } template void xclibApiCall(int err, loglevel_t log_level = spdlog::level::debug) { xclibApiCall(err, std::string_view(""), log_level); } };