diff --git a/kstars/ekos/guide/externalguide/phd2.h b/kstars/ekos/guide/externalguide/phd2.h --- a/kstars/ekos/guide/externalguide/phd2.h +++ b/kstars/ekos/guide/externalguide/phd2.h @@ -91,7 +91,7 @@ enum PHD2ResultType { NO_RESULT, - //capture_single_frame + CAPTURE_SINGLE_FRAME, //capture_single_frame CLEAR_CALIBRATION_COMMAND_RECEIVED, //clear_calibration DITHER_COMMAND_RECEIVED, //dither //find_star @@ -103,11 +103,11 @@ //get_calibration_data IS_EQUIPMENT_CONNECTED, //get_connected //get_cooler_status - //get_current_equipment + GET_CURRENT_EQUIPMENT, //get_current_equipment DEC_GUIDE_MODE, //get_dec_guide_mode EXPOSURE_TIME, //get_exposure EXPOSURE_DURATIONS, //get_exposure_durations - //get_lock_position + LOCK_POSITION, //get_lock_position //get_lock_shift_enabled //get_lock_shift_params //get_paused @@ -120,13 +120,13 @@ //get_use_subframes GUIDE_COMMAND_RECEIVED, //guide //guide_pulse - //loop + LOOP, //loop //save_image //set_algo_param CONNECTION_RESULT, //set_connected SET_DEC_GUIDE_MODE_COMMAND_RECEIVED, //set_dec_guide_mode SET_EXPOSURE_COMMAND_RECEIVED, //set_exposure - //set_lock_position + SET_LOCK_POSITION, //set_lock_position //set_lock_shift_enabled //set_lock_shift_params SET_PAUSED_COMMAND_RECEIVED, //set_paused @@ -148,7 +148,7 @@ //These are the PHD2 Methods. Only some are implemented in Ekos. - //capture_single_frame + void captureSingleFrame(); //capture_single_frame bool clearCalibration() override; //clear_calibration bool dither(double pixels) override; //dither //find_star @@ -160,11 +160,11 @@ //get_calibration_data void checkIfEquipmentConnected(); //get_connected //get_cooler_status - //get_current_equipment + void requestCurrentEquipmentUpdate(); //get_current_equipment void checkDEGuideMode(); //get_dec_guide_mode void requestExposureTime(); //get_exposure void requestExposureDurations(); //get_exposure_durations - //get_lock_position + void requestLockPosition(); //get_lock_position //get_lock_shift_enabled //get_lock_shift_params //get_paused @@ -177,13 +177,13 @@ //get_use_subframes bool guide() override; //guide //guide_pulse - //loop + void loop(); //loop //save_image //set_algo_param void connectEquipment(bool enable);//set_connected void requestSetDEGuideMode(bool deEnabled, bool nEnabled, bool sEnabled); //set_dec_guide_mode void requestSetExposureTime(int time); //set_exposure - //set_lock_position + void setLockPosition(double x, double y); //set_lock_position //set_lock_shift_enabled //set_lock_shift_params bool suspend() override; //set_paused @@ -195,6 +195,10 @@ bool calibrate() override; //Note PHD2 does not have a separate calibrate command. This is unused. void setGuideView(FITSView *guideView); + QString getCurrentCamera(){ return currentCamera; } + QString getCurrentMount(){ return currentMount; } + QString getCurrentAuxMount(){ return currentAuxMount; } + private slots: void readPHD2(); @@ -253,6 +257,11 @@ double pixelScale = 0; QString logValidExposureTimes; + + QString currentCamera; + QString currentMount; + QString currentAuxMount; + }; } diff --git a/kstars/ekos/guide/externalguide/phd2.cpp b/kstars/ekos/guide/externalguide/phd2.cpp --- a/kstars/ekos/guide/externalguide/phd2.cpp +++ b/kstars/ekos/guide/externalguide/phd2.cpp @@ -79,11 +79,11 @@ //get_calibration_data methodResults["get_connected"] = IS_EQUIPMENT_CONNECTED; //get_cooler_status - //get_current_equipment + methodResults["get_current_equipment"] = GET_CURRENT_EQUIPMENT; methodResults["get_dec_guide_mode"] = DEC_GUIDE_MODE; methodResults["get_exposure"] = EXPOSURE_TIME; methodResults["get_exposure_durations"] = EXPOSURE_DURATIONS; - //get_lock_position + methodResults["get_lock_position"] = LOCK_POSITION; //get_lock_shift_enabled //get_lock_shift_params //get_paused @@ -96,13 +96,13 @@ //get_use_subframes methodResults["guide"] = GUIDE_COMMAND_RECEIVED; //guide_pulse - //loop + methodResults["loop"] = LOOP; //save_image //set_algo_param methodResults["set_connected"] = CONNECTION_RESULT; methodResults["set_dec_guide_mode"] = SET_DEC_GUIDE_MODE_COMMAND_RECEIVED; methodResults["set_exposure"] = SET_EXPOSURE_COMMAND_RECEIVED; - //set_lock_position + methodResults["set_lock_position"] = SET_LOCK_POSITION; //set_lock_shift_enabled //set_lock_shift_params methodResults["set_paused"] = SET_PAUSED_COMMAND_RECEIVED; @@ -284,6 +284,7 @@ case StartGuiding: setEquipmentConnected(); updateGuideParameters(); + requestCurrentEquipmentUpdate(); // Do not report guiding as started because it will start scheduled capture before guiding is settled // just print the log message and GUIDE_STARTED status will be set in SettleDone // phd2 will always send SettleDone event @@ -473,8 +474,13 @@ emit newAxisSigma(sqrt(total_sqr_RA_error / errorLog.size()), sqrt(total_sqr_DE_error / errorLog.size())); } - - requestStarImage(32); //This requests a star image for the guide view. 32 x 32 pixels + //Note that if it is receiving full size remote images, it should not get the guide star image. + //But if it is not getting the full size images, it should get the guide star image + //If we are getting the full size image, we will want to know the lock position for the image that loads in the viewer. + if (Options::guideRemoteImagesEnabled()) + requestLockPosition(); + else + requestStarImage(32); //This requests a star image for the guide view. 32 x 32 pixels } break; @@ -542,7 +548,9 @@ //Ekos didn't ask for this result? break; - //capture_single_frame + case CAPTURE_SINGLE_FRAME: //capture_single_frame + break; + case CLEAR_CALIBRATION_COMMAND_RECEIVED: //clear_calibration break; @@ -574,7 +582,18 @@ break; //get_cooler_status - //get_current_equipment + case GET_CURRENT_EQUIPMENT: //get_current_equipment + { + QJsonObject equipObject = jsonObj["result"].toObject(); + currentCamera = equipObject["camera"].toObject()["name"].toString(); + currentMount = equipObject["mount"].toObject()["name"].toString(); + currentAuxMount = equipObject["aux_mount"].toObject()["name"].toString(); + + emit guideEquipmentUpdated(); + + break; + } + case DEC_GUIDE_MODE: //get_dec_guide_mode { @@ -606,7 +625,21 @@ emit newLog(logValidExposureTimes); break; } - //get_lock_position + case LOCK_POSITION: //get_lock_position + { + if(jsonObj["result"].toArray().count()==2) + { + double x = jsonObj["result"].toArray().at(0).toDouble(); + double y = jsonObj["result"].toArray().at(1).toDouble(); + QVector3D newStarCenter(x, y, 0); + emit newStarPosition(newStarCenter, true); + + //This is needed so that PHD2 sends the new star pixmap when + //remote images are enabled. + emit newStarPixmap(guideFrame->getTrackingBoxPixmap()); + } + break; + } //get_lock_shift_enabled //get_lock_shift_params //get_paused @@ -636,8 +669,12 @@ case GUIDE_COMMAND_RECEIVED: //guide break; + //guide_pulse - //loop + + case LOOP: //loop + break; + //save_image //set_algo_param @@ -653,7 +690,9 @@ requestExposureTime(); //This will check what it was set to and print a message as to what it is. break; - //set_lock_position + case SET_LOCK_POSITION: //set_lock_position + break; + //set_lock_shift_enabled //set_lock_shift_params @@ -824,6 +863,7 @@ emit newStatus(Ekos::GUIDE_CONNECTED); updateGuideParameters(); requestExposureDurations(); + requestCurrentEquipmentUpdate(); } } @@ -838,6 +878,10 @@ //This section handles the methods/requests sent to PHD2, some are not implemented. //capture_single_frame +void PHD2::captureSingleFrame() +{ + sendPHD2Request("capture_single_frame"); +} //clear_calibration bool PHD2::clearCalibration() @@ -931,6 +975,10 @@ //get_cooler_status //get_current_equipment +void PHD2::requestCurrentEquipmentUpdate() +{ + sendPHD2Request("get_current_equipment"); +} //get_dec_guide_mode void PHD2::checkDEGuideMode() @@ -951,6 +999,10 @@ } //get_lock_position +void PHD2::requestLockPosition() +{ + sendPHD2Request("get_lock_position"); +} //get_lock_shift_enabled //get_lock_shift_params //get_paused @@ -969,9 +1021,6 @@ //get_star_image void PHD2::requestStarImage(int size) { - // if (!Options::guideRemoteImagesEnabled()) - // return; - if (starImageRequested) { if (Options::verboseLogging()) @@ -1027,6 +1076,10 @@ //guide_pulse //loop +void PHD2::loop() +{ + sendPHD2Request("loop"); +} //save_image //set_algo_param @@ -1093,6 +1146,13 @@ } //set_lock_position +void PHD2::setLockPosition(double x,double y) +{ + // Note: false will mean if a guide star is near the coordinates, it will use that. + QJsonArray args; + args << x << y << false; + sendPHD2Request("set_lock_position", args); +} //set_lock_shift_enabled //set_lock_shift_params diff --git a/kstars/ekos/guide/guide.h b/kstars/ekos/guide/guide.h --- a/kstars/ekos/guide/guide.h +++ b/kstars/ekos/guide/guide.h @@ -193,6 +193,7 @@ /** @}*/ void addCamera(ISD::GDInterface *newCCD); + void configurePHD2Camera(); void setTelescope(ISD::GDInterface *newTelescope); void addST4(ISD::ST4 *setST4); void setAO(ISD::ST4 *newAO); diff --git a/kstars/ekos/guide/guide.cpp b/kstars/ekos/guide/guide.cpp --- a/kstars/ekos/guide/guide.cpp +++ b/kstars/ekos/guide/guide.cpp @@ -115,6 +115,7 @@ connect(opsGuide, &OpsGuide::settingsUpdated, [this]() { onThresholdChanged(Options::guideAlgorithm()); + configurePHD2Camera(); }); page = dialog->addPage(opsGuide, i18n("Guide")); @@ -544,29 +545,87 @@ if (CCDs.contains(ccd)) return; - if (guiderType != GUIDE_INTERNAL) + if (guiderType == GUIDE_LINGUIDER) { connect(ccd, &ISD::CCD::newBLOBManager, [ccd](INDI::Property * prop) { if (!strcmp(prop->getName(), "CCD1") || !strcmp(prop->getName(), "CCD2")) + { ccd->setBLOBEnabled(Options::guideRemoteImagesEnabled(), prop->getName()); + } }); + } + if(guiderType != GUIDE_INTERNAL) + { guiderCombo->clear(); guiderCombo->setEnabled(false); if (guiderType == GUIDE_PHD2) guiderCombo->addItem("PHD2"); else guiderCombo->addItem("LinGuider"); - return; } else + { guiderCombo->setEnabled(true); + guiderCombo->addItem(ccd->getDeviceName()); + } CCDs.append(ccd); + checkCCD(); +} - guiderCombo->addItem(ccd->getDeviceName()); +void Guide::configurePHD2Camera() +{ + if(phd2Guider->getCurrentCamera() == "" || guiderType != GUIDE_PHD2) + return; - checkCCD(); + currentCCD = nullptr; + foreach(ISD::CCD *ccd, CCDs) + { + if(phd2Guider->getCurrentCamera().contains(ccd->getDeviceName())) + { + currentCCD = ccd; + break; + } + } + + //This means that PHD2's current camera is not in Ekos. + if(!currentCCD) + { + QString log = i18n("PHD2's current camera: %1, is NOT connected to Ekos. ", phd2Guider->getCurrentCamera()); + if(Options::guideRemoteImagesEnabled()) + log += i18n("No guide image will be displayed while guiding. Please disable the Receive External Guide Frames Option in Ekos Guide Options if you want to receive the PHD2 Guide Star Image."); + else + log += i18n("It does not need to be connected, the PHD2 Guide Star Image should still be received."); + appendLogText(log); + return; + } + + QString log = i18n("PHD2's current camera: %1, IS connected to Ekos. ", phd2Guider->getCurrentCamera()); + if(Options::guideRemoteImagesEnabled()) + log += i18n("Ekos is currently configured to receive the full External Guide Frames."); + else + log += i18n("Ekos is currently configured to ignore the full external guide frames. The PHD2 Guide Star Image should be received instead."); + appendLogText(log); + + currentCCD->setBLOBEnabled(Options::guideRemoteImagesEnabled(), currentCCD->getDeviceName()); + + if(Options::guideRemoteImagesEnabled()) + { + if (currentCCD->hasGuideHead() && guiderCombo->currentText().contains("Guider")) + useGuideHead = true; + else + useGuideHead = false; + + ISD::CCDChip *targetChip = + currentCCD->getChip(useGuideHead ? ISD::CCDChip::GUIDE_CCD : ISD::CCDChip::PRIMARY_CCD); + if (targetChip && !targetChip->isCapturing()) + { + targetChip->setImageView(guideView, FITS_GUIDE); + targetChip->setCaptureMode(FITS_GUIDE); + } + syncCCDInfo(); + } } void Guide::addGuideHead(ISD::GDInterface *newCCD) @@ -797,7 +856,9 @@ if (targetChip->getFrameType() != FRAME_LIGHT) return; - binningCombo->setEnabled(targetChip->canBin()); + if(guiderType == GUIDE_INTERNAL) + binningCombo->setEnabled(targetChip->canBin()); + int subBinX = 1, subBinY = 1; if (targetChip->canBin()) { @@ -1099,10 +1160,10 @@ } else { + captureB->setEnabled(true); + loopB->setEnabled(true); if (guiderType == GUIDE_INTERNAL) { - captureB->setEnabled(true); - loopB->setEnabled(true); darkFrameCheck->setEnabled(true); subFrameCheck->setEnabled(true); autoStarCheck->setEnabled(true); @@ -1713,8 +1774,8 @@ appendLogText(i18n("External guider connected.")); externalConnectB->setEnabled(false); externalDisconnectB->setEnabled(true); - captureB->setEnabled(false); - loopB->setEnabled(false); + captureB->setEnabled(true); + loopB->setEnabled(true); clearCalibrationB->setEnabled(true); guideB->setEnabled(true); setBLOBEnabled(false); @@ -1905,6 +1966,8 @@ void Guide::syncTrackingBoxPosition() { + if(!currentCCD) + return; ISD::CCDChip *targetChip = currentCCD->getChip(useGuideHead ? ISD::CCDChip::GUIDE_CCD : ISD::CCDChip::PRIMARY_CCD); Q_ASSERT(targetChip); @@ -2070,7 +2133,7 @@ ST4Combo->setEnabled(false); exposureIN->setEnabled(true); binningCombo->setEnabled(false); - boxSizeCombo->setEnabled(false); + boxSizeCombo->setEnabled(Options::guideRemoteImagesEnabled()); filterCombo->setEnabled(false); if (Options::guideRemoteImagesEnabled() == false) @@ -2135,6 +2198,8 @@ connect(guider, &Ekos::GuideInterface::newAxisDelta, this, &Ekos::Guide::setAxisDelta); connect(guider, &Ekos::GuideInterface::newAxisPulse, this, &Ekos::Guide::setAxisPulse); connect(guider, &Ekos::GuideInterface::newAxisSigma, this, &Ekos::Guide::setAxisSigma); + + connect(guider, &Ekos::GuideInterface::guideEquipmentUpdated, this, &Ekos::Guide::configurePHD2Camera); } externalConnectB->setEnabled(false); @@ -2433,6 +2498,11 @@ QVector3D newStarPosition(x, y, -1); setStarPosition(newStarPosition, true); + if(guiderType == GUIDE_PHD2 && Options::guideRemoteImagesEnabled()) + { + phd2Guider->setLockPosition(starCenter.x(),starCenter.y()); + } + /*if (state == GUIDE_STAR_SELECT) { guider->setStarPosition(newStarPosition); @@ -2445,6 +2515,11 @@ void Guide::setAxisDelta(double ra, double de) { + //If PHD2 starts guiding because somebody pusted the button remotely, we want to set the state to guiding. + //If guide pulses start coming in, it must be guiding. + if(guiderType == GUIDE_PHD2 && state != GUIDE_GUIDING) + setStatus(GUIDE_GUIDING); + // Time since timer started. double key = guideTimer.elapsed() / 1000.0; @@ -2982,7 +3057,7 @@ void Guide::setBLOBEnabled(bool enable, const QString &ccd) { - // Nothing to do if guider is international or remote images are enabled + // Nothing to do if guider is internal or remote images are enabled if (guiderType == GUIDE_INTERNAL || Options::guideRemoteImagesEnabled()) return; @@ -3422,15 +3497,24 @@ state = GUIDE_CAPTURE; emit newStatus(state); - capture(); + if(guiderType == GUIDE_PHD2) + phd2Guider->captureSingleFrame(); + else + capture(); }); connect(loopB, &QPushButton::clicked, this, [this]() { state = GUIDE_LOOPING; emit newStatus(state); - capture(); + if(guiderType == GUIDE_PHD2) + { + phd2Guider->loop(); + stopB->setEnabled(true); + } + else + capture(); }); // Stop diff --git a/kstars/ekos/guide/guideinterface.h b/kstars/ekos/guide/guideinterface.h --- a/kstars/ekos/guide/guideinterface.h +++ b/kstars/ekos/guide/guideinterface.h @@ -69,6 +69,8 @@ void frameCaptureRequested(); + void guideEquipmentUpdated(); + protected: Ekos::GuideState state { GUIDE_IDLE }; double ccdPixelSizeX { 0 };