From d2a4853a5446ba888e0740af6ede8d9df7132ec8 Mon Sep 17 00:00:00 2001 From: Tetsuya Kakura Date: Sun, 1 Oct 2023 21:46:22 +0900 Subject: [PATCH 1/7] Stabilization of SVBONYBase::workerExposure - SVBONYBase::grabImage disassembled and embedded in SVBONYBase::workerExposure - Remove unused SVBONYBase::grabImage function svbony_helpers.h - Added processing in preparation for monochrome camera support (monochrome support not yet completed) - Changed Luma to Luma 8 bit and Luma 16 bit --- indi-svbony/svbony_base.cpp | 192 ++++++++++++++++------------------- indi-svbony/svbony_base.h | 3 +- indi-svbony/svbony_helpers.h | 5 +- 3 files changed, 91 insertions(+), 109 deletions(-) diff --git a/indi-svbony/svbony_base.cpp b/indi-svbony/svbony_base.cpp index 664cb1070..8616c1914 100644 --- a/indi-svbony/svbony_base.cpp +++ b/indi-svbony/svbony_base.cpp @@ -125,7 +125,7 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur if (ret == SVB_SUCCESS) break; - LOGF_ERROR("Failed to start exposure (%d)", Helpers::toString(ret)); + LOGF_ERROR("Failed to start exposure (%s)", Helpers::toString(ret)); // Wait 100ms before trying again usleep(100 * 1000); } @@ -135,12 +135,44 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur if (duration > VERBOSE_EXPOSURE) LOGF_INFO("Taking a %g seconds frame...", duration); + /* + Prepare a read buffer for SVB_IMG_RGB24. + */ + SVB_IMG_TYPE type = getImageType(); + + std::unique_lock guard(ccdBufferLock); + uint8_t *image = PrimaryCCD.getFrameBuffer(); + uint8_t *buffer = image; + + uint16_t subW = PrimaryCCD.getSubW() / PrimaryCCD.getBinX(); + uint16_t subH = PrimaryCCD.getSubH() / PrimaryCCD.getBinY(); + int nChannels = (type == SVB_IMG_RGB24) ? 3 : 1; + size_t nTotalBytes = subW * subH * nChannels * (PrimaryCCD.getBPP() / 8); + + if (type == SVB_IMG_RGB24) + { + buffer = static_cast(malloc(nTotalBytes)); + if (buffer == nullptr) + { + LOGF_ERROR("%s: %d malloc failed (RGB 24).", getDeviceName(), nTotalBytes); + return; + } + } + + /* + Perform exposure and image data reading + */ + int nRetry = 100; // Number of retries when ret is SVB_ERROR_TIMEOUT while (1) { if (isAboutToQuit) { - // Discard from buffer but do not send - grabImage(0, false); + ret = SVBGetVideoData(mCameraInfo.CameraID, buffer, nTotalBytes, 1000); + LOGF_DEBUG("Discard unretrieved exposure data: SVBGetVideoData(%s)", Helpers::toString(ret)); + if (type == SVB_IMG_RGB24) + free(buffer); + guard.unlock(); + PrimaryCCD.setExposureLeft(0); return; } @@ -161,17 +193,63 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur delay = std::max(timeLeft - std::trunc(timeLeft), 0.005f); timeLeft = std::round(timeLeft); } - if (timeLeft > 0) { PrimaryCCD.setExposureLeft(timeLeft); } else { - grabImage(duration); - return; - } + ret = SVBGetVideoData(mCameraInfo.CameraID, buffer, nTotalBytes, 1000); + LOGF_DEBUG("Retrieved exposure data: SVBGetVideoData(%s)", Helpers::toString(ret)); + switch (ret) + { + case SVB_SUCCESS: + if (type == SVB_IMG_RGB24) + { + uint8_t *dstR = image; + uint8_t *dstG = image + subW * subH; + uint8_t *dstB = image + subW * subH * 2; + + const uint8_t *src = buffer; + const uint8_t *end = buffer + subW * subH * 3; + + while (src != end) + { + *dstB++ = *src++; + *dstG++ = *src++; + *dstR++ = *src++; + } + free(buffer); + } + guard.unlock(); + sendImage(type, duration); + + mExposureRetry = 0; + PrimaryCCD.setExposureLeft(0.0); + if (PrimaryCCD.getExposureDuration() > VERBOSE_EXPOSURE) + LOG_INFO("Exposure done, downloading image..."); + return; + break; + case SVB_ERROR_TIMEOUT: + --nRetry; + LOGF_DEBUG("Remaining retry count for SVBGetVideoData:%d", nRetry); + if (nRetry) + { + // No image data is prepared in the buffer yet. Retry next step of while loop. + delay = 0.5f; + break; + } + //fall through + default: // Cannot continue to retrive image data when ret is any error except timeout. + if (type == SVB_IMG_RGB24) + free(buffer); + guard.unlock(); + PrimaryCCD.setExposureLeft(0); + PrimaryCCD.setExposureFailed(); + return; + } + } usleep(delay * 1000 * 1000); } } @@ -963,104 +1041,6 @@ bool SVBONYBase::UpdateCCDBin(int binx, int biny) return UpdateCCDFrame(PrimaryCCD.getSubX(), PrimaryCCD.getSubY(), PrimaryCCD.getSubW(), PrimaryCCD.getSubH()); } -/* Downloads the image from the CCD. - N.B. No processing is done on the image */ -bool SVBONYBase::grabImage(float duration, bool send) -{ - SVB_ERROR_CODE ret = SVB_SUCCESS; - - SVB_IMG_TYPE type = getImageType(); - - std::unique_lock guard(ccdBufferLock); - uint8_t *image = PrimaryCCD.getFrameBuffer(); - uint8_t *buffer = image; - - uint16_t subW = PrimaryCCD.getSubW() / PrimaryCCD.getBinX(); - uint16_t subH = PrimaryCCD.getSubH() / PrimaryCCD.getBinY(); - int nChannels = (type == SVB_IMG_RGB24) ? 3 : 1; - size_t nTotalBytes = subW * subH * nChannels * (PrimaryCCD.getBPP() / 8); - - if (type == SVB_IMG_RGB24) - { - buffer = static_cast(malloc(nTotalBytes)); - if (buffer == nullptr) - { - LOGF_ERROR("%s: %d malloc failed (RGB 24).", getDeviceName()); - return -1; - } - } - - for (int i = 0; i < 5; i++) - { - ret = SVBGetVideoData(mCameraInfo.CameraID, buffer, nTotalBytes, 1000); - // Sleep for 100 ms and try up to five times - if (ret == SVB_ERROR_TIMEOUT) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - continue; - } - else if (ret != SVB_SUCCESS) - { - LOGF_ERROR("Failed to get data after exposure (%dx%d #%d channels) (%s).", subW, subH, nChannels, Helpers::toString(ret)); - PrimaryCCD.setExposureLeft(0); - PrimaryCCD.setExposureFailed(); - if (type == SVB_IMG_RGB24) - free(buffer); - return false; - } - else - break; - } - - PrimaryCCD.setExposureLeft(0); - - // Duration 0 means aborted so we just need to discard the frame. - if (duration == 0) - { - PrimaryCCD.setExposureFailed(); - if (type == SVB_IMG_RGB24) - free(buffer); - return true; - } - else if (ret != SVB_SUCCESS) - { - LOGF_ERROR("Failed to get data after exposure (%dx%d #%d channels) (%s).", subW, subH, nChannels, Helpers::toString(ret)); - PrimaryCCD.setExposureFailed(); - if (type == SVB_IMG_RGB24) - free(buffer); - return false; - } - else - { - if (PrimaryCCD.getExposureDuration() > VERBOSE_EXPOSURE) - LOG_INFO("Exposure done, downloading image..."); - } - - if (type == SVB_IMG_RGB24) - { - uint8_t *dstR = image; - uint8_t *dstG = image + subW * subH; - uint8_t *dstB = image + subW * subH * 2; - - const uint8_t *src = buffer; - const uint8_t *end = buffer + subW * subH * 3; - - while (src != end) - { - *dstB++ = *src++; - *dstG++ = *src++; - *dstR++ = *src++; - } - - free(buffer); - } - guard.unlock(); - - if (send) - sendImage(type, duration); - return true; -} - void SVBONYBase::sendImage(SVB_IMG_TYPE type, float duration) { PrimaryCCD.setNAxis(type == SVB_IMG_RGB24 ? 3 : 2); @@ -1377,4 +1357,4 @@ bool SVBONYBase::saveConfigItems(FILE *fp) bool SVBONYBase::SetCaptureFormat(uint8_t index) { return setVideoFormat(index); -} +} \ No newline at end of file diff --git a/indi-svbony/svbony_base.h b/indi-svbony/svbony_base.h index f4641a773..22acb6779 100644 --- a/indi-svbony/svbony_base.h +++ b/indi-svbony/svbony_base.h @@ -89,8 +89,7 @@ class SVBONYBase : public INDI::CCD void workerStreamVideo(const std::atomic_bool &isAboutToQuit); void workerExposure(const std::atomic_bool &isAboutToQuit, float duration); - /** Get image from CCD and send it to client */ - bool grabImage(float duration, bool send = true); + /** Send CCD image to client */ void sendImage(SVB_IMG_TYPE type, float duration); protected: diff --git a/indi-svbony/svbony_helpers.h b/indi-svbony/svbony_helpers.h index d2ee9513f..7f5048f4c 100644 --- a/indi-svbony/svbony_helpers.h +++ b/indi-svbony/svbony_helpers.h @@ -87,6 +87,7 @@ const char *toString(SVB_IMG_TYPE type) case SVB_IMG_RGB24: return "SVB_IMG_RGB24"; case SVB_IMG_RAW16: return "SVB_IMG_RAW16"; case SVB_IMG_Y8: return "SVB_IMG_Y8"; + case SVB_IMG_Y16: return "SVB_IMG_Y16"; case SVB_IMG_END: return "SVB_IMG_END"; default: return "UNKNOWN"; } @@ -99,7 +100,8 @@ const char *toPrettyString(SVB_IMG_TYPE type) case SVB_IMG_RAW8: return "Raw 8 bit"; case SVB_IMG_RGB24: return "RGB 24"; case SVB_IMG_RAW16: return "Raw 16 bit"; - case SVB_IMG_Y8: return "Luma"; + case SVB_IMG_Y8: return "Luma 8 bit"; + case SVB_IMG_Y16: return "Luma 16 bit"; case SVB_IMG_END: return "END"; default: return "UNKNOWN"; } @@ -114,6 +116,7 @@ INDI_PIXEL_FORMAT pixelFormat(SVB_IMG_TYPE type, SVB_BAYER_PATTERN pattern, bool { case SVB_IMG_RGB24: return INDI_RGB; case SVB_IMG_Y8: return INDI_MONO; + case SVB_IMG_Y16: return INDI_MONO; default:; // see below } From ae3fdc58ea043b1fab4508a771daf107e0357c0e Mon Sep 17 00:00:00 2001 From: Tetsuya Kakura Date: Wed, 4 Oct 2023 23:14:32 +0900 Subject: [PATCH 2/7] SVBONY re-implementing monochrome camera and fixed some video format issues. --- indi-svbony/svbony_base.cpp | 180 ++++++++++++++++++++++------------- indi-svbony/svbony_helpers.h | 123 +++++++++++++++++++++++- 2 files changed, 233 insertions(+), 70 deletions(-) diff --git a/indi-svbony/svbony_base.cpp b/indi-svbony/svbony_base.cpp index 8616c1914..740684e93 100644 --- a/indi-svbony/svbony_base.cpp +++ b/indi-svbony/svbony_base.cpp @@ -60,48 +60,86 @@ void SVBONYBase::workerStreamVideo(const std::atomic_bool &isAboutToQuit) if (ret != SVB_SUCCESS) { LOGF_ERROR("Failed to set exposure duration (%s).", Helpers::toString(ret)); + return; } - ret = SVBStartVideoCapture(mCameraInfo.CameraID); - if (ret != SVB_SUCCESS) + // set camera normal mode + ret = SVBSetCameraMode(mCameraInfo.CameraID, SVB_MODE_NORMAL); + if(ret != SVB_SUCCESS) { - LOGF_ERROR("Failed to start video capture (%s).", Helpers::toString(ret)); + LOGF_ERROR("Failed to set normal mode (%s).", Helpers::toString(ret)); + return; } + LOG_INFO("Camera normal mode"); - while (!isAboutToQuit) + ret = SVBStartVideoCapture(mCameraInfo.CameraID); + if (ret == SVB_SUCCESS) { - uint8_t *targetFrame = PrimaryCCD.getFrameBuffer(); - uint32_t totalBytes = PrimaryCCD.getFrameBufferSize(); - int waitMS = static_cast((ExposureRequest * 2000.0) + 500); - - ret = SVBGetVideoData(mCameraInfo.CameraID, targetFrame, totalBytes, waitMS); - if (ret != SVB_SUCCESS) + while (!isAboutToQuit) { - if (ret != SVB_ERROR_TIMEOUT) + uint8_t *targetFrame = PrimaryCCD.getFrameBuffer(); + uint32_t totalBytes = PrimaryCCD.getFrameBufferSize(); + int waitMS = static_cast((ExposureRequest * 2000.0) + 500); + + ret = SVBGetVideoData(mCameraInfo.CameraID, targetFrame, totalBytes, waitMS); + if (ret != SVB_SUCCESS) { - Streamer->setStream(false); - LOGF_ERROR("Failed to read video data (%s).", Helpers::toString(ret)); - break; + if (ret != SVB_ERROR_TIMEOUT) + { + Streamer->setStream(false); + LOGF_ERROR("Failed to read video data (%s).", Helpers::toString(ret)); + break; + } + + usleep(100); + continue; } - usleep(100); - continue; + /* + RGB channel data align in targetFrame: 24bit:BGR, 32bit:BGRA + RGB channel data align in file: 24bit:RGB, 32bit:RGBA + */ + if (Helpers::isRGB(mCurrentVideoFormat)) { + int nChannels = Helpers::getNChannels(mCurrentVideoFormat); + for (uint32_t i = 0; i < totalBytes; i += nChannels) + std::swap(targetFrame[i], targetFrame[i + 2]); // swap R and B channel. + } + + Streamer->newFrame(targetFrame, totalBytes); } - if (mCurrentVideoFormat == SVB_IMG_RGB24) - for (uint32_t i = 0; i < totalBytes; i += 3) - std::swap(targetFrame[i], targetFrame[i + 2]); - - Streamer->newFrame(targetFrame, totalBytes); + SVBStopVideoCapture(mCameraInfo.CameraID); + } + else + { + LOGF_ERROR("Failed to start video capture (%s).", Helpers::toString(ret)); } - SVBStopVideoCapture(mCameraInfo.CameraID); + // set camera soft trigger mode + ret = SVBSetCameraMode(mCameraInfo.CameraID, SVB_MODE_TRIG_SOFT); + if(ret != SVB_SUCCESS) + { + LOGF_ERROR("Failed to set soft trigger mode (%s).", Helpers::toString(ret)); + } + else + { + LOG_DEBUG("Camera soft trigger mode"); + } } void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float duration) { SVB_ERROR_CODE ret; + // set camera soft trigger mode + ret = SVBSetCameraMode(mCameraInfo.CameraID, SVB_MODE_TRIG_SOFT); + if(ret != SVB_SUCCESS) + { + LOGF_ERROR("Failed to set soft trigger mode (%s).", Helpers::toString(ret)); + return; + } + LOG_DEBUG("Camera soft trigger mode"); + ret = SVBStartVideoCapture(mCameraInfo.CameraID); if (ret != SVB_SUCCESS) { @@ -119,7 +157,8 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur } // Try exposure for 3 times - for (int i = 0; i < 3; i++) + int nRetry = 3; // Number of retries to start exposure + while (nRetry--) { ret = SVBSendSoftTrigger(mCameraInfo.CameraID); if (ret == SVB_SUCCESS) @@ -129,6 +168,11 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur // Wait 100ms before trying again usleep(100 * 1000); } + if (!nRetry) + { + LOG_ERROR("Failed to start exposure three times."); + return; + } INDI::ElapsedTimer exposureTimer; @@ -136,7 +180,7 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur LOGF_INFO("Taking a %g seconds frame...", duration); /* - Prepare a read buffer for SVB_IMG_RGB24. + Prepare a read buffer for SVB_IMG_RGB24 and SVB_IMG_RGB32 */ SVB_IMG_TYPE type = getImageType(); @@ -146,15 +190,16 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur uint16_t subW = PrimaryCCD.getSubW() / PrimaryCCD.getBinX(); uint16_t subH = PrimaryCCD.getSubH() / PrimaryCCD.getBinY(); - int nChannels = (type == SVB_IMG_RGB24) ? 3 : 1; + int nChannels = Helpers::getNChannels(type); size_t nTotalBytes = subW * subH * nChannels * (PrimaryCCD.getBPP() / 8); - if (type == SVB_IMG_RGB24) + if (Helpers::isRGB(type)) { buffer = static_cast(malloc(nTotalBytes)); if (buffer == nullptr) { - LOGF_ERROR("%s: %d malloc failed (RGB 24).", getDeviceName(), nTotalBytes); + LOGF_ERROR("%s: %d malloc failed (RGB 24/32).", getDeviceName(), nTotalBytes); + guard.unlock(); return; } } @@ -162,14 +207,14 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur /* Perform exposure and image data reading */ - int nRetry = 100; // Number of retries when ret is SVB_ERROR_TIMEOUT + nRetry = 50; // Number of retries when ret is SVB_ERROR_TIMEOUT while (1) { if (isAboutToQuit) { ret = SVBGetVideoData(mCameraInfo.CameraID, buffer, nTotalBytes, 1000); LOGF_DEBUG("Discard unretrieved exposure data: SVBGetVideoData(%s)", Helpers::toString(ret)); - if (type == SVB_IMG_RGB24) + if (Helpers::isRGB(type)) free(buffer); guard.unlock(); PrimaryCCD.setExposureLeft(0); @@ -204,20 +249,38 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur switch (ret) { case SVB_SUCCESS: - if (type == SVB_IMG_RGB24) + if (Helpers::isRGB(type)) { uint8_t *dstR = image; uint8_t *dstG = image + subW * subH; uint8_t *dstB = image + subW * subH * 2; const uint8_t *src = buffer; - const uint8_t *end = buffer + subW * subH * 3; - while (src != end) + /* + To optimize execution speed, RGB32 and RGB24 are discriminated outside of while loop. + */ + if (type == SVB_IMG_RGB32) + { + const uint8_t *end = buffer + subW * subH * 4; + uint8_t *dstA = image + subW * subH * 3; // Alpha channel destination address + while (src != end) + { + *dstB++ = *src++; + *dstG++ = *src++; + *dstR++ = *src++; + *dstA++ = *src++; + } + } + else { - *dstB++ = *src++; - *dstG++ = *src++; - *dstR++ = *src++; + const uint8_t *end = buffer + subW * subH * 3; + while (src != end) + { + *dstB++ = *src++; + *dstG++ = *src++; + *dstR++ = *src++; + } } free(buffer); } @@ -230,7 +293,7 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur LOG_INFO("Exposure done, downloading image..."); return; - break; + case SVB_ERROR_TIMEOUT: --nRetry; LOGF_DEBUG("Remaining retry count for SVBGetVideoData:%d", nRetry); @@ -242,7 +305,7 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur } //fall through default: // Cannot continue to retrive image data when ret is any error except timeout. - if (type == SVB_IMG_RGB24) + if (Helpers::isRGB(type)) free(buffer); guard.unlock(); PrimaryCCD.setExposureLeft(0); @@ -367,7 +430,7 @@ bool SVBONYBase::updateProperties() { defineProperty(VideoFormatSP); - // Try to set 16bit RAW by default. + // Try to set 16bit RAW or 16bit Y by default. // It can get be overwritten by config value. // If config fails, we try to set 16 if exists. if (loadConfig(true, VideoFormatSP.getName()) == false) @@ -375,7 +438,9 @@ bool SVBONYBase::updateProperties() for (size_t i = 0; i < VideoFormatSP.size(); i++) { CaptureFormatSP[i].setState(ISS_OFF); - if (mCameraProperty.SupportedVideoFormat[i] == SVB_IMG_RAW16) + // In most cases, monochrome cameras will be Y16 and color cameras will be RAW16. + // Cameras that support both Y16 and RAW16 will be in the format that matches whichever comes first. + if (mCameraProperty.SupportedVideoFormat[i] == SVB_IMG_RAW16 || mCameraProperty.SupportedVideoFormat[i] == SVB_IMG_Y16) { setVideoFormat(i); CaptureFormatSP[i].setState(ISS_ON); @@ -601,20 +666,12 @@ void SVBONYBase::setupParams() mCurrentVideoFormat); // Get video format and bit depth - int bit_depth = 8; - switch (mCurrentVideoFormat) - { - case SVB_IMG_RAW16: - bit_depth = 16; - break; - - default: - break; - } + int bpp = Helpers::getBPP(mCurrentVideoFormat); // getBPP will retuen 8,16,24 or 32 VideoFormatSP.resize(0); for (const auto &videoFormat : mCameraProperty.SupportedVideoFormat) { + LOGF_DEBUG("Supported Video Format %d:%s", videoFormat, Helpers::toString(videoFormat)); if (videoFormat == SVB_IMG_END) break; @@ -629,7 +686,7 @@ void SVBONYBase::setupParams() VideoFormatSP.push(std::move(node)); CaptureFormat format = {Helpers::toString(videoFormat), Helpers::toPrettyString(videoFormat), - static_cast((videoFormat == SVB_IMG_RAW16) ? 16 : 8), + static_cast(Helpers::getBPP(videoFormat)), videoFormat == mCurrentVideoFormat }; addCaptureFormat(format); @@ -641,7 +698,7 @@ void SVBONYBase::setupParams() uint32_t maxWidth = mCameraProperty.MaxWidth; uint32_t maxHeight = mCameraProperty.MaxHeight; - SetCCDParams(maxWidth, maxHeight, bit_depth, pixelSize, pixelSize); + SetCCDParams(maxWidth, maxHeight, bpp, pixelSize, pixelSize); // Let's calculate required buffer int nbuf = PrimaryCCD.getXRes() * PrimaryCCD.getYRes() * PrimaryCCD.getBPP() / 8; @@ -1004,16 +1061,7 @@ bool SVBONYBase::UpdateCCDFrame(int x, int y, int w, int h) } mCurrentVideoFormat = getImageType(); - switch (mCurrentVideoFormat) - { - case SVB_IMG_RAW16: - PrimaryCCD.setBPP(16); - break; - - default: - PrimaryCCD.setBPP(8); - break; - } + PrimaryCCD.setBPP(Helpers::getBPP(mCurrentVideoFormat)); SVBSetOutputImageType(mCameraInfo.CameraID, mCurrentVideoFormat); @@ -1021,7 +1069,7 @@ bool SVBONYBase::UpdateCCDFrame(int x, int y, int w, int h) PrimaryCCD.setFrame(subX * binX, subY * binY, subW * binX, subH * binY); // Total bytes required for image buffer - auto nbuf = (subW * subH * static_cast(PrimaryCCD.getBPP()) / 8) * ((getImageType() == SVB_IMG_RGB24) ? 3 : 1); + auto nbuf = (subW * subH * static_cast(PrimaryCCD.getBPP()) / 8) * (Helpers::getNChannels(getImageType())); LOGF_DEBUG("Setting frame buffer size to %d bytes.", nbuf); PrimaryCCD.setFrameBufferSize(nbuf); @@ -1043,7 +1091,7 @@ bool SVBONYBase::UpdateCCDBin(int binx, int biny) void SVBONYBase::sendImage(SVB_IMG_TYPE type, float duration) { - PrimaryCCD.setNAxis(type == SVB_IMG_RGB24 ? 3 : 2); + PrimaryCCD.setNAxis(Helpers::getNAxis(type)); // If mono camera or we're sending Luma or RGB, turn off bayering if (mCameraProperty.IsColorCam == false || type >= SVB_IMG_Y8) @@ -1308,9 +1356,9 @@ void SVBONYBase::updateRecorderFormat() Helpers::pixelFormat( mCurrentVideoFormat, mCameraProperty.BayerPattern, - mCameraProperty.IsColorCam - ), - mCurrentVideoFormat == SVB_IMG_RAW16 ? 16 : 8 + Helpers::isColor(mCurrentVideoFormat) + ), + Helpers::getBPP(mCurrentVideoFormat) ); } diff --git a/indi-svbony/svbony_helpers.h b/indi-svbony/svbony_helpers.h index 7f5048f4c..5763608b3 100644 --- a/indi-svbony/svbony_helpers.h +++ b/indi-svbony/svbony_helpers.h @@ -45,7 +45,8 @@ const char *toString(SVB_BAYER_PATTERN pattern) case SVB_BAYER_BG: return "BGGR"; case SVB_BAYER_GR: return "GRBG"; case SVB_BAYER_GB: return "GBRG"; - default: return "RGGB"; + case SVB_BAYER_RG: return "RGGB"; + default: return "GRBG"; // default bayer pattern for SVBONY OSC camera. } } @@ -84,8 +85,12 @@ const char *toString(SVB_IMG_TYPE type) switch (type) { case SVB_IMG_RAW8: return "SVB_IMG_RAW8"; - case SVB_IMG_RGB24: return "SVB_IMG_RGB24"; + case SVB_IMG_RAW10: return "SVB_IMG_RAW10"; + case SVB_IMG_RAW12: return "SVB_IMG_RAW12"; + case SVB_IMG_RAW14: return "SVB_IMG_RAW14"; case SVB_IMG_RAW16: return "SVB_IMG_RAW16"; + case SVB_IMG_RGB24: return "SVB_IMG_RGB24"; + case SVB_IMG_RGB32: return "SVB_IMG_RGB32"; case SVB_IMG_Y8: return "SVB_IMG_Y8"; case SVB_IMG_Y16: return "SVB_IMG_Y16"; case SVB_IMG_END: return "SVB_IMG_END"; @@ -98,15 +103,26 @@ const char *toPrettyString(SVB_IMG_TYPE type) switch (type) { case SVB_IMG_RAW8: return "Raw 8 bit"; - case SVB_IMG_RGB24: return "RGB 24"; + case SVB_IMG_RAW10: return "Raw 10 bit"; + case SVB_IMG_RAW12: return "Raw 12 bit"; + case SVB_IMG_RAW14: return "Raw 14 bit"; case SVB_IMG_RAW16: return "Raw 16 bit"; case SVB_IMG_Y8: return "Luma 8 bit"; case SVB_IMG_Y16: return "Luma 16 bit"; + case SVB_IMG_RGB24: return "RGB 24"; + case SVB_IMG_RGB32: return "RGB 32"; case SVB_IMG_END: return "END"; default: return "UNKNOWN"; } } +/* + Determine the arguments in the following order: + isColor: not color -> mono + type: RGB* -> rgb, Y* -> mono + pattern: bayer pattern + other -> mono +*/ INDI_PIXEL_FORMAT pixelFormat(SVB_IMG_TYPE type, SVB_BAYER_PATTERN pattern, bool isColor) { if (isColor == false) @@ -115,8 +131,9 @@ INDI_PIXEL_FORMAT pixelFormat(SVB_IMG_TYPE type, SVB_BAYER_PATTERN pattern, bool switch (type) { case SVB_IMG_RGB24: return INDI_RGB; + case SVB_IMG_RGB32: return INDI_RGB; case SVB_IMG_Y8: return INDI_MONO; - case SVB_IMG_Y16: return INDI_MONO; + case SVB_IMG_Y16: return INDI_MONO; default:; // see below } @@ -131,4 +148,102 @@ INDI_PIXEL_FORMAT pixelFormat(SVB_IMG_TYPE type, SVB_BAYER_PATTERN pattern, bool return INDI_MONO; } +int getBPP(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: return 8; + case SVB_IMG_RAW10: return 16; + case SVB_IMG_RAW12: return 16; + case SVB_IMG_RAW14: return 16; + case SVB_IMG_RAW16: return 16; + case SVB_IMG_RGB24: return 8; + case SVB_IMG_RGB32: return 8; + case SVB_IMG_Y8: return 8; + case SVB_IMG_Y16: return 16; + case SVB_IMG_END: return 8; + default: return 8; + } +} + +int getNChannels(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: return 1; + case SVB_IMG_RAW10: return 1; + case SVB_IMG_RAW12: return 1; + case SVB_IMG_RAW14: return 1; + case SVB_IMG_RAW16: return 1; + case SVB_IMG_RGB24: return 3; + case SVB_IMG_RGB32: return 4; + case SVB_IMG_Y8: return 1; + case SVB_IMG_Y16: return 1; + case SVB_IMG_END: return 1; + default: return 1; + } +} + +int getNAxis(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: return 2; + case SVB_IMG_RAW10: return 2; + case SVB_IMG_RAW12: return 2; + case SVB_IMG_RAW14: return 2; + case SVB_IMG_RAW16: return 2; + case SVB_IMG_RGB24: return 3; + case SVB_IMG_RGB32: return 3; + case SVB_IMG_Y8: return 2; + case SVB_IMG_Y16: return 2; + case SVB_IMG_END: return 2; + default: return 2; + } +} + +bool isRGB(SVB_IMG_TYPE type) +{ + return (type == SVB_IMG_RGB24) || (type == SVB_IMG_RGB32); +} + +bool isColor(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: + case SVB_IMG_RAW10: + case SVB_IMG_RAW12: + case SVB_IMG_RAW14: + case SVB_IMG_RAW16: + case SVB_IMG_RGB24: + case SVB_IMG_RGB32: + return true; + case SVB_IMG_Y8: + case SVB_IMG_Y16: + default: + return false; + } +} + +bool hasBayer(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: + case SVB_IMG_RAW10: + case SVB_IMG_RAW12: + case SVB_IMG_RAW14: + case SVB_IMG_RAW16: + return true; + case SVB_IMG_RGB24: + case SVB_IMG_RGB32: + case SVB_IMG_Y8: + case SVB_IMG_Y16: + default: + return false; + } +} + } + From dcd51b2ddbb760a3dba4ca525874a1f06001ddc5 Mon Sep 17 00:00:00 2001 From: Tetsuya Kakura Date: Wed, 11 Oct 2023 18:56:41 +0900 Subject: [PATCH 3/7] SVBONYBase::Connect changes - Added DEBUGLOG output for camera attributes. - Modified to set Bayer pattern to INDI control panel. Modification of SVBONYBase::setupParam - modified to consider the number of channels when allocating buffers Improvement of SVBONYBase::sendImage - Modified to use Helper function to determine whether Bayer is present or not. --- indi-svbony/svbony_base.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/indi-svbony/svbony_base.cpp b/indi-svbony/svbony_base.cpp index 740684e93..3fb97d017 100644 --- a/indi-svbony/svbony_base.cpp +++ b/indi-svbony/svbony_base.cpp @@ -557,6 +557,24 @@ bool SVBONYBase::Connect() LOGF_DEBUG("IsCoolerCam: %s", mCameraPropertyExtended.bSupportControlTemp ? "True" : "False"); LOGF_DEBUG("BitDepth: %d", mCameraProperty.MaxBitDepth); LOGF_DEBUG("IsTriggerCam: %s", mCameraProperty.IsTriggerCam ? "True" : "False"); + LOGF_DEBUG("BayerPattern:%s", Helpers::toString(mCameraProperty.BayerPattern)); + + // Output camera properties to log + if (isDebug()) + { + for (int i = 0; (i < (int)(sizeof(mCameraProperty.SupportedBins) / sizeof(mCameraProperty.SupportedBins[0]))) && mCameraProperty.SupportedBins[i] != 0; i++) + { + LOGF_DEBUG(" Bin %d", mCameraProperty.SupportedBins[i]); + } + for (int i = 0; (i < (int)(sizeof(mCameraProperty.SupportedVideoFormat) / sizeof(mCameraProperty.SupportedVideoFormat[0]))) && mCameraProperty.SupportedVideoFormat[i] != SVB_IMG_END; i++) + { + LOGF_DEBUG(" Supported Video Format: %s", Helpers::toString(mCameraProperty.SupportedVideoFormat[i])); + } + } + + // output camera properties ex to log + LOGF_DEBUG("SupportPulseGuide: %s", mCameraPropertyExtended.bSupportPulseGuide ? "True" : "False"); + LOGF_DEBUG("SupportControlTemp: %s", mCameraPropertyExtended.bSupportControlTemp ? "True" : "False"); uint32_t cap = 0; @@ -570,7 +588,11 @@ bool SVBONYBase::Connect() cap |= CCD_HAS_ST4_PORT; if (mCameraProperty.IsColorCam) + { cap |= CCD_HAS_BAYER; + IUSaveText(&BayerT[2], getBayerString()); + IDSetText(&BayerTP, nullptr); + } cap |= CCD_CAN_ABORT; cap |= CCD_CAN_SUBFRAME; @@ -701,7 +723,7 @@ void SVBONYBase::setupParams() SetCCDParams(maxWidth, maxHeight, bpp, pixelSize, pixelSize); // Let's calculate required buffer - int nbuf = PrimaryCCD.getXRes() * PrimaryCCD.getYRes() * PrimaryCCD.getBPP() / 8; + int nbuf = (PrimaryCCD.getXRes() * PrimaryCCD.getYRes() * PrimaryCCD.getBPP() / 8) * Helpers::getNChannels(mCurrentVideoFormat); PrimaryCCD.setFrameBufferSize(nbuf); long value = 0; @@ -1094,9 +1116,7 @@ void SVBONYBase::sendImage(SVB_IMG_TYPE type, float duration) PrimaryCCD.setNAxis(Helpers::getNAxis(type)); // If mono camera or we're sending Luma or RGB, turn off bayering - if (mCameraProperty.IsColorCam == false || type >= SVB_IMG_Y8) - SetCCDCapability(GetCCDCapability() & ~CCD_HAS_BAYER); - else + if (Helpers::hasBayer(type)) { SetCCDCapability(GetCCDCapability() | CCD_HAS_BAYER); auto bayerString = getBayerString(); @@ -1107,6 +1127,10 @@ void SVBONYBase::sendImage(SVB_IMG_TYPE type, float duration) IDSetText(&BayerTP, nullptr); } } + else + { + SetCCDCapability(GetCCDCapability() & ~CCD_HAS_BAYER); + } if (duration > VERBOSE_EXPOSURE) LOG_INFO("Download complete."); From 63219b5218c8a18d9e8fabbe97098e693b2f1661 Mon Sep 17 00:00:00 2001 From: Tetsuya Kakura Date: Sun, 15 Oct 2023 00:46:30 +0900 Subject: [PATCH 4/7] Fixed some bugs and added workaround code for SVBONY Camera SDK - Changed the SDK to set the ROI only if it is different from the current ROI when setting the ROI - Added code to call SVBSetCameraMode function in SVBONYBase::Connect(). This is a workaround code for a bug in the SDK. - Fixed parameter for setting target cooling temperature in SVB_TARGET_TEMPERATURE --- indi-svbony/svbony_base.cpp | 50 ++++++++++++++++++++++++++++++------- indi-svbony/svbony_base.h | 3 +++ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/indi-svbony/svbony_base.cpp b/indi-svbony/svbony_base.cpp index 3fb97d017..79875b26d 100644 --- a/indi-svbony/svbony_base.cpp +++ b/indi-svbony/svbony_base.cpp @@ -50,6 +50,35 @@ const char *SVBONYBase::getBayerString() const return Helpers::toString(mCameraProperty.BayerPattern); } +// Set ROI and Binning +bool SVBONYBase::SetROIFormat(int x, int y, int w, int h, int bin) +{ + SVB_ERROR_CODE ret; + int currentX = 0, currentY = 0, currentW = 0, currentH = 0, currentBin = 0; + + ret = SVBGetROIFormat(mCameraInfo.CameraID, ¤tY, ¤tY, ¤tW, ¤tH, ¤tBin); + if (ret != SVB_SUCCESS) + { + LOGF_ERROR("Failed to get ROI format (%s).", Helpers::toString(ret)); + } + LOGF_DEBUG("SVBGetROIFormat (%d,%d-%d,%d, bin:%d)", currentX, currentY, currentW, currentH, currentBin); + + if (currentX == x && currentY == y && currentW == w && currentH == h && currentBin == bin) + { + LOG_DEBUG("SetROIFormat: Both the requested ROI and Bin are same as current ones. So don't need to change it to what are requested."); + return true; // both the requested ROI and Bin are same as current ones. So don't need to change it to what are requested. + } + + LOGF_DEBUG("SVBSetROIFormat (%d,%d-%d,%d, bin:%d)", x, y, w, h, bin); + ret = SVBSetROIFormat(mCameraInfo.CameraID, x, y, w, h, bin); + if (ret != SVB_SUCCESS) + { + LOGF_ERROR("Failed to set ROI (%s).", Helpers::toString(ret)); + return false; + } + return true; +} + void SVBONYBase::workerStreamVideo(const std::atomic_bool &isAboutToQuit) { SVB_ERROR_CODE ret; @@ -614,6 +643,11 @@ bool SVBONYBase::Connect() // set exposure time SVBSetControlValue(mCameraInfo.CameraID, SVB_EXPOSURE, static_cast(1 * 1000000L), SVB_FALSE); + // workaround for SDK cooling fan stopping issue + // The cooling fan stops when SVBSetCameraMode is changed. + // Set to Soft Trigger Mode for taking still pictures to reduce the impact of this problem. + SVBSetCameraMode(mCameraInfo.CameraID, SVB_MODE_TRIG_SOFT); + /* Success! */ LOG_INFO("Camera is online. Retrieving configuration."); @@ -743,8 +777,7 @@ void SVBONYBase::setupParams() if (ret != SVB_SUCCESS) LOGF_ERROR("Failed to stop video capture (%s).", Helpers::toString(ret)); - LOGF_DEBUG("setupParams SVBSetROIFormat (%dx%d, bin %d, type %d)", maxWidth, maxHeight, 1, mCurrentVideoFormat); - SVBSetROIFormat(mCameraInfo.CameraID, 0, 0, maxWidth, maxHeight, 1); + SetROIFormat(0, 0, maxWidth, maxHeight, 1); updateRecorderFormat(); Streamer->setSize(maxWidth, maxHeight); @@ -973,7 +1006,7 @@ int SVBONYBase::SetTemperature(double temperature) SVB_ERROR_CODE ret; - ret = SVBSetControlValue(mCameraInfo.CameraID, SVB_TARGET_TEMPERATURE, std::round(temperature), SVB_TRUE); + ret = SVBSetControlValue(mCameraInfo.CameraID, SVB_TARGET_TEMPERATURE, std::round(temperature * 10.0), SVB_TRUE); // For SVB_TARGET_TEMPERATURE, 1 unit is set as 0.1 degree. if (ret != SVB_SUCCESS) { LOGF_ERROR("Failed to set temperature (%s).", Helpers::toString(ret)); @@ -1072,13 +1105,8 @@ bool SVBONYBase::UpdateCCDFrame(int x, int y, int w, int h) subH -= subH % 2; LOGF_DEBUG("Frame ROI x:%d y:%d w:%d h:%d", subX, subY, subW, subH); - - SVB_ERROR_CODE ret; - - ret = SVBSetROIFormat(mCameraInfo.CameraID, subX, subY, subW, subH, binX); - if (ret != SVB_SUCCESS) + if (false == SetROIFormat(subX, subY, subW, subH, binX)) { - LOGF_ERROR("Failed to set ROI (%s).", Helpers::toString(ret)); return false; } @@ -1167,6 +1195,10 @@ void SVBONYBase::temperatureTimerTimeout() TemperatureNP.s = newState; TemperatureN[0].value = mCurrentTemperature; IDSetNumber(&TemperatureNP, nullptr); +/* + This log should be commented out except when investigating bugs, etc., as it outputs very frequently. + LOGF_DEBUG("Current Temperature %.2f degree", mCurrentTemperature); +*/ } if (HasCooler()) diff --git a/indi-svbony/svbony_base.h b/indi-svbony/svbony_base.h index 22acb6779..51cc986bd 100644 --- a/indi-svbony/svbony_base.h +++ b/indi-svbony/svbony_base.h @@ -84,6 +84,9 @@ class SVBONYBase : public INDI::CCD /** Get the current Bayer string used */ const char *getBayerString() const; + // Set ROI and Binning + bool SetROIFormat(int x, int y, int w, int h, int bin); + protected: INDI::SingleThreadPool mWorker; void workerStreamVideo(const std::atomic_bool &isAboutToQuit); From 40169e2b6af20c6be1967528a1063f000f8f0731 Mon Sep 17 00:00:00 2001 From: Tetsuya Kakura Date: Sun, 15 Oct 2023 11:27:20 +0900 Subject: [PATCH 5/7] Save default value to prevent crash * Merge with d1622b9 --- indi-svbony/svbony_base.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indi-svbony/svbony_base.cpp b/indi-svbony/svbony_base.cpp index 79875b26d..5c8c338f7 100644 --- a/indi-svbony/svbony_base.cpp +++ b/indi-svbony/svbony_base.cpp @@ -409,6 +409,8 @@ bool SVBONYBase::initProperties() NicknameTP[0].fill("nickname", "nickname", mNickname); NicknameTP.fill(getDeviceName(), "NICKNAME", "Nickname", INFO_TAB, IP_RW, 60, IPS_IDLE); + IUSaveText(&BayerT[2], "GRBG"); + addAuxControls(); return true; From d24cff576c61f7cfee64985d0c074190f64c3322 Mon Sep 17 00:00:00 2001 From: Tetsuya Kakura Date: Wed, 4 Oct 2023 23:14:32 +0900 Subject: [PATCH 6/7] SVBONY re-implementing monochrome camera and fixed some video format issues. --- indi-svbony/svbony_base.cpp | 266 ++++++++++++++++++++++++----------- indi-svbony/svbony_helpers.h | 123 +++++++++++++++- 2 files changed, 304 insertions(+), 85 deletions(-) diff --git a/indi-svbony/svbony_base.cpp b/indi-svbony/svbony_base.cpp index a35054c1d..5c8c338f7 100644 --- a/indi-svbony/svbony_base.cpp +++ b/indi-svbony/svbony_base.cpp @@ -50,6 +50,35 @@ const char *SVBONYBase::getBayerString() const return Helpers::toString(mCameraProperty.BayerPattern); } +// Set ROI and Binning +bool SVBONYBase::SetROIFormat(int x, int y, int w, int h, int bin) +{ + SVB_ERROR_CODE ret; + int currentX = 0, currentY = 0, currentW = 0, currentH = 0, currentBin = 0; + + ret = SVBGetROIFormat(mCameraInfo.CameraID, ¤tY, ¤tY, ¤tW, ¤tH, ¤tBin); + if (ret != SVB_SUCCESS) + { + LOGF_ERROR("Failed to get ROI format (%s).", Helpers::toString(ret)); + } + LOGF_DEBUG("SVBGetROIFormat (%d,%d-%d,%d, bin:%d)", currentX, currentY, currentW, currentH, currentBin); + + if (currentX == x && currentY == y && currentW == w && currentH == h && currentBin == bin) + { + LOG_DEBUG("SetROIFormat: Both the requested ROI and Bin are same as current ones. So don't need to change it to what are requested."); + return true; // both the requested ROI and Bin are same as current ones. So don't need to change it to what are requested. + } + + LOGF_DEBUG("SVBSetROIFormat (%d,%d-%d,%d, bin:%d)", x, y, w, h, bin); + ret = SVBSetROIFormat(mCameraInfo.CameraID, x, y, w, h, bin); + if (ret != SVB_SUCCESS) + { + LOGF_ERROR("Failed to set ROI (%s).", Helpers::toString(ret)); + return false; + } + return true; +} + void SVBONYBase::workerStreamVideo(const std::atomic_bool &isAboutToQuit) { SVB_ERROR_CODE ret; @@ -60,48 +89,86 @@ void SVBONYBase::workerStreamVideo(const std::atomic_bool &isAboutToQuit) if (ret != SVB_SUCCESS) { LOGF_ERROR("Failed to set exposure duration (%s).", Helpers::toString(ret)); + return; } - ret = SVBStartVideoCapture(mCameraInfo.CameraID); - if (ret != SVB_SUCCESS) + // set camera normal mode + ret = SVBSetCameraMode(mCameraInfo.CameraID, SVB_MODE_NORMAL); + if(ret != SVB_SUCCESS) { - LOGF_ERROR("Failed to start video capture (%s).", Helpers::toString(ret)); + LOGF_ERROR("Failed to set normal mode (%s).", Helpers::toString(ret)); + return; } + LOG_INFO("Camera normal mode"); - while (!isAboutToQuit) + ret = SVBStartVideoCapture(mCameraInfo.CameraID); + if (ret == SVB_SUCCESS) { - uint8_t *targetFrame = PrimaryCCD.getFrameBuffer(); - uint32_t totalBytes = PrimaryCCD.getFrameBufferSize(); - int waitMS = static_cast((ExposureRequest * 2000.0) + 500); - - ret = SVBGetVideoData(mCameraInfo.CameraID, targetFrame, totalBytes, waitMS); - if (ret != SVB_SUCCESS) + while (!isAboutToQuit) { - if (ret != SVB_ERROR_TIMEOUT) + uint8_t *targetFrame = PrimaryCCD.getFrameBuffer(); + uint32_t totalBytes = PrimaryCCD.getFrameBufferSize(); + int waitMS = static_cast((ExposureRequest * 2000.0) + 500); + + ret = SVBGetVideoData(mCameraInfo.CameraID, targetFrame, totalBytes, waitMS); + if (ret != SVB_SUCCESS) { - Streamer->setStream(false); - LOGF_ERROR("Failed to read video data (%s).", Helpers::toString(ret)); - break; + if (ret != SVB_ERROR_TIMEOUT) + { + Streamer->setStream(false); + LOGF_ERROR("Failed to read video data (%s).", Helpers::toString(ret)); + break; + } + + usleep(100); + continue; } - usleep(100); - continue; + /* + RGB channel data align in targetFrame: 24bit:BGR, 32bit:BGRA + RGB channel data align in file: 24bit:RGB, 32bit:RGBA + */ + if (Helpers::isRGB(mCurrentVideoFormat)) { + int nChannels = Helpers::getNChannels(mCurrentVideoFormat); + for (uint32_t i = 0; i < totalBytes; i += nChannels) + std::swap(targetFrame[i], targetFrame[i + 2]); // swap R and B channel. + } + + Streamer->newFrame(targetFrame, totalBytes); } - if (mCurrentVideoFormat == SVB_IMG_RGB24) - for (uint32_t i = 0; i < totalBytes; i += 3) - std::swap(targetFrame[i], targetFrame[i + 2]); - - Streamer->newFrame(targetFrame, totalBytes); + SVBStopVideoCapture(mCameraInfo.CameraID); + } + else + { + LOGF_ERROR("Failed to start video capture (%s).", Helpers::toString(ret)); } - SVBStopVideoCapture(mCameraInfo.CameraID); + // set camera soft trigger mode + ret = SVBSetCameraMode(mCameraInfo.CameraID, SVB_MODE_TRIG_SOFT); + if(ret != SVB_SUCCESS) + { + LOGF_ERROR("Failed to set soft trigger mode (%s).", Helpers::toString(ret)); + } + else + { + LOG_DEBUG("Camera soft trigger mode"); + } } void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float duration) { SVB_ERROR_CODE ret; + // set camera soft trigger mode + ret = SVBSetCameraMode(mCameraInfo.CameraID, SVB_MODE_TRIG_SOFT); + if(ret != SVB_SUCCESS) + { + LOGF_ERROR("Failed to set soft trigger mode (%s).", Helpers::toString(ret)); + return; + } + LOG_DEBUG("Camera soft trigger mode"); + ret = SVBStartVideoCapture(mCameraInfo.CameraID); if (ret != SVB_SUCCESS) { @@ -119,7 +186,8 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur } // Try exposure for 3 times - for (int i = 0; i < 3; i++) + int nRetry = 3; // Number of retries to start exposure + while (nRetry--) { ret = SVBSendSoftTrigger(mCameraInfo.CameraID); if (ret == SVB_SUCCESS) @@ -129,6 +197,11 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur // Wait 100ms before trying again usleep(100 * 1000); } + if (!nRetry) + { + LOG_ERROR("Failed to start exposure three times."); + return; + } INDI::ElapsedTimer exposureTimer; @@ -136,7 +209,7 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur LOGF_INFO("Taking a %g seconds frame...", duration); /* - Prepare a read buffer for SVB_IMG_RGB24. + Prepare a read buffer for SVB_IMG_RGB24 and SVB_IMG_RGB32 */ SVB_IMG_TYPE type = getImageType(); @@ -146,15 +219,16 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur uint16_t subW = PrimaryCCD.getSubW() / PrimaryCCD.getBinX(); uint16_t subH = PrimaryCCD.getSubH() / PrimaryCCD.getBinY(); - int nChannels = (type == SVB_IMG_RGB24) ? 3 : 1; + int nChannels = Helpers::getNChannels(type); size_t nTotalBytes = subW * subH * nChannels * (PrimaryCCD.getBPP() / 8); - if (type == SVB_IMG_RGB24) + if (Helpers::isRGB(type)) { buffer = static_cast(malloc(nTotalBytes)); if (buffer == nullptr) { - LOGF_ERROR("%s: %d malloc failed (RGB 24).", getDeviceName(), nTotalBytes); + LOGF_ERROR("%s: %d malloc failed (RGB 24/32).", getDeviceName(), nTotalBytes); + guard.unlock(); return; } } @@ -162,14 +236,14 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur /* Perform exposure and image data reading */ - int nRetry = 100; // Number of retries when ret is SVB_ERROR_TIMEOUT + nRetry = 50; // Number of retries when ret is SVB_ERROR_TIMEOUT while (1) { if (isAboutToQuit) { ret = SVBGetVideoData(mCameraInfo.CameraID, buffer, nTotalBytes, 1000); LOGF_DEBUG("Discard unretrieved exposure data: SVBGetVideoData(%s)", Helpers::toString(ret)); - if (type == SVB_IMG_RGB24) + if (Helpers::isRGB(type)) free(buffer); guard.unlock(); PrimaryCCD.setExposureLeft(0); @@ -204,20 +278,38 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur switch (ret) { case SVB_SUCCESS: - if (type == SVB_IMG_RGB24) + if (Helpers::isRGB(type)) { uint8_t *dstR = image; uint8_t *dstG = image + subW * subH; uint8_t *dstB = image + subW * subH * 2; const uint8_t *src = buffer; - const uint8_t *end = buffer + subW * subH * 3; - while (src != end) + /* + To optimize execution speed, RGB32 and RGB24 are discriminated outside of while loop. + */ + if (type == SVB_IMG_RGB32) + { + const uint8_t *end = buffer + subW * subH * 4; + uint8_t *dstA = image + subW * subH * 3; // Alpha channel destination address + while (src != end) + { + *dstB++ = *src++; + *dstG++ = *src++; + *dstR++ = *src++; + *dstA++ = *src++; + } + } + else { - *dstB++ = *src++; - *dstG++ = *src++; - *dstR++ = *src++; + const uint8_t *end = buffer + subW * subH * 3; + while (src != end) + { + *dstB++ = *src++; + *dstG++ = *src++; + *dstR++ = *src++; + } } free(buffer); } @@ -230,7 +322,7 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur LOG_INFO("Exposure done, downloading image..."); return; - break; + case SVB_ERROR_TIMEOUT: --nRetry; LOGF_DEBUG("Remaining retry count for SVBGetVideoData:%d", nRetry); @@ -240,9 +332,9 @@ void SVBONYBase::workerExposure(const std::atomic_bool &isAboutToQuit, float dur delay = 0.5f; break; } - //fall through + //fall through default: // Cannot continue to retrive image data when ret is any error except timeout. - if (type == SVB_IMG_RGB24) + if (Helpers::isRGB(type)) free(buffer); guard.unlock(); PrimaryCCD.setExposureLeft(0); @@ -369,7 +461,7 @@ bool SVBONYBase::updateProperties() { defineProperty(VideoFormatSP); - // Try to set 16bit RAW by default. + // Try to set 16bit RAW or 16bit Y by default. // It can get be overwritten by config value. // If config fails, we try to set 16 if exists. if (loadConfig(true, VideoFormatSP.getName()) == false) @@ -377,7 +469,9 @@ bool SVBONYBase::updateProperties() for (size_t i = 0; i < VideoFormatSP.size(); i++) { CaptureFormatSP[i].setState(ISS_OFF); - if (mCameraProperty.SupportedVideoFormat[i] == SVB_IMG_RAW16) + // In most cases, monochrome cameras will be Y16 and color cameras will be RAW16. + // Cameras that support both Y16 and RAW16 will be in the format that matches whichever comes first. + if (mCameraProperty.SupportedVideoFormat[i] == SVB_IMG_RAW16 || mCameraProperty.SupportedVideoFormat[i] == SVB_IMG_Y16) { setVideoFormat(i); CaptureFormatSP[i].setState(ISS_ON); @@ -494,6 +588,24 @@ bool SVBONYBase::Connect() LOGF_DEBUG("IsCoolerCam: %s", mCameraPropertyExtended.bSupportControlTemp ? "True" : "False"); LOGF_DEBUG("BitDepth: %d", mCameraProperty.MaxBitDepth); LOGF_DEBUG("IsTriggerCam: %s", mCameraProperty.IsTriggerCam ? "True" : "False"); + LOGF_DEBUG("BayerPattern:%s", Helpers::toString(mCameraProperty.BayerPattern)); + + // Output camera properties to log + if (isDebug()) + { + for (int i = 0; (i < (int)(sizeof(mCameraProperty.SupportedBins) / sizeof(mCameraProperty.SupportedBins[0]))) && mCameraProperty.SupportedBins[i] != 0; i++) + { + LOGF_DEBUG(" Bin %d", mCameraProperty.SupportedBins[i]); + } + for (int i = 0; (i < (int)(sizeof(mCameraProperty.SupportedVideoFormat) / sizeof(mCameraProperty.SupportedVideoFormat[0]))) && mCameraProperty.SupportedVideoFormat[i] != SVB_IMG_END; i++) + { + LOGF_DEBUG(" Supported Video Format: %s", Helpers::toString(mCameraProperty.SupportedVideoFormat[i])); + } + } + + // output camera properties ex to log + LOGF_DEBUG("SupportPulseGuide: %s", mCameraPropertyExtended.bSupportPulseGuide ? "True" : "False"); + LOGF_DEBUG("SupportControlTemp: %s", mCameraPropertyExtended.bSupportControlTemp ? "True" : "False"); uint32_t cap = 0; @@ -507,7 +619,11 @@ bool SVBONYBase::Connect() cap |= CCD_HAS_ST4_PORT; if (mCameraProperty.IsColorCam) + { cap |= CCD_HAS_BAYER; + IUSaveText(&BayerT[2], getBayerString()); + IDSetText(&BayerTP, nullptr); + } cap |= CCD_CAN_ABORT; cap |= CCD_CAN_SUBFRAME; @@ -529,6 +645,11 @@ bool SVBONYBase::Connect() // set exposure time SVBSetControlValue(mCameraInfo.CameraID, SVB_EXPOSURE, static_cast(1 * 1000000L), SVB_FALSE); + // workaround for SDK cooling fan stopping issue + // The cooling fan stops when SVBSetCameraMode is changed. + // Set to Soft Trigger Mode for taking still pictures to reduce the impact of this problem. + SVBSetCameraMode(mCameraInfo.CameraID, SVB_MODE_TRIG_SOFT); + /* Success! */ LOG_INFO("Camera is online. Retrieving configuration."); @@ -603,20 +724,12 @@ void SVBONYBase::setupParams() mCurrentVideoFormat); // Get video format and bit depth - int bit_depth = 8; - switch (mCurrentVideoFormat) - { - case SVB_IMG_RAW16: - bit_depth = 16; - break; - - default: - break; - } + int bpp = Helpers::getBPP(mCurrentVideoFormat); // getBPP will retuen 8,16,24 or 32 VideoFormatSP.resize(0); for (const auto &videoFormat : mCameraProperty.SupportedVideoFormat) { + LOGF_DEBUG("Supported Video Format %d:%s", videoFormat, Helpers::toString(videoFormat)); if (videoFormat == SVB_IMG_END) break; @@ -631,7 +744,7 @@ void SVBONYBase::setupParams() VideoFormatSP.push(std::move(node)); CaptureFormat format = {Helpers::toString(videoFormat), Helpers::toPrettyString(videoFormat), - static_cast((videoFormat == SVB_IMG_RAW16) ? 16 : 8), + static_cast(Helpers::getBPP(videoFormat)), videoFormat == mCurrentVideoFormat }; addCaptureFormat(format); @@ -643,10 +756,10 @@ void SVBONYBase::setupParams() uint32_t maxWidth = mCameraProperty.MaxWidth; uint32_t maxHeight = mCameraProperty.MaxHeight; - SetCCDParams(maxWidth, maxHeight, bit_depth, pixelSize, pixelSize); + SetCCDParams(maxWidth, maxHeight, bpp, pixelSize, pixelSize); // Let's calculate required buffer - int nbuf = PrimaryCCD.getXRes() * PrimaryCCD.getYRes() * PrimaryCCD.getBPP() / 8; + int nbuf = (PrimaryCCD.getXRes() * PrimaryCCD.getYRes() * PrimaryCCD.getBPP() / 8) * Helpers::getNChannels(mCurrentVideoFormat); PrimaryCCD.setFrameBufferSize(nbuf); long value = 0; @@ -666,8 +779,7 @@ void SVBONYBase::setupParams() if (ret != SVB_SUCCESS) LOGF_ERROR("Failed to stop video capture (%s).", Helpers::toString(ret)); - LOGF_DEBUG("setupParams SVBSetROIFormat (%dx%d, bin %d, type %d)", maxWidth, maxHeight, 1, mCurrentVideoFormat); - SVBSetROIFormat(mCameraInfo.CameraID, 0, 0, maxWidth, maxHeight, 1); + SetROIFormat(0, 0, maxWidth, maxHeight, 1); updateRecorderFormat(); Streamer->setSize(maxWidth, maxHeight); @@ -896,7 +1008,7 @@ int SVBONYBase::SetTemperature(double temperature) SVB_ERROR_CODE ret; - ret = SVBSetControlValue(mCameraInfo.CameraID, SVB_TARGET_TEMPERATURE, std::round(temperature), SVB_TRUE); + ret = SVBSetControlValue(mCameraInfo.CameraID, SVB_TARGET_TEMPERATURE, std::round(temperature * 10.0), SVB_TRUE); // For SVB_TARGET_TEMPERATURE, 1 unit is set as 0.1 degree. if (ret != SVB_SUCCESS) { LOGF_ERROR("Failed to set temperature (%s).", Helpers::toString(ret)); @@ -995,27 +1107,13 @@ bool SVBONYBase::UpdateCCDFrame(int x, int y, int w, int h) subH -= subH % 2; LOGF_DEBUG("Frame ROI x:%d y:%d w:%d h:%d", subX, subY, subW, subH); - - SVB_ERROR_CODE ret; - - ret = SVBSetROIFormat(mCameraInfo.CameraID, subX, subY, subW, subH, binX); - if (ret != SVB_SUCCESS) + if (false == SetROIFormat(subX, subY, subW, subH, binX)) { - LOGF_ERROR("Failed to set ROI (%s).", Helpers::toString(ret)); return false; } mCurrentVideoFormat = getImageType(); - switch (mCurrentVideoFormat) - { - case SVB_IMG_RAW16: - PrimaryCCD.setBPP(16); - break; - - default: - PrimaryCCD.setBPP(8); - break; - } + PrimaryCCD.setBPP(Helpers::getBPP(mCurrentVideoFormat)); SVBSetOutputImageType(mCameraInfo.CameraID, mCurrentVideoFormat); @@ -1023,7 +1121,7 @@ bool SVBONYBase::UpdateCCDFrame(int x, int y, int w, int h) PrimaryCCD.setFrame(subX * binX, subY * binY, subW * binX, subH * binY); // Total bytes required for image buffer - auto nbuf = (subW * subH * static_cast(PrimaryCCD.getBPP()) / 8) * ((getImageType() == SVB_IMG_RGB24) ? 3 : 1); + auto nbuf = (subW * subH * static_cast(PrimaryCCD.getBPP()) / 8) * (Helpers::getNChannels(getImageType())); LOGF_DEBUG("Setting frame buffer size to %d bytes.", nbuf); PrimaryCCD.setFrameBufferSize(nbuf); @@ -1045,12 +1143,10 @@ bool SVBONYBase::UpdateCCDBin(int binx, int biny) void SVBONYBase::sendImage(SVB_IMG_TYPE type, float duration) { - PrimaryCCD.setNAxis(type == SVB_IMG_RGB24 ? 3 : 2); + PrimaryCCD.setNAxis(Helpers::getNAxis(type)); // If mono camera or we're sending Luma or RGB, turn off bayering - if (mCameraProperty.IsColorCam == false || type >= SVB_IMG_Y8) - SetCCDCapability(GetCCDCapability() & ~CCD_HAS_BAYER); - else + if (Helpers::hasBayer(type)) { SetCCDCapability(GetCCDCapability() | CCD_HAS_BAYER); auto bayerString = getBayerString(); @@ -1061,6 +1157,10 @@ void SVBONYBase::sendImage(SVB_IMG_TYPE type, float duration) IDSetText(&BayerTP, nullptr); } } + else + { + SetCCDCapability(GetCCDCapability() & ~CCD_HAS_BAYER); + } if (duration > VERBOSE_EXPOSURE) LOG_INFO("Download complete."); @@ -1097,6 +1197,10 @@ void SVBONYBase::temperatureTimerTimeout() TemperatureNP.s = newState; TemperatureN[0].value = mCurrentTemperature; IDSetNumber(&TemperatureNP, nullptr); +/* + This log should be commented out except when investigating bugs, etc., as it outputs very frequently. + LOGF_DEBUG("Current Temperature %.2f degree", mCurrentTemperature); +*/ } if (HasCooler()) @@ -1310,9 +1414,9 @@ void SVBONYBase::updateRecorderFormat() Helpers::pixelFormat( mCurrentVideoFormat, mCameraProperty.BayerPattern, - mCameraProperty.IsColorCam - ), - mCurrentVideoFormat == SVB_IMG_RAW16 ? 16 : 8 + Helpers::isColor(mCurrentVideoFormat) + ), + Helpers::getBPP(mCurrentVideoFormat) ); } @@ -1359,4 +1463,4 @@ bool SVBONYBase::saveConfigItems(FILE *fp) bool SVBONYBase::SetCaptureFormat(uint8_t index) { return setVideoFormat(index); -} +} \ No newline at end of file diff --git a/indi-svbony/svbony_helpers.h b/indi-svbony/svbony_helpers.h index 7f5048f4c..5763608b3 100644 --- a/indi-svbony/svbony_helpers.h +++ b/indi-svbony/svbony_helpers.h @@ -45,7 +45,8 @@ const char *toString(SVB_BAYER_PATTERN pattern) case SVB_BAYER_BG: return "BGGR"; case SVB_BAYER_GR: return "GRBG"; case SVB_BAYER_GB: return "GBRG"; - default: return "RGGB"; + case SVB_BAYER_RG: return "RGGB"; + default: return "GRBG"; // default bayer pattern for SVBONY OSC camera. } } @@ -84,8 +85,12 @@ const char *toString(SVB_IMG_TYPE type) switch (type) { case SVB_IMG_RAW8: return "SVB_IMG_RAW8"; - case SVB_IMG_RGB24: return "SVB_IMG_RGB24"; + case SVB_IMG_RAW10: return "SVB_IMG_RAW10"; + case SVB_IMG_RAW12: return "SVB_IMG_RAW12"; + case SVB_IMG_RAW14: return "SVB_IMG_RAW14"; case SVB_IMG_RAW16: return "SVB_IMG_RAW16"; + case SVB_IMG_RGB24: return "SVB_IMG_RGB24"; + case SVB_IMG_RGB32: return "SVB_IMG_RGB32"; case SVB_IMG_Y8: return "SVB_IMG_Y8"; case SVB_IMG_Y16: return "SVB_IMG_Y16"; case SVB_IMG_END: return "SVB_IMG_END"; @@ -98,15 +103,26 @@ const char *toPrettyString(SVB_IMG_TYPE type) switch (type) { case SVB_IMG_RAW8: return "Raw 8 bit"; - case SVB_IMG_RGB24: return "RGB 24"; + case SVB_IMG_RAW10: return "Raw 10 bit"; + case SVB_IMG_RAW12: return "Raw 12 bit"; + case SVB_IMG_RAW14: return "Raw 14 bit"; case SVB_IMG_RAW16: return "Raw 16 bit"; case SVB_IMG_Y8: return "Luma 8 bit"; case SVB_IMG_Y16: return "Luma 16 bit"; + case SVB_IMG_RGB24: return "RGB 24"; + case SVB_IMG_RGB32: return "RGB 32"; case SVB_IMG_END: return "END"; default: return "UNKNOWN"; } } +/* + Determine the arguments in the following order: + isColor: not color -> mono + type: RGB* -> rgb, Y* -> mono + pattern: bayer pattern + other -> mono +*/ INDI_PIXEL_FORMAT pixelFormat(SVB_IMG_TYPE type, SVB_BAYER_PATTERN pattern, bool isColor) { if (isColor == false) @@ -115,8 +131,9 @@ INDI_PIXEL_FORMAT pixelFormat(SVB_IMG_TYPE type, SVB_BAYER_PATTERN pattern, bool switch (type) { case SVB_IMG_RGB24: return INDI_RGB; + case SVB_IMG_RGB32: return INDI_RGB; case SVB_IMG_Y8: return INDI_MONO; - case SVB_IMG_Y16: return INDI_MONO; + case SVB_IMG_Y16: return INDI_MONO; default:; // see below } @@ -131,4 +148,102 @@ INDI_PIXEL_FORMAT pixelFormat(SVB_IMG_TYPE type, SVB_BAYER_PATTERN pattern, bool return INDI_MONO; } +int getBPP(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: return 8; + case SVB_IMG_RAW10: return 16; + case SVB_IMG_RAW12: return 16; + case SVB_IMG_RAW14: return 16; + case SVB_IMG_RAW16: return 16; + case SVB_IMG_RGB24: return 8; + case SVB_IMG_RGB32: return 8; + case SVB_IMG_Y8: return 8; + case SVB_IMG_Y16: return 16; + case SVB_IMG_END: return 8; + default: return 8; + } +} + +int getNChannels(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: return 1; + case SVB_IMG_RAW10: return 1; + case SVB_IMG_RAW12: return 1; + case SVB_IMG_RAW14: return 1; + case SVB_IMG_RAW16: return 1; + case SVB_IMG_RGB24: return 3; + case SVB_IMG_RGB32: return 4; + case SVB_IMG_Y8: return 1; + case SVB_IMG_Y16: return 1; + case SVB_IMG_END: return 1; + default: return 1; + } +} + +int getNAxis(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: return 2; + case SVB_IMG_RAW10: return 2; + case SVB_IMG_RAW12: return 2; + case SVB_IMG_RAW14: return 2; + case SVB_IMG_RAW16: return 2; + case SVB_IMG_RGB24: return 3; + case SVB_IMG_RGB32: return 3; + case SVB_IMG_Y8: return 2; + case SVB_IMG_Y16: return 2; + case SVB_IMG_END: return 2; + default: return 2; + } +} + +bool isRGB(SVB_IMG_TYPE type) +{ + return (type == SVB_IMG_RGB24) || (type == SVB_IMG_RGB32); +} + +bool isColor(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: + case SVB_IMG_RAW10: + case SVB_IMG_RAW12: + case SVB_IMG_RAW14: + case SVB_IMG_RAW16: + case SVB_IMG_RGB24: + case SVB_IMG_RGB32: + return true; + case SVB_IMG_Y8: + case SVB_IMG_Y16: + default: + return false; + } +} + +bool hasBayer(SVB_IMG_TYPE type) +{ + switch (type) + { + case SVB_IMG_RAW8: + case SVB_IMG_RAW10: + case SVB_IMG_RAW12: + case SVB_IMG_RAW14: + case SVB_IMG_RAW16: + return true; + case SVB_IMG_RGB24: + case SVB_IMG_RGB32: + case SVB_IMG_Y8: + case SVB_IMG_Y16: + default: + return false; + } +} + } + From 891b384aa0e731dd91cd5144b116a600030bd02b Mon Sep 17 00:00:00 2001 From: Tetsuya Kakura Date: Sun, 15 Oct 2023 00:46:30 +0900 Subject: [PATCH 7/7] Fixed some bugs and added workaround code for SVBONY Camera SDK - Changed the SDK to set the ROI only if it is different from the current ROI when setting the ROI - Added code to call SVBSetCameraMode function in SVBONYBase::Connect(). This is a workaround code for a bug in the SDK. - Fixed parameter for setting target cooling temperature in SVB_TARGET_TEMPERATURE --- indi-svbony/svbony_base.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indi-svbony/svbony_base.h b/indi-svbony/svbony_base.h index 22acb6779..51cc986bd 100644 --- a/indi-svbony/svbony_base.h +++ b/indi-svbony/svbony_base.h @@ -84,6 +84,9 @@ class SVBONYBase : public INDI::CCD /** Get the current Bayer string used */ const char *getBayerString() const; + // Set ROI and Binning + bool SetROIFormat(int x, int y, int w, int h, int bin); + protected: INDI::SingleThreadPool mWorker; void workerStreamVideo(const std::atomic_bool &isAboutToQuit);