From 3f3afaeaee2fadb597f2ae522b96d24e4bf8e984 Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Tue, 21 Jul 2026 14:59:25 -0700 Subject: [PATCH 1/3] Wire frame outputs on Interface base class frame_outputs was never populated, so dispatch_frame was a no-op. Add Interface::configure_frame_outputs(), called from camerad startup. --- camerad/camera_interface.cpp | 16 ++++++++++++++++ camerad/camera_interface.h | 1 + camerad/camerad.cpp | 1 + 3 files changed, 18 insertions(+) diff --git a/camerad/camera_interface.cpp b/camerad/camera_interface.cpp index ddc60ac..c112583 100644 --- a/camerad/camera_interface.cpp +++ b/camerad/camera_interface.cpp @@ -1,4 +1,5 @@ #include "camera_interface.h" +#include "frame_output_factory.h" namespace Camera { @@ -8,6 +9,21 @@ namespace Camera { this->server=s; } + /***** Camera::Interface::configure_frame_outputs ***************************/ + /** + * @brief build the in-band frame output sinks (FITS, shared memory) + * @details dispatch_frame fans every retrieved frame out to these + * + */ + void Interface::configure_frame_outputs() { + const std::string function("Camera::Interface::configure_frame_outputs"); + FrameOutputsConfig outcfg; + apply_config_overrides(outcfg, this->configfile); + this->frame_outputs = make_frame_outputs(outcfg); + logwrite(function, "configured "+std::to_string(this->frame_outputs.size())+" frame output(s)"); + } + /***** Camera::Interface::configure_frame_outputs ***************************/ + void Interface::func_shared() { std::string function("Camera::Interface::func_shared"); logwrite(function, "common implementation function"); diff --git a/camerad/camera_interface.h b/camerad/camera_interface.h index 629d1aa..c46a3b8 100644 --- a/camerad/camera_interface.h +++ b/camerad/camera_interface.h @@ -75,6 +75,7 @@ namespace Camera { // void set_server(Camera::Server* s); void func_shared(); + void configure_frame_outputs(); void disconnect_controller(); bool is_exposuremode_set() { return ( this->exposuremode && !this->exposuremode->get_type().empty() ); } diff --git a/camerad/camerad.cpp b/camerad/camerad.cpp index 02a173c..49d9746 100644 --- a/camerad/camerad.cpp +++ b/camerad/camerad.cpp @@ -48,6 +48,7 @@ int main( int argc, char** argv ) { camerad.interface->configure_controller(); camerad.interface->configure_interface(); camerad.interface->configure_instrument(); + camerad.interface->configure_frame_outputs(); } catch (const std::exception &e) { logwrite(function, "ERROR configuring system: "+std::string(e.what())); From 177704be88aea981e86d8e4c169cb554e48b6288 Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Tue, 21 Jul 2026 14:59:25 -0700 Subject: [PATCH 2/3] Add Archon RAW (pre-CDS) retrieval and raw command Parse BUFnRAWOFFSET, size fetch from RAW geometry, read as 16-bit, reshape and dispatch in-band. New: raw config|set|read. Verified against emulator. --- camerad/archon_controller.cpp | 165 ++++++++++++++++++++++++++++++++-- camerad/archon_controller.h | 16 +++- camerad/archon_interface.cpp | 46 ++++++++++ camerad/archon_interface.h | 1 + camerad/camera_server.cpp | 4 + common/camerad_commands.h | 2 + 6 files changed, 224 insertions(+), 10 deletions(-) diff --git a/camerad/archon_controller.cpp b/camerad/archon_controller.cpp index bbf808d..eee3c18 100644 --- a/camerad/archon_controller.cpp +++ b/camerad/archon_controller.cpp @@ -582,8 +582,10 @@ namespace Camera { case 8: // BUFnCOMPLETE if (std::strncmp(suffix, "COMPLETE", 8)==0) this->frameinfo.bufcomplete[bufnum] = std::atoi(value_start); break; - case 9: // BUFnTIMESTAMP + case 9: // BUFnTIMESTAMP, RAWOFFSET if (std::strncmp(suffix, "TIMESTAMP", 9)==0) this->frameinfo.buftimestamp[bufnum] = std::strtoull(value_start, nullptr, 16); + else + if (std::strncmp(suffix, "RAWOFFSET", 9)==0) this->frameinfo.bufrawoffset[bufnum] = std::strtoul(value_start, nullptr, 10); break; case 11: // BUFnRETIMESTAMP, FETIMESTAMP if (std::strncmp(suffix, "RETIMESTAMP", 11)==0) this->frameinfo.bufretimestamp[bufnum] = std::strtoull(value_start, nullptr, 16); @@ -1608,8 +1610,10 @@ namespace Camera { this->get_configmap_value("FRAMEMODE", mode->geometry.framemode); this->get_configmap_value("RAWENABLE", mode->rawenable); this->get_configmap_value("RAWSEL", this->rawinfo.adchan); - this->get_configmap_value("RAWSAMPLES", this->rawinfo.rawsamples); - this->get_configmap_value("RAWENDLINE", this->rawinfo.rawlines); + this->get_configmap_value("RAWSAMPLES", this->rawinfo.samples); + this->get_configmap_value("RAWSTARTLINE", this->rawinfo.startline); + this->get_configmap_value("RAWENDLINE", this->rawinfo.endline); + this->get_configmap_value("RAWSTARTPIXEL", this->rawinfo.startpixel); // Read geometry from the mode's configmap (not the global one) since each // mode section can override LINECOUNT/PIXELCOUNT @@ -1911,11 +1915,10 @@ namespace Camera { // switch (this->frametype) { case Camera::ArchonController::FRAME_RAW: - // Archon buffer base address + // RAW data lives at bufbase + rawoffset; block count derives from the + // RAW geometry (RAWSAMPLES x rawlines x 2 bytes), never image_memory bufaddr = this->frameinfo.bufbase[index] + this->frameinfo.bufrawoffset[index]; - - // Calculate the number of blocks expected. image_memory is bytes per detector - bufblocks = (unsigned int) floor( (this->interface->camera_info.image_memory + BLOCK_LEN - 1 ) / BLOCK_LEN ); + bufblocks = (this->raw_frame_bytes() + BLOCK_LEN - 1) / BLOCK_LEN; break; case Camera::ArchonController::FRAME_IMAGE: @@ -2034,6 +2037,154 @@ namespace Camera { } + /***** Camera::ArchonController::is_raw_config_key *************************/ + /** + * @brief return true if key is one of the settable RAW config keywords + */ + bool ArchonController::is_raw_config_key(const std::string &key) { + for (const auto* raw_key : {"RAWENABLE", "RAWSEL", "RAWSTARTLINE", + "RAWENDLINE", "RAWSTARTPIXEL", "RAWSAMPLES"}) { + if (key == raw_key) return true; + } + return false; + } + /***** Camera::ArchonController::is_raw_config_key *************************/ + + + /***** Camera::ArchonController::raw_frame_bytes **************************/ + /** + * @brief size-aware byte count for one RAW capture + * @details RAW samples are always 16-bit. Count = rawlines x RAWSAMPLES, + * rawlines = RAWENDLINE - RAWSTARTLINE. RAWSTARTPIXEL only sets + * the reshape origin, so it does not change the sample count. + */ + uint32_t ArchonController::raw_frame_bytes() const { + const int lines = this->rawinfo.endline - this->rawinfo.startline; + const uint32_t rawlines = lines > 0 ? static_cast(lines) : 1u; + return rawlines * static_cast(this->rawinfo.samples) * sizeof(uint16_t); + } + /***** Camera::ArchonController::raw_frame_bytes **************************/ + + + /***** Camera::ArchonController::get_raw_config **************************/ + /** + * @brief report the current RAW configuration keywords + * @param[out] retstring space-delimited KEY=VALUE pairs + */ + long ArchonController::get_raw_config(std::string &retstring) { + std::ostringstream oss; + for (const auto* key : {"RAWENABLE", "RAWSEL", "RAWSTARTLINE", + "RAWENDLINE", "RAWSTARTPIXEL", "RAWSAMPLES"}) { + auto it = this->configmap.find(key); + oss << key << "=" << (it != this->configmap.end() ? it->second.value : "?") << " "; + } + retstring = oss.str(); + return NO_ERROR; + } + /***** Camera::ArchonController::get_raw_config **************************/ + + + /***** Camera::ArchonController::set_raw_config **************************/ + /** + * @brief set one or more RAW config keywords then APPLYALL + * @param[in] args "KEY VALUE [KEY VALUE ...]" + * @param[out] retstring resulting RAW configuration + */ + long ArchonController::set_raw_config(const std::string &args, std::string &retstring) { + const std::string function("Camera::ArchonController::set_raw_config"); + + std::vector tokens; + Tokenize(args, tokens, " "); + if (tokens.empty() || tokens.size() % 2 != 0) { + logwrite(function, "ERROR expected KEY VALUE pairs"); + retstring = "expected KEY VALUE pairs"; + return ERROR; + } + + bool changed = false; + for (size_t i = 0; i < tokens.size(); i += 2) { + std::string key = tokens[i]; + std::transform(key.begin(), key.end(), key.begin(), ::toupper); + if (!is_raw_config_key(key)) { + logwrite(function, "ERROR unknown RAW key: "+key); + retstring = "unknown RAW key: "+key; + return ERROR; + } + if (this->write_config_key(key.c_str(), tokens[i+1].c_str(), changed) != NO_ERROR) { + retstring = "failed writing "+key; + return ERROR; + } + } + + if (changed && this->send_cmd(APPLYALL) != NO_ERROR) { + logwrite(function, "ERROR applying RAW configuration"); + retstring = "failed to apply"; + return ERROR; + } + + // refresh cached geometry from the now-updated configmap + this->get_configmap_value("RAWSEL", this->rawinfo.adchan); + this->get_configmap_value("RAWSAMPLES", this->rawinfo.samples); + this->get_configmap_value("RAWSTARTLINE", this->rawinfo.startline); + this->get_configmap_value("RAWENDLINE", this->rawinfo.endline); + this->get_configmap_value("RAWSTARTPIXEL", this->rawinfo.startpixel); + + return this->get_raw_config(retstring); + } + /***** Camera::ArchonController::set_raw_config **************************/ + + + /***** Camera::ArchonController::read_raw *******************************/ + /** + * @brief retrieve RAW (pre-CDS) data from the newest frame buffer + * @details Reads the size-aware RAW region as 16-bit unsigned samples and + * dispatches it in-band as a (RAWSAMPLES x rawlines) frame, + * independent of the post-CDS pixel mode. + * @param[out] retstring "samples= lines= bytes=" + */ + long ArchonController::read_raw(std::string &retstring) { + const std::string function("Camera::ArchonController::read_raw"); + + this->get_frame_status(); + + const uint32_t bytes = this->raw_frame_bytes(); + if (bytes == 0) { + logwrite(function, "ERROR RAW geometry yields zero bytes; check RAW config"); + retstring = "invalid RAW geometry"; + return ERROR; + } + + const size_t nblocks = (bytes + BLOCK_LEN - 1) / BLOCK_LEN; + std::shared_ptr buffer(new char[nblocks * BLOCK_LEN]); + char* bufptr = buffer.get(); + + if (this->read_frame(FRAME_RAW, bufptr) != NO_ERROR) { + logwrite(function, "ERROR reading RAW frame"); + retstring = "read failed"; + return ERROR; + } + + const int lines = this->rawinfo.endline - this->rawinfo.startline; + const uint32_t rawlines = lines > 0 ? static_cast(lines) : 1u; + const auto index = this->frameinfo.index.load(); + + Camera::FrameMetadata meta; + meta.frame_number = this->frameinfo.bufframen[index]; + meta.timestamp = this->frameinfo.buftimestamp[index]; + meta.width = this->rawinfo.samples; + meta.height = rawlines; + meta.bytes_per_pixel = sizeof(uint16_t); + this->interface->dispatch_frame(buffer.get(), bytes, meta); + + std::ostringstream oss; + oss << "samples=" << this->rawinfo.samples << " lines=" << rawlines << " bytes=" << bytes; + retstring = oss.str(); + logwrite(function, retstring); + return NO_ERROR; + } + /***** Camera::ArchonController::read_raw *******************************/ + + /***** Camera::ArchonController::wait_for_readout ***************************/ /** * @brief creates a wait until the next completed frame buffer is ready diff --git a/camerad/archon_controller.h b/camerad/archon_controller.h index a4d0a04..1cffa74 100644 --- a/camerad/archon_controller.h +++ b/camerad/archon_controller.h @@ -249,10 +249,13 @@ namespace Camera { cfg_map_t configmap; param_map_t parammap; + /** @brief Archon RAW (pre-CDS) capture configuration, mirrors ACF keywords */ struct rawinfo_t { - int adchan; - uint16_t rawsamples; - uint16_t rawlines; + int adchan{0}; // RAWSEL: AD channel captured + uint16_t samples{0}; // RAWSAMPLES: 16-bit samples per line + uint16_t startline{0}; // RAWSTARTLINE + uint16_t endline{0}; // RAWENDLINE + uint16_t startpixel{0}; // RAWSTARTPIXEL } rawinfo; /** @@ -345,6 +348,13 @@ namespace Camera { long write_config_key(const char* key, const char* newvalue, bool &changed); long write_config_key(const char* key, int newvalue, bool &changed); + // RAW (pre-CDS) capture: configuration and retrieval + static bool is_raw_config_key(const std::string &key); + uint32_t raw_frame_bytes() const; // size-aware byte count for a RAW fetch + long set_raw_config(const std::string &args, std::string &retstring); + long get_raw_config(std::string &retstring); + long read_raw(std::string &retstring); + std::map modemap; diff --git a/camerad/archon_interface.cpp b/camerad/archon_interface.cpp index 156b0e1..0643cb2 100644 --- a/camerad/archon_interface.cpp +++ b/camerad/archon_interface.cpp @@ -68,6 +68,10 @@ namespace Camera { return this->set_camera_mode(args, retstring); } else + if ( cmd == CAMERAD_RAW ) { + return this->raw(args, retstring); + } + else if ( cmd == "autofetch_mode" ) { return this->autofetch_mode(args, retstring); } @@ -944,6 +948,48 @@ namespace Camera { /***** Camera::ArchonInterface::set_vcpu_inreg ******************************/ + /***** Camera::ArchonInterface::raw ****************************************/ + /** + * @brief configure and retrieve Archon RAW (pre-CDS) data + * @param[in] args "config" | "set [...]" | "read" + * @param[out] retstring RAW configuration or retrieval summary + * @return ERROR | NO_ERROR | HELP + * + */ + long ArchonInterface::raw( const std::string args, std::string &retstring ) { + const std::string function("Camera::ArchonInterface::raw"); + + if (args=="?" || args=="help") { + retstring = CAMERAD_RAW; + retstring.append( " [ config | set [...] | read ]\n" ); + retstring.append( " config report the RAW config keywords\n" ); + retstring.append( " set .. set RAW keyword(s) then apply\n" ); + retstring.append( " read retrieve RAW data in-band as 16-bit samples\n" ); + retstring.append( " Keys: RAWENABLE RAWSEL RAWSTARTLINE RAWENDLINE RAWSTARTPIXEL RAWSAMPLES\n" ); + return HELP; + } + + std::size_t sep = args.find_first_of(" "); + const std::string subcmd = args.substr(0, sep); + const std::string subargs = (sep==std::string::npos) ? "" : args.substr(sep+1); + + if (subcmd.empty() || subcmd=="config") { + return this->controller->get_raw_config(retstring); + } + if (subcmd=="set") { + return this->controller->set_raw_config(subargs, retstring); + } + if (subcmd=="read") { + return this->controller->read_raw(retstring); + } + + logwrite(function, "ERROR unrecognized subcommand: "+subcmd); + retstring = "unrecognized subcommand: "+subcmd; + return ERROR; + } + /***** Camera::ArchonInterface::raw ****************************************/ + + /***** Camera::ArchonInterface::native **************************************/ /** * @brief send native commands directly to Archon and log result diff --git a/camerad/archon_interface.h b/camerad/archon_interface.h index 8db6bcd..b2ad5c5 100644 --- a/camerad/archon_interface.h +++ b/camerad/archon_interface.h @@ -43,6 +43,7 @@ namespace Camera { long load_firmware( const std::string &args, std::string &retstring ) override; long native( const std::string args, std::string &retstring ) override; long power( const std::string args, std::string &retstring ) override; + long raw( const std::string args, std::string &retstring ); long test( const std::string args, std::string &retstring ) override; long do_expose() override; diff --git a/camerad/camera_server.cpp b/camerad/camera_server.cpp index 0149c6f..dff7f9a 100644 --- a/camerad/camera_server.cpp +++ b/camerad/camera_server.cpp @@ -321,6 +321,10 @@ namespace Camera { ret = interface->controller_cmd(cmd, args, retstring); } else + if ( cmd == CAMERAD_RAW ) { + ret = interface->controller_cmd(cmd, args, retstring); + } + else if ( cmd == "bob" ) { ret = interface->controller_cmd(cmd, args, retstring); } diff --git a/common/camerad_commands.h b/common/camerad_commands.h index 3da8f98..86e7758 100644 --- a/common/camerad_commands.h +++ b/common/camerad_commands.h @@ -44,6 +44,7 @@ const std::string CAMERAD_OPEN("open"); const int CAMERAD_OPEN_TIMEOUT(10000); const std::string CAMERAD_PAUSE("pause"); const std::string CAMERAD_POWER("power"); const std::string CAMERAD_PREEXPOSURES("preexposures"); +const std::string CAMERAD_RAW("raw"); const std::string CAMERAD_READACF("readacf"); const std::string CAMERAD_READOUT("readout"); const std::string CAMERAD_RESUME("resume"); @@ -88,6 +89,7 @@ const std::vector CAMERAD_SYNTAX = { CAMERAD_OPEN+" [ ? | ]", CAMERAD_PAUSE, CAMERAD_PREEXPOSURES, + CAMERAD_RAW+" [ ? | config | set [...] | read ]", CAMERAD_READACF+" [ ? | ]", CAMERAD_READOUT+" [ ? ] | [ | [ ] ]", CAMERAD_RESUME, From e81f0c8f383e5e11e03c1c9d603bdcf460f7f879 Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Tue, 21 Jul 2026 16:48:24 -0700 Subject: [PATCH 3/3] fix RAW sizing, inclusive line count, per-line block padding from BUFnRAWBLOCKS/LINES, and APPLYCDS --- camerad/archon_controller.cpp | 86 ++++++++++++++++++++++++++--------- camerad/archon_controller.h | 10 +++- 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/camerad/archon_controller.cpp b/camerad/archon_controller.cpp index eee3c18..504e6e5 100644 --- a/camerad/archon_controller.cpp +++ b/camerad/archon_controller.cpp @@ -579,13 +579,17 @@ namespace Camera { else if (std::strncmp(suffix, "HEIGHT", 6)==0) this->frameinfo.bufheight[bufnum] = std::atoi(value_start); break; - case 8: // BUFnCOMPLETE + case 8: // BUFnCOMPLETE, RAWLINES if (std::strncmp(suffix, "COMPLETE", 8)==0) this->frameinfo.bufcomplete[bufnum] = std::atoi(value_start); + else + if (std::strncmp(suffix, "RAWLINES", 8)==0) this->frameinfo.bufrawlines[bufnum] = std::atoi(value_start); break; - case 9: // BUFnTIMESTAMP, RAWOFFSET + case 9: // BUFnTIMESTAMP, RAWOFFSET, RAWBLOCKS if (std::strncmp(suffix, "TIMESTAMP", 9)==0) this->frameinfo.buftimestamp[bufnum] = std::strtoull(value_start, nullptr, 16); else if (std::strncmp(suffix, "RAWOFFSET", 9)==0) this->frameinfo.bufrawoffset[bufnum] = std::strtoul(value_start, nullptr, 10); + else + if (std::strncmp(suffix, "RAWBLOCKS", 9)==0) this->frameinfo.bufrawblocks[bufnum] = std::atoi(value_start); break; case 11: // BUFnRETIMESTAMP, FETIMESTAMP if (std::strncmp(suffix, "RETIMESTAMP", 11)==0) this->frameinfo.bufretimestamp[bufnum] = std::strtoull(value_start, nullptr, 16); @@ -2051,17 +2055,44 @@ namespace Camera { /***** Camera::ArchonController::is_raw_config_key *************************/ + /***** Camera::ArchonController::raw_geometry ****************************/ + /** + * @brief resolve RAW capture geometry for the newest buffer + * @details Prefers the controller-reported BUFnRAWBLOCKS/BUFnRAWLINES, + * which already account for the per-line rounding to whole + * 1024-byte blocks. Falls back to the config keys when the + * controller has not reported them (e.g. emulator). The line + * range is inclusive: RAWSTARTLINE=0,RAWENDLINE=1 is two lines. + */ + ArchonController::raw_geometry_t ArchonController::raw_geometry() const { + const auto index = this->frameinfo.index.load(); + raw_geometry_t geom; + geom.samples = static_cast(this->rawinfo.samples); + geom.blocks_per_line = static_cast(this->frameinfo.bufrawblocks[index]); + geom.lines = static_cast(this->frameinfo.bufrawlines[index]); + + if (geom.blocks_per_line == 0 || geom.lines == 0) { + geom.blocks_per_line = + (static_cast(geom.samples) * sizeof(uint16_t) + BLOCK_LEN - 1) / BLOCK_LEN; + const int span = this->rawinfo.endline - this->rawinfo.startline + 1; + geom.lines = span > 0 ? static_cast(span) : 0u; + } + return geom; + } + /***** Camera::ArchonController::raw_geometry ****************************/ + + /***** Camera::ArchonController::raw_frame_bytes **************************/ /** - * @brief size-aware byte count for one RAW capture - * @details RAW samples are always 16-bit. Count = rawlines x RAWSAMPLES, - * rawlines = RAWENDLINE - RAWSTARTLINE. RAWSTARTPIXEL only sets - * the reshape origin, so it does not change the sample count. + * @brief padded, size-aware byte count for one RAW fetch + * @details Each line occupies whole 1024-byte blocks, so the fetch reads + * blocks_per_line x lines blocks. RAW samples are always 16-bit; + * RAWSTARTPIXEL only sets where sampling begins in a line and so + * does not change the count. */ uint32_t ArchonController::raw_frame_bytes() const { - const int lines = this->rawinfo.endline - this->rawinfo.startline; - const uint32_t rawlines = lines > 0 ? static_cast(lines) : 1u; - return rawlines * static_cast(this->rawinfo.samples) * sizeof(uint16_t); + const raw_geometry_t geom = this->raw_geometry(); + return geom.blocks_per_line * geom.lines * BLOCK_LEN; } /***** Camera::ArchonController::raw_frame_bytes **************************/ @@ -2116,7 +2147,7 @@ namespace Camera { } } - if (changed && this->send_cmd(APPLYALL) != NO_ERROR) { + if (changed && this->send_cmd(APPLYCDS) != NO_ERROR) { logwrite(function, "ERROR applying RAW configuration"); retstring = "failed to apply"; return ERROR; @@ -2147,16 +2178,16 @@ namespace Camera { this->get_frame_status(); - const uint32_t bytes = this->raw_frame_bytes(); - if (bytes == 0) { - logwrite(function, "ERROR RAW geometry yields zero bytes; check RAW config"); + const raw_geometry_t geom = this->raw_geometry(); + if (geom.samples == 0 || geom.lines == 0) { + logwrite(function, "ERROR RAW geometry is empty; check RAW config"); retstring = "invalid RAW geometry"; return ERROR; } - const size_t nblocks = (bytes + BLOCK_LEN - 1) / BLOCK_LEN; - std::shared_ptr buffer(new char[nblocks * BLOCK_LEN]); - char* bufptr = buffer.get(); + const size_t fetch_bytes = static_cast(geom.blocks_per_line) * geom.lines * BLOCK_LEN; + std::shared_ptr raw_buffer(new char[fetch_bytes]); + char* bufptr = raw_buffer.get(); if (this->read_frame(FRAME_RAW, bufptr) != NO_ERROR) { logwrite(function, "ERROR reading RAW frame"); @@ -2164,20 +2195,31 @@ namespace Camera { return ERROR; } - const int lines = this->rawinfo.endline - this->rawinfo.startline; - const uint32_t rawlines = lines > 0 ? static_cast(lines) : 1u; + // The Archon pads each raw line out to whole 1024-byte blocks, so copy only + // the valid RAWSAMPLES from each line into a contiguous lines x samples array + const size_t line_stride = static_cast(geom.blocks_per_line) * BLOCK_LEN; + const size_t payload_samples = static_cast(geom.lines) * geom.samples; + std::vector samples(payload_samples); + for (uint32_t line = 0; line < geom.lines; ++line) { + std::memcpy(samples.data() + static_cast(line) * geom.samples, + raw_buffer.get() + line * line_stride, + static_cast(geom.samples) * sizeof(uint16_t)); + } + const auto index = this->frameinfo.index.load(); + const size_t payload_bytes = payload_samples * sizeof(uint16_t); Camera::FrameMetadata meta; meta.frame_number = this->frameinfo.bufframen[index]; meta.timestamp = this->frameinfo.buftimestamp[index]; - meta.width = this->rawinfo.samples; - meta.height = rawlines; + meta.width = geom.samples; + meta.height = geom.lines; meta.bytes_per_pixel = sizeof(uint16_t); - this->interface->dispatch_frame(buffer.get(), bytes, meta); + this->interface->dispatch_frame(reinterpret_cast(samples.data()), + payload_bytes, meta); std::ostringstream oss; - oss << "samples=" << this->rawinfo.samples << " lines=" << rawlines << " bytes=" << bytes; + oss << "samples=" << geom.samples << " lines=" << geom.lines << " bytes=" << payload_bytes; retstring = oss.str(); logwrite(function, retstring); return NO_ERROR; diff --git a/camerad/archon_controller.h b/camerad/archon_controller.h index 1cffa74..59932de 100644 --- a/camerad/archon_controller.h +++ b/camerad/archon_controller.h @@ -349,8 +349,16 @@ namespace Camera { long write_config_key(const char* key, int newvalue, bool &changed); // RAW (pre-CDS) capture: configuration and retrieval + /** @brief resolved RAW capture geometry for the newest buffer */ + struct raw_geometry_t { + uint32_t samples; // valid 16-bit samples per line (RAWSAMPLES) + uint32_t blocks_per_line; // 1024-byte blocks per line, padded per Archon + uint32_t lines; // number of raw lines (RAWENDLINE-RAWSTARTLINE+1) + }; + static bool is_raw_config_key(const std::string &key); - uint32_t raw_frame_bytes() const; // size-aware byte count for a RAW fetch + raw_geometry_t raw_geometry() const; + uint32_t raw_frame_bytes() const; // padded, size-aware byte count for a RAW fetch long set_raw_config(const std::string &args, std::string &retstring); long get_raw_config(std::string &retstring); long read_raw(std::string &retstring);