From ab7b439554cc62d960545279ace5323bc56c0936 Mon Sep 17 00:00:00 2001 From: David Hale Date: Thu, 2 Apr 2026 12:16:52 -0700 Subject: [PATCH 1/2] starts cryoscope implementation: - adds Instruments/cryoscope/ specific files - renames RXRV -> VIDEORXR for consistency with ACF files - adds Exposuremode_VIDEORXR class - adds producer thread for VIDEORXR affecting all instruments: - adds ImageQueue class - adds get functions to Config class - camerad now initializes the logging system --- camerad/CMakeLists.txt | 1 + camerad/Instruments/cryoscope/cryoscope.cmake | 11 ++ .../cryoscope/cryoscope_exposure_modes.cpp | 44 +++++ .../cryoscope/cryoscope_exposure_modes.h | 45 +++++ .../cryoscope/cryoscope_instrument.cpp | 173 ++++++++++++++++++ .../cryoscope/cryoscope_instrument.h | 39 ++++ .../cryoscope/cryoscope_interface_factory.cpp | 23 +++ camerad/archon_exposure_modes.cpp | 60 ------ camerad/archon_exposure_modes.h | 46 +++-- camerad/archon_exposure_modes_rxrv.cpp | 172 +++++++++++++++++ camerad/archon_interface.cpp | 17 +- camerad/camerad.cpp | 14 +- camerad/exposure_modes.h | 5 +- camerad/image_process.cpp | 12 +- camerad/image_queue.h | 58 ++++++ utils/config.h | 46 +++++ 16 files changed, 678 insertions(+), 88 deletions(-) create mode 100644 camerad/Instruments/cryoscope/cryoscope.cmake create mode 100644 camerad/Instruments/cryoscope/cryoscope_exposure_modes.cpp create mode 100644 camerad/Instruments/cryoscope/cryoscope_exposure_modes.h create mode 100644 camerad/Instruments/cryoscope/cryoscope_instrument.cpp create mode 100644 camerad/Instruments/cryoscope/cryoscope_instrument.h create mode 100644 camerad/Instruments/cryoscope/cryoscope_interface_factory.cpp create mode 100644 camerad/archon_exposure_modes_rxrv.cpp create mode 100644 camerad/image_queue.h diff --git a/camerad/CMakeLists.txt b/camerad/CMakeLists.txt index 7fbecc9..400b686 100644 --- a/camerad/CMakeLists.txt +++ b/camerad/CMakeLists.txt @@ -50,6 +50,7 @@ elseif (CONTROLLER STREQUAL "archon") ${CAMERAD_DIR}/archon_interface.cpp ${CAMERAD_DIR}/archon_controller.cpp ${CAMERAD_DIR}/archon_exposure_modes.cpp + ${CAMERAD_DIR}/archon_exposure_modes_rxrv.cpp ) # ---------------------------------------------------------------------------- # AstroCam ARC-64/66 PCI/e diff --git a/camerad/Instruments/cryoscope/cryoscope.cmake b/camerad/Instruments/cryoscope/cryoscope.cmake new file mode 100644 index 0000000..e472c26 --- /dev/null +++ b/camerad/Instruments/cryoscope/cryoscope.cmake @@ -0,0 +1,11 @@ +# ---------------------------------------------------------------------------- +# @file Instruments/cryoscope/cryoscope.cmake +# @brief CryoScope-specific input to the CMake build system for camerad +# @author David Hale +# ---------------------------------------------------------------------------- + +set (INSTRUMENT_SOURCES + ${CAMERAD_DIR}/Instruments/cryoscope/cryoscope_instrument.cpp + ${CAMERAD_DIR}/Instruments/cryoscope/cryoscope_interface_factory.cpp + ${CAMERAD_DIR}/Instruments/cryoscope/cryoscope_exposure_modes.cpp + ) diff --git a/camerad/Instruments/cryoscope/cryoscope_exposure_modes.cpp b/camerad/Instruments/cryoscope/cryoscope_exposure_modes.cpp new file mode 100644 index 0000000..6b9df4b --- /dev/null +++ b/camerad/Instruments/cryoscope/cryoscope_exposure_modes.cpp @@ -0,0 +1,44 @@ +/** + * @file Instruments/cryoscope/cryoscope_exposure_modes.h + * @brief implements CryoScope-specific exposure modes + * + */ + +#include "archon_exposure_modes.h" +#include "cryoscope_exposure_modes.h" +#include "archon_interface.h" + +namespace Camera { + + /***** Camera::ExposureModeXXX::expose *************************************/ + /** + * @brief implementation of cryoscope-specific expose for a non-standard mode + * + */ + long ExposureModeXXX::expose() { + const std::string function("Camera::ExposureModeXXX::expose"); + logwrite(function, "meep meep"); + return NO_ERROR; + } + /***** Camera::ExposureModeXXX::expose *************************************/ + + + /***** Camera::ExposureModeXXX::image_acquisition_thread ********************/ + /** + * @brief producer thread for non-standard ExposureMode XXX + * @details Spawned by Camera::ArchonInterface::do_expose() + * + */ + void ExposureModeXXX::image_acquisition_thread() { + const std::string function("Camera::ExposureModeXXX::image_acquisition_thread"); + char message[256]; + + logwrite(function, "mode args:"); + + for (const auto &arg : this->args) { + logwrite(function, arg); + } + } + /***** Camera::ExposureModeXXX::image_acquisition_thread ********************/ + +} diff --git a/camerad/Instruments/cryoscope/cryoscope_exposure_modes.h b/camerad/Instruments/cryoscope/cryoscope_exposure_modes.h new file mode 100644 index 0000000..c70c14c --- /dev/null +++ b/camerad/Instruments/cryoscope/cryoscope_exposure_modes.h @@ -0,0 +1,45 @@ +/** + * @file Instruments/cryoscope/cryoscope_exposure_modes.h + * @brief delcares CryoScope-specific exposure mode classes + * @details Declares classes that implement exposure modes supported by + * CryoScope. These classes override virtual functions in the + * ExposureMode base class to provide mode-specific behavior. + * + */ + +#pragma once + +#include "exposure_modes.h" // ExposureMode base class + +namespace Camera { + + /** + * @namespace all recognized exposure modes for CryoScope + */ + namespace CryoScopeExposureMode { + constexpr const char* XXX = "XXX"; + constexpr const char* ALLMODES[] = {XXX}; + } + + class CryoScope; + + /** + * @brief class constructor example for cryoscope-specific non-standard mode XXX + * @param[in] _interface Pointer to Camera InterfaceType + * @param[in] _modeargs mode arguments + */ + class ExposureModeXXX : public ExposureModeTemplate { + public: + ExposureModeXXX(Camera::ArchonInterface* iface, std::vector modeargs) + : ExposureModeTemplate(iface) { + this->type=CryoScopeExposureMode::XXX; + this->args=std::move(modeargs); + } + + long expose() override; + void image_acquisition_thread() override; + }; + +} diff --git a/camerad/Instruments/cryoscope/cryoscope_instrument.cpp b/camerad/Instruments/cryoscope/cryoscope_instrument.cpp new file mode 100644 index 0000000..6cc4a00 --- /dev/null +++ b/camerad/Instruments/cryoscope/cryoscope_instrument.cpp @@ -0,0 +1,173 @@ +/** + * @file Instruments/cryoscope/cryoscope_instrument.cpp + * @brief implementation for CryoScope-specific properties + * @author David Hale + * + */ + +#include "cryoscope_instrument.h" +#include "cryoscope_exposure_modes.h" + +namespace Camera { + + /***** Camera::CryoScope::instrument_cmd ************************************/ + /** + * @brief dispatcher for CryoScope-specific instrument commands + * @details This allows dispatching instrument specific commands by receiving + * the command and args and calling the appropriate instrument + * specific function. + * @param[in] cmd command + * @param[in] args any number of arguments + * @param[out] retstring return string + * @return ERROR|NO_ERROR|HELP + * + */ + long CryoScope::instrument_cmd(const std::string &cmd, + const std::string &args, + std::string &retstring) { + if (false) { + } + else { + retstring = "unrecognized command"; + return ERROR; + } + } + /***** Camera::CryoScope::instrument_cmd ************************************/ + + + /***** Camera::CryoScope::configure_instrument ******************************/ + /** + * @brief extract+apply instrument-specific parameters from config file + * @throws std::runtime_error + * + */ + void CryoScope::configure_instrument() { + const std::string function("Camera::CryoScope::configure_instrument"); + int numapplied=0, lastapplied=0; + + if (this->configfile.n_rows < 1) throw std::runtime_error("empty configuration"); + + // iterate through each row in config file + for (int row=0; row < this->configfile.n_rows; row++) { + + lastapplied=numapplied; + + // START_PARAM + if (this->configfile.param[row]=="START_PARAM") { + this->start_param = this->configfile.arg[row]; + numapplied++; + } + + if (numapplied > lastapplied) { + std::ostringstream oss; + oss << "config:" << this->configfile.param[row] << "=" << this->configfile.arg[row]; + logwrite(function, oss.str()); + } + } + } + /***** Camera::CryoScope::configure_instrument ******************************/ + + + /***** Camera::CryoScope::power *********************************************/ + /** + * @brief set/get power and set Start=1 when powered on + * @details This overrides ArchonInterface::power. It calls the standard function, + * but if powered on then it also sets the Start parameter = 1 + * @param[in] args requested state or help, on|off|? + * @param[out] retstring contains power_status string on success + * @return ERROR | NO_ERROR | HELP + * + */ + long CryoScope::power(const std::string args, std::string &retstring) { + + // first call standard ArchonInterface power + long error = this->ArchonInterface::power(args, retstring); + + // if power is turned on set Start=1 and setup detector + if (error==NO_ERROR && !args.empty() && this->controller->power_status=="ON") { + error = this->controller->set_parameter(this->start_param, 1); + if (error==NO_ERROR) error = this->setup_detector(); + } + else + // if power is turned off set Start=0 + if (error==NO_ERROR && !args.empty() && this->controller->power_status=="OFF") { + error = this->controller->set_parameter(this->start_param, 0); + } + + return error; + } + /***** Camera::CryoScope::power *********************************************/ + + + /***** Camera::CryoScope::get_exposure_modes ********************************/ + /** + * @brief return a vector of strings of recognized exposure modes + * @details This adds CryoScope exposure modes to the base exposure modes. + * @return vector + * + */ + std::vector CryoScope::get_exposure_modes() { + // base exposure modes + auto modes = this->ArchonInterface::get_exposure_modes(); + + // add cryoscope exposure modes + for (const auto &mode : Camera::CryoScopeExposureMode::ALLMODES) { modes.push_back(mode); } + + return modes; + } + /***** Camera::CryoScope::get_exposure_modes ********************************/ + + + /***** Camera::CryoScope::set_exposure_mode *********************************/ + /** + * @brief actually sets the exposure mode + * @details This creates the appropriate exposure mode object for the + * requested exposure mode, providing access to that mode's functions. + * This is cryoscope-specific but gets called by ArchonInterface because + * it overrides. If the requested mode is not a cryoscope mode then + * this will call the set_exposure_mode in the base class. + * @param[in] modein desired exposure mode + * @param[in] modeargs optional arguments for the mode + * @return ERROR|NO_ERROR + * + */ + long CryoScope::set_exposure_mode(const std::string &modein, const std::vector &modeargs) { + + // check for specialized cryoscope-specific expose modes + if (caseCompareString(modein, CryoScopeExposureMode::XXX)) { + this->exposuremode = std::make_unique(this, modeargs); + } + // otherwise it's a standard ArchonInterface exposure mode + else { + return this->ArchonInterface::set_exposure_mode(modein, modeargs); + } + + return NO_ERROR; + } + /***** Camera::CryoScope::set_exposure_mode *********************************/ + + + /***** Camera::CryoScope::setup_detector ************************************/ + /** + * @brief setup detector + * @details This is a hard-coded placeholder for a better version but + * here just so the detector works. + * @return ERROR|NO_ERROR + * + */ + long CryoScope::setup_detector() { + long error=NO_ERROR; + std::vector commands = { "10 1 16402", + "10 0 1", + "10 0 0" }; + for (const auto &cmd : commands) { + error = this->controller->set_vcpu_inreg(cmd); + if (error!=NO_ERROR) break; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + return error; + } + /***** Camera::CryoScope::setup_detector ************************************/ + +} diff --git a/camerad/Instruments/cryoscope/cryoscope_instrument.h b/camerad/Instruments/cryoscope/cryoscope_instrument.h new file mode 100644 index 0000000..4b30906 --- /dev/null +++ b/camerad/Instruments/cryoscope/cryoscope_instrument.h @@ -0,0 +1,39 @@ +/** + * @file Instruments/cryoscope/cryoscope_instrument.h + * @brief contains properties unique to the CryoScope instrument + * @author David Hale + * + */ +#pragma once + +#include "archon_interface.h" /// CryoScope uses ArchonInteface + +namespace Camera { + + /***** Camera::CryoScope ****************************************************/ + /** + * @class CryoScope + * @brief derived class inherits from ArchonInterface + * @details This class describes CryoScope-specific functionality. + * + */ + class CryoScope : public ArchonInterface { + public: + // instrument command dispatcher + long instrument_cmd(const std::string &cmd, + const std::string &args, + std::string &retstring) override; + + void configure_instrument() override; + long power(std::string args, std::string &retstring) override; + + std::vector get_exposure_modes() override; + long set_exposure_mode(const std::string &modein, const std::vector &args) override; + + private: + // these are CryoScope-specific functions + std::string start_param; + long setup_detector(); + }; + /***** Camera::CryoScope ****************************************************/ +} diff --git a/camerad/Instruments/cryoscope/cryoscope_interface_factory.cpp b/camerad/Instruments/cryoscope/cryoscope_interface_factory.cpp new file mode 100644 index 0000000..132756c --- /dev/null +++ b/camerad/Instruments/cryoscope/cryoscope_interface_factory.cpp @@ -0,0 +1,23 @@ +/** + * @file Instruments/cryoscope/cryoscope_interface_factory.cpp + * @brief CryoScope Interface Factory + * @author David Hale + * + */ +#include "cryoscope_instrument.h" +#include "camera_interface.h" + +namespace Camera { + + /***** Camera::Interface::create ********************************************/ + /** + * @brief factory function to create pointer to CryoScope + * @return unique_ptr + * + */ + std::unique_ptr Interface::create() { + return std::make_unique(); + } + /***** Camera::Interface::create ********************************************/ + +} diff --git a/camerad/archon_exposure_modes.cpp b/camerad/archon_exposure_modes.cpp index 892dc82..0470619 100644 --- a/camerad/archon_exposure_modes.cpp +++ b/camerad/archon_exposure_modes.cpp @@ -175,64 +175,4 @@ namespace Camera { } /***** Camera::ExposureModeRaw::expose *************************************/ - - /***** Camera::ExposureModeRXRV ********************************************/ - /** - * @brief implementation of Archon-specific expose for RXR-Video - * - */ - long ExposureModeRXRV::expose() { - const std::string function("Camera::ExposureModeRXRV::expose"); - - size_t sz=100; - - // Two each of signal and reset buffers, current and previous, since - // we need to pair the reset from the previous frame with signal from - // the current frame. These will hold deinterlaced frames. - // - std::vector> sigbuf(2, std::vector(sz)); - std::vector> resbuf(2, std::vector(sz)); - - // allocate memory for frame buffer read from Archon - interface->allocate_framebuf(sz); - - // create an appropriate deinterlacer object - try { processor = make_image_processor("rxrv"); - } - catch(const std::exception &e) { - logwrite(function, "ERROR: "+std::string(e.what())); - return ERROR; - } - - // read first frame pair into my frame buffer - char* buffer=new char[1024]{}; // TODO temporary, for compilation only - this->interface->controller->read_frame(ArchonController::FRAME_IMAGE, buffer); - - // process (deinterlace) first frame pair - processor->deinterlacer()->deinterlace(interface->get_framebuf(), sigbuf[0].data(), resbuf[0].data()); - - // sample calls to other processor functions - uint16_t a, b; - int16_t c; - processor->subtractor()->subtract(&a, &b, &c); - processor->coadder()->coadd(&a, &b); - - // show contents - std::stringstream message; - message << "sig:"; - for (int i=0; i<10; i++) message << " " << sigbuf[0][i]; - logwrite(function, message.str()); - message.str(""); message << "res:"; - for (int i=0; i<10; i++) message << " " << resbuf[0][i]; - logwrite(function, message.str()); - - // loop: - // subsequent frame pairs, read, deinterlace, write - - delete [] buffer; - - return NO_ERROR; - } - /***** Camera::ExposureModeRXRV ********************************************/ - } diff --git a/camerad/archon_exposure_modes.h b/camerad/archon_exposure_modes.h index 54430cc..5051372 100644 --- a/camerad/archon_exposure_modes.h +++ b/camerad/archon_exposure_modes.h @@ -20,8 +20,8 @@ namespace Camera { namespace ArchonExposureMode { constexpr const char* RAW = "RAW"; constexpr const char* SINGLE = "SINGLE"; - constexpr const char* RXRV = "RXRV"; - constexpr const char* ALLMODES[] = {RAW, SINGLE, RXRV}; + constexpr const char* VIDEORXR = "VIDEORXR"; + constexpr const char* ALLMODES[] = {RAW, SINGLE, VIDEORXR}; }; /** @struct ArchonImageBuffer @@ -39,19 +39,25 @@ namespace Camera { class ArchonInterface; // forward declaration + /***** Camera::ExposureModeRaw **********************************************/ /** * @brief class constructor * @param[in] _interface Pointer to Camera InterfaceType */ - class ExposureModeRaw : public ArchonImageBuffer, public ExposureModeTemplate { + class ExposureModeRaw : public ArchonImageBuffer, + public ExposureModeTemplate { public: ExposureModeRaw(Camera::ArchonInterface* iface) - : ExposureModeTemplate(iface) { + : ExposureModeTemplate(iface) { this->type=ArchonExposureMode::RAW; } long expose() override; }; + /***** Camera::ExposureModeRaw **********************************************/ + /***** Camera::ExposureModeSingle *******************************************/ /** @@ -59,10 +65,13 @@ namespace Camera { * @brief derived class for Exposure Mode Single * */ - class ExposureModeSingle : public ArchonImageBuffer, public ExposureModeTemplate { + class ExposureModeSingle : public ArchonImageBuffer, + public ExposureModeTemplate { public: ExposureModeSingle(Camera::ArchonInterface* iface) - : ExposureModeTemplate(iface) { + : ExposureModeTemplate(iface) { type=ArchonExposureMode::SINGLE; } @@ -79,13 +88,28 @@ namespace Camera { /***** Camera::ExposureModeSingle *******************************************/ - class ExposureModeRXRV : public ArchonImageBuffer, public ExposureModeTemplate { + /***** Camera::ExposureMode_VIDEORXR ****************************************/ + class ExposureMode_VIDEORXR : public ArchonImageBuffer, + public ExposureModeTemplate { public: - ExposureModeRXRV(Camera::ArchonInterface* iface) - : ExposureModeTemplate(iface) { - type=ArchonExposureMode::RXRV; + ExposureMode_VIDEORXR(Camera::ArchonInterface* iface, std::vector argsin) + : ExposureModeTemplate(iface) { + type=ArchonExposureMode::VIDEORXR; + args=argsin; + processor = Camera::make_image_processor("rxrv"); } - long expose() override; + /** @var imagebuf_queue + * @brief the FIFO queue to contain images from Archon + */ + std::queue> imagebuf_queue; + + void image_acquisition_thread() override; + void image_processing_thread() override; + long expose() override; }; + /***** Camera::ExposureMode_VIDEORXR ****************************************/ + } diff --git a/camerad/archon_exposure_modes_rxrv.cpp b/camerad/archon_exposure_modes_rxrv.cpp new file mode 100644 index 0000000..075a337 --- /dev/null +++ b/camerad/archon_exposure_modes_rxrv.cpp @@ -0,0 +1,172 @@ +/** + * @file archon_exposure_mode_rxrv.cpp + * @brief implements Archon-specific exposure modes + * + */ + +#include "archon_exposure_modes.h" +#include "archon_interface.h" + +namespace Camera { + + /***** Camera::ExposureMode_VIDEORXR ***************************************/ + /** + * @brief implementation of Archon-specific expose for RXR-Video + * + */ + long ExposureMode_VIDEORXR::expose() { + const std::string function("Camera::ExposureMode_VIDEORXR::expose"); + + size_t sz=100; + + // Two each of signal and reset buffers, current and previous, since + // we need to pair the reset from the previous frame with signal from + // the current frame. These will hold deinterlaced frames. + // + std::vector> sigbuf(2, std::vector(sz)); + std::vector> resbuf(2, std::vector(sz)); + + // allocate memory for frame buffer read from Archon + interface->allocate_framebuf(sz); + + // create an appropriate deinterlacer object + try { processor = make_image_processor("rxrv"); + } + catch(const std::exception &e) { + logwrite(function, "ERROR: "+std::string(e.what())); + return ERROR; + } + + // read first frame pair into my frame buffer + char* buffer=new char[1024]{}; // TODO temporary, for compilation only + this->interface->controller->read_frame(ArchonController::FRAME_IMAGE, buffer); + + // process (deinterlace) first frame pair + processor->deinterlacer()->deinterlace(interface->get_framebuf(), sigbuf[0].data(), resbuf[0].data()); + + // sample calls to other processor functions + uint16_t a, b; + int16_t c; + processor->subtractor()->subtract(&a, &b, &c); + processor->coadder()->coadd(&a, &b); + + // show contents + std::stringstream message; + message << "sig:"; + for (int i=0; i<10; i++) message << " " << sigbuf[0][i]; + logwrite(function, message.str()); + message.str(""); message << "res:"; + for (int i=0; i<10; i++) message << " " << resbuf[0][i]; + logwrite(function, message.str()); + + // loop: + // subsequent frame pairs, read, deinterlace, write + + delete [] buffer; + + return NO_ERROR; + } + /***** Camera::ExposureMode_VIDEORXR ****************************************/ + + + /***** Camera::ExposureMode_VIDEORXR::image_acquisition_thread **************/ + /** + * @brief producer thread for ExposureMode VIDEORXR + * @details Spawned by Camera::ArchonInterface::do_expose() + * + */ + void ExposureMode_VIDEORXR::image_acquisition_thread() { + const std::string function("Camera::ExposureMode_VIDEORXR::image_acquisition_thread"); + char message[256]; + + logwrite(function, ""); + + // get value for 'Expose = ' from the class args for the exposure mode + int nexp=1; + if (this->args.size() > 0) { + try { nexp = std::stoi(args.at(0)); + } + catch (const std::exception &e) { + logwrite(function, "ERROR getting nexp from mode args: "+std::string(e.what())); + return; + } + } + + auto info = &this->interface->camera_info; + + // record system time when exposure starts (YYYY-MM-DDTHH:MM:SS.sss) + info->start_time = get_timestamp(); + + // ---------- initiate the exposure -------------------- + // + if ( this->interface->controller->initiate_exposure(nexp) != NO_ERROR ) { + logwrite(function, "could not initiate exposure"); + return; + } + logwrite(function, "exposure started"); + + long error=NO_ERROR; + + uint64_t bufferbytes = (uint64_t)info->image_data_bytes * info->cubedepth; + + while (error==NO_ERROR && !this->interface->is_aborted() && nexp > 0) { + + // prepare an ImageBuffer object for the exposure + auto imagebuffer = std::make_shared(); + try { imagebuffer->rawpixels.reset(new char[bufferbytes]); + } + catch (const std::exception &e) { + SNPRINTF(message, "memory allocation failed: %s", e.what()); + logwrite(function, "ERROR "+std::string(message)); + error=ERROR; + break; + } + + imagebuffer->n_slices = 1; // VIDEORXR uses extensions, not cubes + + imagebuffer->bufframen_slice.reserve(imagebuffer->n_slices); + imagebuffer->buftimestamp_slice.reserve(imagebuffer->n_slices); + + // wait for frame readout into Archon buffer + if ( (error=this->interface->controller->wait_for_readout()) == ERROR ) break; + + // read frame from Archon into memory pointed to by p_imagebuffer + char* base = imagebuffer->rawpixels.get(); + char* ptr = base; + error = this->interface->controller->read_frame( ArchonController::FRAME_IMAGE, ptr ); + + // frame metadata + auto index = this->interface->controller->frameinfo.index.load(); + imagebuffer->bufframen_slice.push_back( this->interface->controller->frameinfo.bufframen[index] ); + imagebuffer->buftimestamp_slice.push_back( this->interface->controller->frameinfo.buftimestamp[index] ); + + // push frame into queue + this->image_queue.enqueue( std::move(imagebuffer) ); + + nexp--; + + } // end loop over number of frames + + this->image_queue.enqueue( nullptr ); + + logwrite(function, "complete"); + } + /***** Camera::ExposureMode_VIDEORXR::image_acquisition_thread **************/ + + + /***** Camera::ExposureMode_VIDEORXR::image_processing_thread ***************/ + /** + * @brief consumer thread for ExposureMode VIDEORXR + * @details Spawned by Camera::ArchonInterface::do_expose() + * + */ + void ExposureMode_VIDEORXR::image_processing_thread() { + const std::string function("Camera::ExposureMode_VIDEORXR::image_processing_thread"); + logwrite(function, "started"); + +// auto postproc = std::make_unique>( DeInterfaceMode::VIDEORXR, +// this->interface->camera_info.naxes ); + logwrite(function, "complete"); + } + /***** Camera::ExposureMode_VIDEORXR::image_processing_thread ***************/ +} diff --git a/camerad/archon_interface.cpp b/camerad/archon_interface.cpp index 19f766d..e3e0c48 100644 --- a/camerad/archon_interface.cpp +++ b/camerad/archon_interface.cpp @@ -411,10 +411,11 @@ namespace Camera { logwrite(function, "ERROR not connected to controller"); return ERROR; } - if (!this->controller->is_powered) { - logwrite(function, "ERROR power is not on"); - return ERROR; - } +// there might be cases when this could be useful +// if (!this->controller->is_powered) { +// logwrite(function, "ERROR power is not on"); +// return ERROR; +// } if (!this->is_exposuremode_set()) { logwrite(function, "ERROR exposure mode not set!"); return ERROR; @@ -549,14 +550,16 @@ namespace Camera { this->exposuremode = std::make_shared(this); } else - if (caseCompareString(modein, ArchonExposureMode::RXRV)) { - this->exposuremode = std::make_shared(this); + if (caseCompareString(modein, ArchonExposureMode::VIDEORXR)) { + this->exposuremode = std::make_shared(this, modeargs); + this->set_camera_mode(ArchonExposureMode::VIDEORXR); } else { logwrite("Camera::ArchonInterface::set_exposure_mode", - "ERROR unrecognized exposure mode \""+modein+"\""); + "ERROR unrecognized exposure mode '"+modein+"'"); return ERROR; } + return NO_ERROR; } /***** Camera::ArchonInterface::set_exposure_mode ***************************/ diff --git a/camerad/camerad.cpp b/camerad/camerad.cpp index 02a173c..d686d6a 100644 --- a/camerad/camerad.cpp +++ b/camerad/camerad.cpp @@ -22,11 +22,11 @@ int main( int argc, char** argv ) { // immediately daemonize. // if (!hasOption(argc, argv, "--foreground")) { - logwrite(function, "starting daemon"); + std::cerr << get_timestamp() << " (" << function << ") starting daemon" << std::endl;; Daemon::daemonize( "camerad", "/tmp", "/dev/null", "/tmp/camerad.stderr", "", false ); std::cerr << get_timestamp() << " (" << function << ") daemonized. child process running" << std::endl; } - else logwrite(function, "starting"); + else std::cerr << get_timestamp() << " (" << function << ") starting" << std::endl; // the child process instantiates a Server object // @@ -37,13 +37,21 @@ int main( int argc, char** argv ) { auto filename = getOptionArg(argc, argv, "--config"); if (filename.empty()) { - logwrite(function, "ERROR --config is required"); + std::cerr << get_timestamp() << " (" << function << ") ERROR --config is required" << std::endl; exit(1); } camerad.interface->configfile.filename = filename; try { camerad.interface->configfile.read_config(); + // initialize the logging system + if ( ( init_log("camerad", + camerad.interface->configfile.required("LOGPATH"), + camerad.interface->configfile.optional("STDERR"), + camerad.interface->configfile.required("TM_ZONE_LOG")) != 0 ) ) { + std::cerr << get_timestamp() << " (" << function << ") ERROR unable to initialize logging system" << std::endl; + exit(1); + } camerad.configure_server(); camerad.interface->configure_controller(); camerad.interface->configure_interface(); diff --git a/camerad/exposure_modes.h b/camerad/exposure_modes.h index 26ba477..1c8ff70 100644 --- a/camerad/exposure_modes.h +++ b/camerad/exposure_modes.h @@ -12,6 +12,7 @@ #include "common.h" #include "camera_information.h" #include "image_process.h" +#include "image_queue.h" namespace Camera { @@ -68,11 +69,13 @@ namespace Camera { * Camera Interface. * */ - template + template class ExposureModeTemplate : public ExposureMode { protected: InterfaceType* interface; //!< pointer to the specific Camera Interface instance + ImageQueue image_queue; + // Pointer to the deinterlacer for this mode. This is a pointer // to the base class -- each exposure mode will have to initialize // this to an appropriate deinterlacer using a factory function. diff --git a/camerad/image_process.cpp b/camerad/image_process.cpp index 4b8b778..c5c544b 100644 --- a/camerad/image_process.cpp +++ b/camerad/image_process.cpp @@ -24,17 +24,17 @@ namespace Camera { /***** Camera::DeInterlace_None *********************************************/ - /***** Camera::DeInterlace_RXRV *********************************************/ + /***** Camera::DeInterlace_VIDEORXR *****************************************/ /** - * @brief specialization for deinterlacing RXRV + * @brief specialization for deinterlacing VIDEORXR * @param[in] imgbuf pointer to input buffer * @param[out] sigbuf pointer to deinterlaced signal frame from imgbuf * @param[out] resbuf pointer to deinterlaced reset frame from imgbuf */ - class DeInterlace_RXRV : public DeInterlacer { + class DeInterlace_VIDEORXR : public DeInterlacer { public: void deinterlace(char* imgbuf, uint16_t* sigbuf, uint16_t* resbuf) { - const std::string function("Camera::DeInterlace_RXRV::deinterlace"); + const std::string function("Camera::DeInterlace_VIDEORXR::deinterlace"); logwrite(function, "here"); std::stringstream message; message << "contents:"; @@ -47,7 +47,7 @@ namespace Camera { logwrite(function, message.str()); } }; - /***** Camera::DeInterlace_RXRV *********************************************/ + /***** Camera::DeInterlace_VIDEORXR *****************************************/ class SubtractSimple : public Subtractor { @@ -96,7 +96,7 @@ namespace Camera { else if (mode=="rxrv") { return std::make_unique( - std::make_unique(), + std::make_unique(), std::make_unique(), std::make_unique() ); diff --git a/camerad/image_queue.h b/camerad/image_queue.h new file mode 100644 index 0000000..8faa466 --- /dev/null +++ b/camerad/image_queue.h @@ -0,0 +1,58 @@ +/** + * @file image_queue.h + * @brief implements a thread-safe blocking queue + * @author David Hale + * + */ + +#pragma once + +namespace Camera { + + /***** Camera::ImageQueue ***************************************************/ + /** + * @brief template-based, thread-safe FIFO queue + * @details This wraps the queue push and pop with mutexes and condition + * variables. + * + */ + template + class ImageQueue { + + private: + std::queue> q; + std::mutex m; + std::condition_variable cv; + + public: + /***** Camera::ImageQueue::enqueue ******************************************/ + /** + * @brief adds an image to the end of the queue + * @details Ownership of the image is transferred to the queue. + * @param[in] image shared_ptr to the typed image buffer + * + */ + void enqueue(std::shared_ptr image) { + { + std::lock_guard lock(m); + q.push(std::move(image)); + } + cv.notify_one(); + } + + /***** Camera::ImageQueue::dequeue ******************************************/ + /** + * @brief removes and returns next image from the queue + * @details This blocks while the queue is empty. + */ + std::shared_ptr dequeue() { + std::unique_lock lock(m); + cv.wait(lock, [&]{ return !q.empty(); }); + + auto image = std::move( q.front() ); + q.pop(); + return image; + } + }; + /***** Camera::ImageQueue ***************************************************/ +} diff --git a/utils/config.h b/utils/config.h index f1399e4..6686a2b 100644 --- a/utils/config.h +++ b/utils/config.h @@ -10,6 +10,7 @@ #include #include +#include #include "utilities.h" // for rtrim /***** Config ***************************************************************/ @@ -30,6 +31,39 @@ class Config { int n_rows; std::vector param; std::vector arg; + std::map configmap; + + + /***** Config::get ******************************************************/ + /** + * @brief return configuration value for provided key + * @details When is_required is true then this will throw an + * exception if the key is not found. When false, silently + * return an empty string. + * @param[in] key configuration key + * @param[in] is_required true|false must I return a value? + * @return value configuration value + * @throws runtime_error + * + */ + std::string get(const std::string &key, bool is_required=false) { + if (configmap.find(key) != configmap.end()) { + return configmap.at(key); + } + if (is_required) { + throw std::runtime_error("'"+key+"' not in configuration"); + } + else return ""; + } + /***** Config::get ******************************************************/ + + /** @brief calls Config::get() with is_requred=true + */ + std::string required(const std::string &key) { return get(key, true); } + + /** @brief calls Config::get() with is_requred=false + */ + std::string optional(const std::string &key) { return get(key, false); } /***** Config::read_config **********************************************/ @@ -66,6 +100,7 @@ class Config { // this->param.clear(); this->arg.clear(); + this->configmap.clear(); // Read the config file // @@ -74,6 +109,8 @@ class Config { while (getline(filestream, line)) { // Get a line from the file as long as they are available + std::string key="", value=""; + if (line.length() > 2) { // valid line is at least 3 characters, ie. X=Y @@ -88,6 +125,8 @@ class Config { rtrim(line); // remove trailing whitespace index1 = line.find_first_of("="); // Find the = delimiter in the line this->param.push_back(line.substr(0, index1)); + key = line.substr(0, index1); + // Put the variable name into the vector holding the names // Look for configuration parameters in a vector format (i.e. surrounded by parentheses). @@ -104,6 +143,7 @@ class Config { // index2 = line.find_first_of("\t\0"); this->arg.push_back(line.substr(index1 + 1, index2 - index1)); + value = line.substr(index1 + 1, index2 - index1); } // There is a comment, get the index and put the value (note the @@ -112,6 +152,7 @@ class Config { else { index2 = line.find_first_of(" \t#"); this->arg.push_back(line.substr(index1 + 1, index2 - index1 - 1)); + value = line.substr(index1 + 1, index2 - index1 - 1); } } @@ -122,6 +163,7 @@ class Config { index1 = line.find_first_of("\"") + 1; index2 = line.find_last_of("\""); this->arg.push_back(line.substr(index1, index2 - index1)); + value = line.substr(index1, index2 - index1); } } @@ -131,8 +173,12 @@ class Config { index1 = line.find_first_of("(") + 1; index2 = line.find_last_of(")"); this->arg.push_back(line.substr(index1, index2 - index1)); + value = line.substr(index1, index2 - index1); } } + + configmap[key] = value; + } else continue; // For lines of less than 2 characters, we just loop to the next line linesread++; // Increment the number of values read successfully From d6d15a4b7211c320b95e20970c684e2d5c641dec Mon Sep 17 00:00:00 2001 From: David Hale Date: Fri, 3 Apr 2026 13:30:15 -0700 Subject: [PATCH 2/2] adds to consumer (image_processing_thread) adds deinterlacing adds placeholders for subtraction and fits writing changes logwrite to accept std::string_view --- .../cryoscope/cryoscope_exposure_modes.cpp | 2 +- .../cryoscope/cryoscope_exposure_modes.h | 4 +- camerad/archon_controller.cpp | 2 - camerad/archon_exposure_modes.cpp | 6 +- camerad/archon_exposure_modes.h | 30 ++++-- camerad/archon_exposure_modes_rxrv.cpp | 93 ++++++++++++++++--- camerad/exposure_modes.h | 10 +- camerad/image_process.cpp | 42 ++++++--- camerad/image_process.h | 22 ++--- utils/logentry.cpp | 2 +- utils/logentry.h | 2 +- 11 files changed, 156 insertions(+), 59 deletions(-) diff --git a/camerad/Instruments/cryoscope/cryoscope_exposure_modes.cpp b/camerad/Instruments/cryoscope/cryoscope_exposure_modes.cpp index 6b9df4b..907efed 100644 --- a/camerad/Instruments/cryoscope/cryoscope_exposure_modes.cpp +++ b/camerad/Instruments/cryoscope/cryoscope_exposure_modes.cpp @@ -35,7 +35,7 @@ namespace Camera { logwrite(function, "mode args:"); - for (const auto &arg : this->args) { + for (const auto &arg : this->modeargs) { logwrite(function, arg); } } diff --git a/camerad/Instruments/cryoscope/cryoscope_exposure_modes.h b/camerad/Instruments/cryoscope/cryoscope_exposure_modes.h index c70c14c..c6e6765 100644 --- a/camerad/Instruments/cryoscope/cryoscope_exposure_modes.h +++ b/camerad/Instruments/cryoscope/cryoscope_exposure_modes.h @@ -34,8 +34,8 @@ namespace Camera { ExposureModeXXX(Camera::ArchonInterface* iface, std::vector modeargs) : ExposureModeTemplate(iface) { - this->type=CryoScopeExposureMode::XXX; - this->args=std::move(modeargs); + this->modetype = CryoScopeExposureMode::XXX; + this->modeargs = std::move(modeargs); } long expose() override; diff --git a/camerad/archon_controller.cpp b/camerad/archon_controller.cpp index a4af035..7e69d3f 100644 --- a/camerad/archon_controller.cpp +++ b/camerad/archon_controller.cpp @@ -1855,8 +1855,6 @@ namespace Camera { int num_detect = this->modemap[this->selectedmode].geometry.num_detect; auto index = this->frameinfo.index.load(); - logwrite(function, ""); - this->frametype = type; // Archon buffer number of the last frame read into memory diff --git a/camerad/archon_exposure_modes.cpp b/camerad/archon_exposure_modes.cpp index 0470619..3a769f9 100644 --- a/camerad/archon_exposure_modes.cpp +++ b/camerad/archon_exposure_modes.cpp @@ -15,7 +15,7 @@ namespace Camera { * */ long ExposureModeSingle::expose() { - const std::string function("Camera::ExposureModeSingle::expose"); + std::string_view function("Camera::ExposureModeSingle::expose"); logwrite(function, "hi"); return NO_ERROR; } @@ -29,7 +29,7 @@ namespace Camera { * */ void ExposureModeSingle::image_acquisition_thread() { - const std::string function("Camera::ExposureModeSingle::image_acquisition_thread"); + std::string_view function("Camera::ExposureModeSingle::image_acquisition_thread"); char message[256]; logwrite(function, ""); @@ -115,7 +115,7 @@ namespace Camera { * */ void ExposureModeSingle::image_processing_thread() { - const std::string function("Camera::ExposureModeSingle::image_processing_thread"); + std::string_view function("Camera::ExposureModeSingle::image_processing_thread"); logwrite(function, "enter"); // open FITS file ? diff --git a/camerad/archon_exposure_modes.h b/camerad/archon_exposure_modes.h index 5051372..4a851e2 100644 --- a/camerad/archon_exposure_modes.h +++ b/camerad/archon_exposure_modes.h @@ -37,6 +37,16 @@ namespace Camera { std::vector buftimestamp_slice; ///< Archon timestamp(s) for all slices in this image }; + struct PairIndex { + int curr; + int prev; + }; + + inline PairIndex get_indices(int count) { + int c = count & 1; + return {c, 1 - c}; + } + class ArchonInterface; // forward declaration /***** Camera::ExposureModeRaw **********************************************/ @@ -51,7 +61,7 @@ namespace Camera { ExposureModeRaw(Camera::ArchonInterface* iface) : ExposureModeTemplate(iface) { - this->type=ArchonExposureMode::RAW; + this->modetype=ArchonExposureMode::RAW; } long expose() override; @@ -61,7 +71,6 @@ namespace Camera { /***** Camera::ExposureModeSingle *******************************************/ /** - * @class Camera::ExposureModeSingle * @brief derived class for Exposure Mode Single * */ @@ -72,7 +81,7 @@ namespace Camera { ExposureModeSingle(Camera::ArchonInterface* iface) : ExposureModeTemplate(iface) { - type=ArchonExposureMode::SINGLE; + this->modetype=ArchonExposureMode::SINGLE; } /** @var imagebuf_queue @@ -89,6 +98,10 @@ namespace Camera { /***** Camera::ExposureMode_VIDEORXR ****************************************/ + /** + * @brief derived class for Exposure Mode VIDEORXR + * + */ class ExposureMode_VIDEORXR : public ArchonImageBuffer, public ExposureModeTemplate { @@ -96,19 +109,18 @@ namespace Camera { ExposureMode_VIDEORXR(Camera::ArchonInterface* iface, std::vector argsin) : ExposureModeTemplate(iface) { - type=ArchonExposureMode::VIDEORXR; - args=argsin; - processor = Camera::make_image_processor("rxrv"); + this->modetype = ArchonExposureMode::VIDEORXR; + this->modeargs = argsin; + this->processor = Camera::make_image_processor("rxrv"); // TODO don't use this string } - /** @var imagebuf_queue - * @brief the FIFO queue to contain images from Archon + /** @brief FIFO queue to contain images from Archon */ std::queue> imagebuf_queue; void image_acquisition_thread() override; void image_processing_thread() override; - long expose() override; + long expose() override; // TODO obsolete? }; /***** Camera::ExposureMode_VIDEORXR ****************************************/ diff --git a/camerad/archon_exposure_modes_rxrv.cpp b/camerad/archon_exposure_modes_rxrv.cpp index 075a337..c0815e9 100644 --- a/camerad/archon_exposure_modes_rxrv.cpp +++ b/camerad/archon_exposure_modes_rxrv.cpp @@ -15,7 +15,7 @@ namespace Camera { * */ long ExposureMode_VIDEORXR::expose() { - const std::string function("Camera::ExposureMode_VIDEORXR::expose"); + std::string_view function("Camera::ExposureMode_VIDEORXR::expose"); size_t sz=100; @@ -42,7 +42,7 @@ namespace Camera { this->interface->controller->read_frame(ArchonController::FRAME_IMAGE, buffer); // process (deinterlace) first frame pair - processor->deinterlacer()->deinterlace(interface->get_framebuf(), sigbuf[0].data(), resbuf[0].data()); +// processor->deinterlacer()->deinterlace(interface->get_framebuf(), sigbuf[0].data(), resbuf[0].data()); // sample calls to other processor functions uint16_t a, b; @@ -76,15 +76,16 @@ namespace Camera { * */ void ExposureMode_VIDEORXR::image_acquisition_thread() { - const std::string function("Camera::ExposureMode_VIDEORXR::image_acquisition_thread"); + std::string_view function("Camera::ExposureMode_VIDEORXR::image_acquisition_thread"); char message[256]; - logwrite(function, ""); + logwrite(function, "started"); // get value for 'Expose = ' from the class args for the exposure mode + // default = 1 int nexp=1; - if (this->args.size() > 0) { - try { nexp = std::stoi(args.at(0)); + if (this->modeargs.size() > 0) { + try { nexp = std::stoi(modeargs.at(0)); } catch (const std::exception &e) { logwrite(function, "ERROR getting nexp from mode args: "+std::string(e.what())); @@ -103,7 +104,7 @@ namespace Camera { logwrite(function, "could not initiate exposure"); return; } - logwrite(function, "exposure started"); + logwrite(function, "exposure initiated with "+std::to_string(nexp)+" frames"); long error=NO_ERROR; @@ -130,7 +131,8 @@ namespace Camera { // wait for frame readout into Archon buffer if ( (error=this->interface->controller->wait_for_readout()) == ERROR ) break; - // read frame from Archon into memory pointed to by p_imagebuffer + // ---------- read frame from Archon ----------------- + // char* base = imagebuffer->rawpixels.get(); char* ptr = base; error = this->interface->controller->read_frame( ArchonController::FRAME_IMAGE, ptr ); @@ -140,13 +142,17 @@ namespace Camera { imagebuffer->bufframen_slice.push_back( this->interface->controller->frameinfo.bufframen[index] ); imagebuffer->buftimestamp_slice.push_back( this->interface->controller->frameinfo.buftimestamp[index] ); - // push frame into queue + // ---------- push frame into queue ------------------ + // this->image_queue.enqueue( std::move(imagebuffer) ); + logwrite("Camera::ExposureMode_VIDEORXR::image_acquisition_thread", "pushed frame"); nexp--; } // end loop over number of frames + // ---------- end-of-queue marker ---------------------- + // this->image_queue.enqueue( nullptr ); logwrite(function, "complete"); @@ -161,11 +167,74 @@ namespace Camera { * */ void ExposureMode_VIDEORXR::image_processing_thread() { - const std::string function("Camera::ExposureMode_VIDEORXR::image_processing_thread"); + std::string_view function("Camera::ExposureMode_VIDEORXR::image_processing_thread"); logwrite(function, "started"); -// auto postproc = std::make_unique>( DeInterfaceMode::VIDEORXR, -// this->interface->camera_info.naxes ); +// output->open(); PLACEHOLDER + + const long bufcols = this->interface->camera_info.naxes[0] / 2; + const long bufrows = this->interface->camera_info.naxes[1]; + const long imgcols = bufcols - 64; + const long imgrows = bufrows; + + // two sig frames, curr and prev + std::vector sig[2] = { std::vector(bufcols*bufrows), + std::vector(bufcols*bufrows) }; + + // two res frames, curr and prev + std::vector res[2] = { std::vector(bufcols*bufrows), + std::vector(bufcols*bufrows) }; + + // one cds frame, sig[curr] - res[prev] + std::vector cds(imgcols*imgrows); + + int count=0; + + // ---------- pop image out of queue ------------------- + // + while (!this->interface->is_aborted()) { + + auto image = this->image_queue.dequeue(); // what comes out of queue is curr frame + + if (!image) break; // end of queue is marked with a nullptr + + uint32_t* raw = reinterpret_cast(image->rawpixels.get()); + + auto [curr, prev] = get_indices(count); // indices of curr and prev frames based on count + + // ---------- deinterlace current image -------------- + // + processor->deinterlacer()->deinterlace( raw, + sig[curr].data(), + res[curr].data(), + bufcols, + bufrows ); + +// // ---------- write unprocessed frames --------------- +// // +// if (unp) { PLACEHOLDER +// output->write( sig[curr].data(), info_unp ); +// output->write( res[curr].data(), info_unp ); +// } +// +// // ---------- write processed (CDS) frame ------------ +// // +// if (count > 0 && processor->has_subtractor()) { PLACEHOLDER +// +// processor->subtractor()->subtract( sig[curr].data(), +// res[prev].data(), +// cds.data(), +// bufcols, bufrows, +// imgcols, imgrows ); +// +// output->write( cds.data(), info_cds ); PLACEHOLDER +// } + + count++; + } + +// output->close(); PLACEHOLDER + logwrite(function, "complete"); } /***** Camera::ExposureMode_VIDEORXR::image_processing_thread ***************/ diff --git a/camerad/exposure_modes.h b/camerad/exposure_modes.h index 1c8ff70..29ec123 100644 --- a/camerad/exposure_modes.h +++ b/camerad/exposure_modes.h @@ -27,8 +27,8 @@ namespace Camera { */ class ExposureMode { protected: - std::string type; ///< what type of exposure mode is this? - std::vector args; ///< optional mode-specific args + std::string modetype; ///< what type of exposure mode is this? + std::vector modeargs; ///< optional mode-specific args public: std::mutex queue_mutex; ///< mutex protects access to the queue @@ -38,13 +38,13 @@ namespace Camera { std::atomic is_producer_error; std::atomic is_consumer_error; - std::string get_type() { return this->type; } - std::vector get_args() { return this->args; } + std::string get_type() { return this->modetype; } + std::vector get_args() { return this->modeargs; } /** brief return the args as a space-delimited string */ std::string get_args_string() { std::ostringstream oss; - for (const auto &arg : this->args) { if (!arg.empty()) { oss << " " << arg; } } + for (const auto &arg : this->modeargs) { if (!arg.empty()) { oss << " " << arg; } } return oss.str(); } diff --git a/camerad/image_process.cpp b/camerad/image_process.cpp index c5c544b..5210863 100644 --- a/camerad/image_process.cpp +++ b/camerad/image_process.cpp @@ -16,7 +16,11 @@ namespace Camera { */ class DeInterlace_None : public DeInterlacer { public: - void deinterlace(char* bufin, uint16_t* bufout) { + void deinterlace(uint32_t* in, + uint32_t* sig, + uint32_t* res, + long cols, + long rows) { const std::string function("Camera::DeInterlace_None::deinterlace"); logwrite(function, "here"); } @@ -33,18 +37,32 @@ namespace Camera { */ class DeInterlace_VIDEORXR : public DeInterlacer { public: - void deinterlace(char* imgbuf, uint16_t* sigbuf, uint16_t* resbuf) { - const std::string function("Camera::DeInterlace_VIDEORXR::deinterlace"); - logwrite(function, "here"); - std::stringstream message; - message << "contents:"; - for (int i=0; i<10; i++) { - message << " " << i; - // modify contents of output buffers - sigbuf[i]=i; - resbuf[i]=100-i; + void deinterlace(uint32_t* in, + uint32_t* sig, + uint32_t* res, + long cols, + long rows) { + + const long bufw = cols * 2; // interleaved reset/read + + for (long r = 0; r < rows; ++r) { + uint32_t* row_in = in + r * bufw; + uint32_t* row_sig = sig + r * cols; + uint32_t* row_res = res + r * cols; + + for (long c = 0; c < cols; c += 64) { + + // read (sig) frame + std::memcpy(row_sig + c, + row_in + (c*2), + 64 * sizeof(uint32_t)); + + // reset (res) frame + std::memcpy(row_res + c, + row_in + (c*2)+64, + 64 * sizeof(uint32_t)); + } } - logwrite(function, message.str()); } }; /***** Camera::DeInterlace_VIDEORXR *****************************************/ diff --git a/camerad/image_process.h b/camerad/image_process.h index db08fe5..93289ae 100644 --- a/camerad/image_process.h +++ b/camerad/image_process.h @@ -16,15 +16,11 @@ namespace Camera { class DeInterlacer { public: virtual ~DeInterlacer() = default; - virtual void deinterlace(char* in, char* out) { - throw std::runtime_error("deinterlace(char*, char*) not supported"); - } - virtual void deinterlace(char* in, uint16_t* out) { - throw std::runtime_error("deinterlace(char*, uint16_t*) not supported"); - } - virtual void deinterlace(char* in, uint16_t* out1, uint16_t* out2) { - throw std::runtime_error("deinterlace(char*, uint16_t*, uint16_t*) not supported"); - } + virtual void deinterlace(uint32_t* in, + uint32_t* sig, + uint32_t* res, + long cols, + long rows) = 0; }; class Subtractor { @@ -67,8 +63,12 @@ namespace Camera { _coadder(std::move(c)) { } DeInterlacer* deinterlacer() const { return _deinterlacer.get(); } - Subtractor* subtractor() const { return _subtractor.get(); } - Coadder* coadder() const { return _coadder.get(); } + Subtractor* subtractor() const { return _subtractor.get(); } + Coadder* coadder() const { return _coadder.get(); } + + bool has_deinterlacer() const { return (bool)_deinterlacer; } + bool has_subtractor() const { return (bool)_subtractor; } + bool has_coadder() const { return (bool)_coadder; } }; /** diff --git a/utils/logentry.cpp b/utils/logentry.cpp index 383b622..e0fb99d 100644 --- a/utils/logentry.cpp +++ b/utils/logentry.cpp @@ -198,7 +198,7 @@ void close_log() { * log filestream isn't open. * */ -void logwrite(const std::string &function, const std::string &message) { +void logwrite(std::string_view function, std::string_view message) { std::stringstream logmsg; std::string timestamp = get_timestamp(tmzone_log); // get the current time (defined in utilities.h) logmsg << timestamp << " (" << function << ") " << message << "\n"; diff --git a/utils/logentry.h b/utils/logentry.h index b4d74c6..9f84723 100644 --- a/utils/logentry.h +++ b/utils/logentry.h @@ -25,6 +25,6 @@ long init_log(std::string name, std::string logpath, std::string logstderr, std: /// initialize the logging system void close_log(); /// close the log file stream -void logwrite(const std::string &function, const std::string &message); +void logwrite(std::string_view function, std::string_view message); /// create a time-stamped log entry "message" from "function"