bool QHYCCD::StartExposure(float duration) { unsigned int ret = QHYCCD_ERROR; if (Streamer->isBusy()) { DEBUG(INDI::Logger::DBG_ERROR, "Cannot take exposure while streaming/recording is active."); return false; } //AbortPrimaryFrame = false; /* if (duration < MINIMUM_CCD_EXPOSURE) { DEBUGF(INDI::Logger::DBG_WARNING, "Exposure shorter than minimum duration %g s requested. Setting exposure time to %g s.", duration, MINIMUM_CCD_EXPOSURE); duration = MINIMUM_CCD_EXPOSURE; }*/ imageFrameType = PrimaryCCD.getFrameType(); /*if (imageFrameType == CCDChip::BIAS_FRAME) { duration = MINIMUM_CCD_EXPOSURE; DEBUGF(INDI::Logger::DBG_SESSION, "Bias Frame (s) : %g", duration); } else*/ if (GetCCDCapability() & CCD_HAS_SHUTTER) { if (imageFrameType == INDI::CCDChip::DARK_FRAME || imageFrameType == INDI::CCDChip::BIAS_FRAME) ControlQHYCCDShutter(camhandle, MACHANICALSHUTTER_CLOSE); else ControlQHYCCDShutter(camhandle, MACHANICALSHUTTER_FREE); } DEBUGF(INDI::Logger::DBG_DEBUG, "Current exposure time is %f us", duration * 1000 * 1000); ExposureRequest = duration; PrimaryCCD.setExposureDuration(duration); if (sim) ret = QHYCCD_SUCCESS; else { if (LastExposureRequest != ExposureRequest) { ret = SetQHYCCDParam(camhandle, CONTROL_EXPOSURE, ExposureRequest * 1000 * 1000); if (ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_ERROR, "Set expose time failed (%d).", ret); return false; } LastExposureRequest = ExposureRequest; } } if (sim) ret = QHYCCD_SUCCESS; else ret = SetQHYCCDBinMode(camhandle, camxbin, camybin); if (ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_SESSION, "Set QHYCCD Bin mode failed (%d)", ret); return false; } DEBUGF(INDI::Logger::DBG_DEBUG, "SetQHYCCDBinMode (%dx%d).", camxbin, camybin); if (sim) ret = QHYCCD_SUCCESS; else ret = SetQHYCCDResolution(camhandle, camroix, camroiy, camroiwidth, camroiheight); if (ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_SESSION, "Set QHYCCD ROI resolution (%d,%d) (%d,%d) failed (%d)", camroix, camroiy, camroiwidth, camroiheight, ret); return false; } DEBUGF(INDI::Logger::DBG_DEBUG, "SetQHYCCDResolution camroix %d camroiy %d camroiwidth %d camroiheight %d", camroix, camroiy, camroiwidth, camroiheight); if (sim) ret = QHYCCD_SUCCESS; else ret = ExpQHYCCDSingleFrame(camhandle); if (ret == QHYCCD_ERROR) { DEBUGF(INDI::Logger::DBG_SESSION, "Begin QHYCCD expose failed (%d)", ret); return false; } gettimeofday(&ExpStart, NULL); DEBUGF(INDI::Logger::DBG_DEBUG, "Taking a %g seconds frame...", ExposureRequest); InExposure = true; // if (ExposureRequest*1000 < POLLMS) // SetTimer(ExposureRequest*1000); // else SetTimer(POLLMS); return true; }
bool Camera_QHY::Capture(int duration, usImage& img, int options, const wxRect& subframe) { bool useSubframe = UseSubframes && !subframe.IsEmpty(); if (Binning != m_curBin) { FullSize = wxSize(m_maxSize.GetX() / Binning, m_maxSize.GetY() / Binning); m_curBin = Binning; useSubframe = false; // subframe may be out of bounds now } if (img.Init(FullSize)) { DisconnectWithAlert(CAPT_FAIL_MEMORY); return true; } wxRect frame = useSubframe ? subframe : wxRect(FullSize); if (useSubframe) img.Clear(); wxRect roi; if (useSubframe) { // Use a larger ROI around the subframe to avoid changing the ROI as the centroid // wobbles around. Changing the ROI introduces a lag of several seconds. // This also satifies the constraint that ROI width and height must be multiples of 4. enum { PAD = 1 << 5 }; roi.SetLeft(round_down(subframe.GetLeft(), PAD)); roi.SetRight(round_up(subframe.GetRight() + 1, PAD) - 1); roi.SetTop(round_down(subframe.GetTop(), PAD)); roi.SetBottom(round_up(subframe.GetBottom() + 1, PAD) - 1); } else { roi = frame; } uint32_t ret = QHYCCD_ERROR; // lzr from QHY says this needs to be set for every exposure ret = SetQHYCCDBinMode(m_camhandle, Binning, Binning); if (ret != QHYCCD_SUCCESS) { Debug.Write(wxString::Format("SetQHYCCDBinMode failed! ret = %d\n", (int)ret)); } if (m_roi != roi) { // when roi changes, must call this ret = CancelQHYCCDExposingAndReadout(m_camhandle); if (ret == QHYCCD_SUCCESS) { Debug.Write("CancelQHYCCDExposingAndReadout success\n"); } else { Debug.Write("CancelQHYCCDExposingAndReadout failed\n"); } ret = SetQHYCCDResolution(m_camhandle, roi.GetLeft(), roi.GetTop(), roi.GetWidth(), roi.GetHeight()); if (ret == QHYCCD_SUCCESS) { m_roi = roi; } else { Debug.Write(wxString::Format("SetQHYCCDResolution(%d,%d,%d,%d) failed! ret = %d\n", roi.GetLeft(), roi.GetTop(), roi.GetWidth(), roi.GetHeight(), (int)ret)); } } if (duration != m_curExposure) { ret = SetQHYCCDParam(m_camhandle, CONTROL_EXPOSURE, duration * 1000.0); // QHY duration is usec if (ret == QHYCCD_SUCCESS) { m_curExposure = duration; } else { Debug.Write(wxString::Format("QHY set exposure ret %d\n", (int)ret)); pFrame->Alert(_("Failed to set camera exposure")); } } if (GuideCameraGain != m_curGain) { double gain = m_gainMin + GuideCameraGain * (m_gainMax - m_gainMin) / 100.0; gain = floor(gain / m_gainStep) * m_gainStep; Debug.Write(wxString::Format("QHY set gain %g (%g..%g incr %g)\n", gain, m_gainMin, m_gainMax, m_gainStep)); ret = SetQHYCCDParam(m_camhandle, CONTROL_GAIN, gain); if (ret == QHYCCD_SUCCESS) { m_curGain = GuideCameraGain; } else { Debug.Write(wxString::Format("QHY set gain ret %d\n", (int)ret)); pFrame->Alert(_("Failed to set camera gain")); } } ret = ExpQHYCCDSingleFrame(m_camhandle); if (ret == QHYCCD_ERROR) { Debug.Write(wxString::Format("QHY exp single frame ret %d\n", (int)ret)); DisconnectWithAlert(_("QHY exposure failed"), NO_RECONNECT); return true; } if (ret == QHYCCD_SUCCESS) { Debug.Write(wxString::Format("QHY: 200ms delay needed\n")); WorkerThread::MilliSleep(200); } if (ret == QHYCCD_READ_DIRECTLY) { //Debug.Write("QHYCCD_READ_DIRECTLY\n"); } uint32_t w, h, bpp, channels; ret = GetQHYCCDSingleFrame(m_camhandle, &w, &h, &bpp, &channels, RawBuffer); if (ret != QHYCCD_SUCCESS || (bpp != 8 && bpp != 16)) { Debug.Write(wxString::Format("QHY get single frame ret %d bpp %u\n", ret, bpp)); // users report that reconnecting the camera after this failure allows // them to resume guiding so we'll try to reconnect automatically DisconnectWithAlert(_("QHY get frame failed"), RECONNECT); return true; } if (useSubframe) { img.Subframe = frame; int xofs = subframe.GetLeft() - roi.GetLeft(); int yofs = subframe.GetTop() - roi.GetTop(); int dxr = w - frame.width - xofs; if (bpp == 8) { const unsigned char *src = RawBuffer + yofs * w; unsigned short *dst = img.ImageData + frame.GetTop() * FullSize.GetWidth() + frame.GetLeft(); for (int y = 0; y < frame.height; y++) { unsigned short *d = dst; src += xofs; for (int x = 0; x < frame.width; x++) *d++ = (unsigned short) *src++; src += dxr; dst += FullSize.GetWidth(); } } else // bpp == 16 { const unsigned short *src = (const unsigned short *) RawBuffer + yofs * w; unsigned short *dst = img.ImageData + frame.GetTop() * FullSize.GetWidth() + frame.GetLeft(); for (int y = 0; y < frame.height; y++) { src += xofs; memcpy(dst, src, frame.width * sizeof(unsigned short)); src += frame.width + dxr; dst += FullSize.GetWidth(); } } } else { if (bpp == 8) { const unsigned char *src = RawBuffer; unsigned short *dst = img.ImageData; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { *dst++ = (unsigned short) *src++; } } } else // bpp == 16 { memcpy(img.ImageData, RawBuffer, w * h * sizeof(unsigned short)); } } if (options & CAPTURE_SUBTRACT_DARK) SubtractDark(img); if (Color && Binning == 1 && (options & CAPTURE_RECON)) QuickLRecon(img); return false; }
bool QHYCCD::StartExposure(float duration) { int ret = QHYCCD_ERROR; #ifndef OSX_EMBEDED_MODE if (streamer->isBusy()) { DEBUG(INDI::Logger::DBG_ERROR, "Cannot take exposure while streaming/recording is active."); return false; } #endif //AbortPrimaryFrame = false; if (duration < MINIMUM_CCD_EXPOSURE) { DEBUGF(INDI::Logger::DBG_WARNING, "Exposure shorter than minimum duration %g s requested. Setting exposure time to %g s.", duration, MINIMUM_CCD_EXPOSURE); duration = MINIMUM_CCD_EXPOSURE; } imageFrameType = PrimaryCCD.getFrameType(); if (imageFrameType == CCDChip::BIAS_FRAME) { duration = MINIMUM_CCD_EXPOSURE; DEBUGF(INDI::Logger::DBG_SESSION, "Bias Frame (s) : %g", duration); } else if(imageFrameType == CCDChip::DARK_FRAME) { ControlQHYCCDShutter(camhandle,MACHANICALSHUTTER_CLOSE); } else { ControlQHYCCDShutter(camhandle,MACHANICALSHUTTER_FREE); } DEBUGF(INDI::Logger::DBG_DEBUG, "Current exposure time is %f us",duration * 1000 * 1000); ExposureRequest = duration; PrimaryCCD.setExposureDuration(duration); if (sim) ret = QHYCCD_SUCCESS; else ret = SetQHYCCDParam(camhandle,CONTROL_EXPOSURE,ExposureRequest * 1000 * 1000); if(ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_ERROR, "Set expose time failed (%d).", ret); return false; } // lzr: we need to call the following apis every single exposure,the usleep(200000) is important if (sim) ret = QHYCCD_SUCCESS; else ret = SetQHYCCDBinMode(camhandle,camxbin,camybin); if(ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_SESSION, "Set QHYCCD Bin mode failed (%d)", ret); return false; } DEBUGF(INDI::Logger::DBG_DEBUG, "SetQHYCCDBinMode %dx%d", camxbin, camybin); if (sim) ret = QHYCCD_SUCCESS; else ret = SetQHYCCDResolution(camhandle,camroix,camroiy,camroiwidth,camroiheight); if(ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_SESSION, "Set QHYCCD ROI resolution failed (%d)", ret); return false; } DEBUGF(INDI::Logger::DBG_DEBUG, "SetQHYCCDResolution camroix %d camroiy %d camroiwidth %d camroiheight %d", camroix,camroiy,camroiwidth,camroiheight); // JM 2016-05-08: Some QHY cameras needs 200ms before you can exposure a frame. Asked QHY to try to minimize this! //usleep(200000); if (sim) ret = QHYCCD_SUCCESS; else ret = ExpQHYCCDSingleFrame(camhandle); if(ret == QHYCCD_ERROR) { DEBUGF(INDI::Logger::DBG_SESSION, "Begin QHYCCD expose failed (%d)", ret); return false; } gettimeofday(&ExpStart, NULL); DEBUGF(INDI::Logger::DBG_DEBUG, "Taking a %g seconds frame...", ExposureRequest); InExposure = true; // if (ExposureRequest*1000 < POLLMS) // SetTimer(ExposureRequest*1000); // else SetTimer(POLLMS); return true; }
bool Camera_QHY::Connect(const wxString& camId) { if (QHYSDKInit()) { wxMessageBox(_("Failed to initialize QHY SDK")); return true; } int num_cams = ScanQHYCCD(); std::vector<std::string> camids; for (int i = 0; i < num_cams; i++) { char camid[32] = ""; GetQHYCCDId(i, camid); bool st4 = false; qhyccd_handle *h = OpenQHYCCD(camid); if (h) { uint32_t ret = IsQHYCCDControlAvailable(h, CONTROL_ST4PORT); if (ret == QHYCCD_SUCCESS) st4 = true; //CloseQHYCCD(h); // CloseQHYCCD() would proform a reset, so the other software that use QHY camera would be impacted. // Do not call this,would not cause memory leak.The SDk has already process this. } Debug.Write(wxString::Format("QHY cam [%d] %s avail %s st4 %s\n", i, camid, h ? "Yes" : "No", st4 ? "Yes" : "No")); if (st4) camids.push_back(camid); } if (camids.size() == 0) { wxMessageBox(_("No compatible QHY cameras found")); return true; } std::string camid; if (camids.size() > 1) { wxArrayString names; int n = 1; for (auto it = camids.begin(); it != camids.end(); ++it, ++n) names.Add(wxString::Format("%d: %s", n, *it)); int i = wxGetSingleChoiceIndex(_("Select QHY camera"), _("Camera choice"), names); if (i == -1) return true; camid = camids[i]; } else camid = camids[0]; char *s = new char[camid.length() + 1]; memcpy(s, camid.c_str(), camid.length() + 1); m_camhandle = OpenQHYCCD(s); delete[] s; Name = camid; if (!m_camhandle) { wxMessageBox(_("Failed to connect to camera")); return true; } // before calling InitQHYCCD() we must call SetQHYCCDStreamMode(camhandle, 0 or 1) // 0: single frame mode // 1: live frame mode uint32_t ret = SetQHYCCDStreamMode(m_camhandle, 0); if (ret != QHYCCD_SUCCESS) { CloseQHYCCD(m_camhandle); m_camhandle = 0; wxMessageBox(_("SetQHYCCDStreamMode failed")); return true; } ret = InitQHYCCD(m_camhandle); if (ret != QHYCCD_SUCCESS) { CloseQHYCCD(m_camhandle); m_camhandle = 0; wxMessageBox(_("Init camera failed")); return true; } ret = GetQHYCCDParamMinMaxStep(m_camhandle, CONTROL_GAIN, &m_gainMin, &m_gainMax, &m_gainStep); if (ret != QHYCCD_SUCCESS) { CloseQHYCCD(m_camhandle); m_camhandle = 0; wxMessageBox(_("Failed to get gain range")); return true; } double chipw, chiph, pixelw, pixelh; uint32_t imagew, imageh, bpp; ret = GetQHYCCDChipInfo(m_camhandle, &chipw, &chiph, &imagew, &imageh, &pixelw, &pixelh, &bpp); if (ret != QHYCCD_SUCCESS) { CloseQHYCCD(m_camhandle); m_camhandle = 0; wxMessageBox(_("Failed to get camera chip info")); return true; } int bayer = IsQHYCCDControlAvailable(m_camhandle, CAM_COLOR); Debug.Write(wxString::Format("QHY: cam reports bayer type %d\n", bayer)); Color = false; switch ((BAYER_ID)bayer) { case BAYER_GB: case BAYER_GR: case BAYER_BG: case BAYER_RG: Color = true; } // check bin modes CONTROL_ID modes[] = { CAM_BIN2X2MODE, CAM_BIN3X3MODE, CAM_BIN4X4MODE, }; int bin[] = { 2, 3, 4, }; int maxBin = 1; for (int i = 0; i < WXSIZEOF(modes); i++) { ret = IsQHYCCDControlAvailable(m_camhandle, modes[i]); #if 0 // FIXME- IsQHYCCDControlAvailable is supposed to return QHYCCD_ERROR_NOTSUPPORT for a // bin mode that is not supported, but in fact it returns QHYCCD_ERROR, so we cannot // distinguish "not supported" from "error". if (ret != QHYCCD_SUCCESS && ret != QHYCCD_ERROR_NOTSUPPORT) { CloseQHYCCD(m_camhandle); m_camhandle = 0; wxMessageBox(_("Failed to get camera bin info")); return true; } #endif if (ret == QHYCCD_SUCCESS) maxBin = bin[i]; else break; } Debug.Write(wxString::Format("QHY: max binning = %d\n", maxBin)); MaxBinning = maxBin; if (Binning > MaxBinning) Binning = MaxBinning; Debug.Write(wxString::Format("QHY: call SetQHYCCDBinMode bin = %d\n", Binning)); ret = SetQHYCCDBinMode(m_camhandle, Binning, Binning); if (ret != QHYCCD_SUCCESS) { CloseQHYCCD(m_camhandle); m_camhandle = 0; wxMessageBox(_("Failed to set camera binning")); return true; } m_curBin = Binning; m_maxSize = wxSize(imagew, imageh); FullSize = wxSize(imagew / Binning, imageh / Binning); delete[] RawBuffer; size_t size = GetQHYCCDMemLength(m_camhandle); RawBuffer = new unsigned char[size]; m_devicePixelSize = sqrt(pixelw * pixelh); m_curGain = -1; m_curExposure = -1; m_roi = wxRect(0, 0, FullSize.GetWidth(), FullSize.GetHeight()); // binned coordinates Debug.Write(wxString::Format("QHY: call SetQHYCCDResolution roi = %d,%d\n", m_roi.width, m_roi.height)); ret = SetQHYCCDResolution(m_camhandle, 0, 0, m_roi.GetWidth(), m_roi.GetHeight()); if (ret != QHYCCD_SUCCESS) { CloseQHYCCD(m_camhandle); m_camhandle = 0; wxMessageBox(_("Init camera failed")); return true; } Debug.Write(wxString::Format("QHY: connect done\n")); Connected = true; return false; }
bool QHYCCD::StartExposure(float duration) { int ret = QHYCCD_ERROR; if (streamer->isBusy()) { DEBUG(INDI::Logger::DBG_ERROR, "Cannot take exposure while streaming/recording is active."); return false; } //AbortPrimaryFrame = false; if (duration < MINIMUM_CCD_EXPOSURE) { DEBUGF(INDI::Logger::DBG_WARNING, "Exposure shorter than minimum duration %g s requested. Setting exposure time to %g s.", duration, MINIMUM_CCD_EXPOSURE); duration = MINIMUM_CCD_EXPOSURE; } imageFrameType = PrimaryCCD.getFrameType(); if (imageFrameType == CCDChip::BIAS_FRAME) { duration = MINIMUM_CCD_EXPOSURE; DEBUGF(INDI::Logger::DBG_SESSION, "Bias Frame (s) : %g", duration); } else if(imageFrameType == CCDChip::DARK_FRAME) { ControlQHYCCDShutter(camhandle,MACHANICALSHUTTER_CLOSE); } else { ControlQHYCCDShutter(camhandle,MACHANICALSHUTTER_FREE); } DEBUGF(INDI::Logger::DBG_DEBUG, "Current exposure time is %f us",duration * 1000 * 1000); ExposureRequest = duration; PrimaryCCD.setExposureDuration(duration); if (sim) ret = QHYCCD_SUCCESS; else ret = SetQHYCCDParam(camhandle,CONTROL_EXPOSURE,ExposureRequest * 1000 * 1000); if(ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_ERROR, "Set expose time failed (%d).", ret); return false; } // lzr: we need to call the following apis every single exposure,the usleep(200000) is important if (sim) ret = QHYCCD_SUCCESS; else ret = SetQHYCCDBinMode(camhandle,camxbin,camybin); if(ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_SESSION, "Set QHYCCD Bin mode failed (%d)", ret); return false; } DEBUGF(INDI::Logger::DBG_DEBUG, "SetQHYCCDBinMode %dx%d", camxbin, camybin); if (sim) ret = QHYCCD_SUCCESS; else ret = SetQHYCCDResolution(camhandle,camroix,camroiy,camroiwidth,camroiheight); if(ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_SESSION, "Set QHYCCD ROI resolution failed (%d)", ret); return false; } DEBUGF(INDI::Logger::DBG_DEBUG, "SetQHYCCDResolution camroix %d camroiy %d camroiwidth %d camroiheight %d", camroix,camroiy,camroiwidth,camroiheight); // Jasem: Removed QHY 300ms delay that was added without specifying the reason. It seems any delay less than 100ms results in QHY Frame error. Again no reason. This renders // exposures less than 100ms useless, but there is nothing I can do about that. usleep(100000); if (sim) ret = QHYCCD_SUCCESS; else ret = ExpQHYCCDSingleFrame(camhandle); if(ret != QHYCCD_SUCCESS) { DEBUGF(INDI::Logger::DBG_SESSION, "Begin QHYCCD expose failed (%d)", ret); return false; } gettimeofday(&ExpStart, NULL); DEBUGF(INDI::Logger::DBG_DEBUG, "Taking a %g seconds frame...", ExposureRequest); InExposure = true; // if (ExposureRequest*1000 < POLLMS) // SetTimer(ExposureRequest*1000); // else SetTimer(POLLMS); return true; }