Skip to content

Commit

Permalink
Stabilization of SVBONYBase::workerExposure (#845)
Browse files Browse the repository at this point in the history
  • Loading branch information
jctk authored Oct 1, 2023
1 parent 913741f commit e940fd9
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 109 deletions.
192 changes: 86 additions & 106 deletions indi-svbony/svbony_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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<std::mutex> 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<uint8_t *>(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;
}

Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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<std::mutex> 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<uint8_t *>(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);
Expand Down Expand Up @@ -1377,4 +1357,4 @@ bool SVBONYBase::saveConfigItems(FILE *fp)
bool SVBONYBase::SetCaptureFormat(uint8_t index)
{
return setVideoFormat(index);
}
}
3 changes: 1 addition & 2 deletions indi-svbony/svbony_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 4 additions & 1 deletion indi-svbony/svbony_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand All @@ -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";
}
Expand All @@ -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
}

Expand Down

0 comments on commit e940fd9

Please sign in to comment.