13 #include "ekos/manager.h"
14 #include "fitsviewer/fitsdata.h"
18 #include <KMessageBox>
21 #include <QJsonDocument>
22 #include <QNetworkReply>
24 #include <ekos_guide_debug.h>
26 #define MAX_SET_CONNECTED_RETRIES 3
36 events[
"Version"] = Version;
37 events[
"LockPositionSet"] = LockPositionSet;
38 events[
"Calibrating"] = Calibrating;
39 events[
"CalibrationComplete"] = CalibrationComplete;
40 events[
"StarSelected"] = StarSelected;
41 events[
"StartGuiding"] = StartGuiding;
42 events[
"Paused"] = Paused;
43 events[
"StartCalibration"] = StartCalibration;
44 events[
"AppState"] = AppState;
45 events[
"CalibrationFailed"] = CalibrationFailed;
46 events[
"CalibrationDataFlipped"] = CalibrationDataFlipped;
47 events[
"LoopingExposures"] = LoopingExposures;
48 events[
"LoopingExposuresStopped"] = LoopingExposuresStopped;
49 events[
"SettleBegin"] = SettleBegin;
50 events[
"Settling"] = Settling;
51 events[
"SettleDone"] = SettleDone;
52 events[
"StarLost"] = StarLost;
53 events[
"GuidingStopped"] = GuidingStopped;
54 events[
"Resumed"] = Resumed;
55 events[
"GuideStep"] = GuideStep;
56 events[
"GuidingDithered"] = GuidingDithered;
57 events[
"LockPositionLost"] = LockPositionLost;
58 events[
"Alert"] = Alert;
59 events[
"GuideParamChange"] = GuideParamChange;
60 events[
"ConfigurationChange"] = ConfigurationChange;
64 methodResults[
"capture_single_frame"] = CAPTURE_SINGLE_FRAME;
65 methodResults[
"clear_calibration"] = CLEAR_CALIBRATION_COMMAND_RECEIVED;
66 methodResults[
"dither"] = DITHER_COMMAND_RECEIVED;
71 methodResults[
"get_app_state"] = APP_STATE_RECEIVED;
74 methodResults[
"get_connected"] = IS_EQUIPMENT_CONNECTED;
76 methodResults[
"get_current_equipment"] = GET_CURRENT_EQUIPMENT;
77 methodResults[
"get_dec_guide_mode"] = DEC_GUIDE_MODE;
78 methodResults[
"get_exposure"] = EXPOSURE_TIME;
79 methodResults[
"get_exposure_durations"] = EXPOSURE_DURATIONS;
80 methodResults[
"get_lock_position"] = LOCK_POSITION;
84 methodResults[
"get_pixel_scale"] = PIXEL_SCALE;
89 methodResults[
"get_star_image"] = STAR_IMAGE;
91 methodResults[
"guide"] = GUIDE_COMMAND_RECEIVED;
93 methodResults[
"loop"] = LOOP;
96 methodResults[
"set_connected"] = CONNECTION_RESULT;
97 methodResults[
"set_dec_guide_mode"] = SET_DEC_GUIDE_MODE_COMMAND_RECEIVED;
98 methodResults[
"set_exposure"] = SET_EXPOSURE_COMMAND_RECEIVED;
99 methodResults[
"set_lock_position"] = SET_LOCK_POSITION;
102 methodResults[
"set_paused"] = SET_PAUSED_COMMAND_RECEIVED;
105 methodResults[
"stop_capture"] = STOP_CAPTURE_COMMAND_RECEIVED;
107 abortTimer =
new QTimer(
this);
110 if (state == CALIBRATING)
111 qCDebug(KSTARS_EKOS_GUIDE) <<
"Abort timeout expired while calibrating, retrying to guide.";
112 else if (state == LOSTLOCK)
113 qCDebug(KSTARS_EKOS_GUIDE) <<
"Abort timeout expired while reacquiring star, retrying to guide.";
115 qCDebug(KSTARS_EKOS_GUIDE) <<
"Abort timeout expired, stopping.";
119 ditherTimer =
new QTimer(
this);
122 qCDebug(KSTARS_EKOS_GUIDE) <<
"ditherTimer expired, state" << state <<
"dithering" << isDitherActive <<
"settling" << isSettling;
124 isDitherActive =
false;
126 if (Options::ditherFailAbortsAutoGuide())
129 emit newStatus(GUIDE_DITHERING_ERROR);
133 emit newLog(
i18n(
"PHD2: There was no dithering response from PHD2, but continue guiding."));
134 emit newStatus(Ekos::GUIDE_DITHERING_SUCCESS);
138 stateTimer =
new QTimer(
this);
143 m_PHD2ReconnectCounter++;
144 if (m_PHD2ReconnectCounter > PHD2_RECONNECT_THRESHOLD)
148 emit newLog(i18n(
"Reconnecting to PHD2 Host: %1, on port %2. . .", Options::pHD2Host(), Options::pHD2Port()));
150 connect(tcpSocket, &QTcpSocket::readyRead, this, &PHD2::readPHD2, Qt::UniqueConnection);
151 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
152 connect(tcpSocket, &QTcpSocket::errorOccurred, this, &PHD2::displayError, Qt::UniqueConnection);
154 connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this,
155 SLOT(displayError(QAbstractSocket::SocketError)));
157 tcpSocket->connectToHost(Options::pHD2Host(), Options::pHD2Port());
162 m_PHD2ReconnectCounter = 0;
163 checkIfEquipmentConnected();
181 connection = CONNECTING;
182 emit newLog(
i18n(
"Connecting to PHD2 Host: %1, on port %2. . .", Options::pHD2Host(), Options::pHD2Port()));
185 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
192 tcpSocket->connectToHost(Options::pHD2Host(), Options::pHD2Port());
194 m_PHD2ReconnectCounter = 0;
195 stateTimer->start(PHD2_RECONNECT_TIMEOUT);
198 case EQUIPMENT_DISCONNECTED:
200 connectEquipment(
true);
212 void PHD2::ResetConnectionState()
214 connection = DISCONNECTED;
217 pendingRpcResultType = NO_RESULT;
218 rpcRequestQueue.clear();
220 starImageRequested =
false;
222 isDitherActive =
false;
227 tcpSocket->disconnect(
this);
229 emit newStatus(GUIDE_DISCONNECTED);
232 bool PHD2::Disconnect()
236 case EQUIPMENT_CONNECTED:
237 emit newLog(
i18n(
"Aborting any capture before disconnecting equipment..."));
239 connection = DISCONNECTING;
244 case EQUIPMENT_DISCONNECTED:
246 tcpSocket->disconnectFromHost();
247 ResetConnectionState();
249 tcpSocket->waitForDisconnected(5000);
250 emit newLog(
i18n(
"Disconnected from PHD2 Host: %1, on port %2.", Options::pHD2Host(), Options::pHD2Port()));
266 emit newLog(
i18n(
"The host disconnected."));
269 emit newLog(
i18n(
"The host was not found. Please check the host name and port settings in Guide options."));
272 emit newLog(
i18n(
"The connection was refused by the peer. Make sure the PHD2 is running, and check that "
273 "the host name and port settings are correct."));
276 emit newLog(
i18n(
"The following error occurred: %1.", tcpSocket->errorString()));
279 ResetConnectionState();
281 emit newStatus(GUIDE_DISCONNECTED);
284 void PHD2::readPHD2()
286 while (!tcpSocket->atEnd() && tcpSocket->canReadLine())
298 emit newLog(
i18n(
"PHD2: invalid response received: %1",
QString(line)));
306 processPHD2Event(jsonObj, line);
308 processPHD2Error(jsonObj, line);
309 else if (jsonObj.
contains(
"result"))
310 processPHD2Result(jsonObj, line);
316 if (Options::verboseLogging())
317 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: event:" << line;
319 QString eventName = jsonEvent[
"Event"].toString();
321 if (!events.contains(eventName))
323 emit newLog(
i18n(
"Unknown PHD2 event: %1", eventName));
327 event = events.value(eventName);
332 emit newLog(
i18n(
"PHD2: Version %1", jsonEvent[
"PHDVersion"].
toString()));
335 case CalibrationComplete:
336 emit newLog(
i18n(
"PHD2: Calibration Complete."));
337 emit newStatus(Ekos::GUIDE_CALIBRATION_SUCCESS);
341 updateGuideParameters();
342 requestCurrentEquipmentUpdate();
346 emit newLog(
i18n(
"PHD2: Waiting for guiding to settle."));
350 handlePHD2AppState(PAUSED);
353 case StartCalibration:
354 handlePHD2AppState(CALIBRATING);
359 processPHD2State(jsonEvent[
"State"].
toString());
361 if (connection == CONNECTING)
363 emit newLog(
"PHD2: Connecting equipment and external guider...");
364 connectEquipment(
true);
368 case CalibrationFailed:
369 emit newLog(
i18n(
"PHD2: Calibration Failed (%1).", jsonEvent[
"Reason"].
toString()));
370 handlePHD2AppState(STOPPED);
373 case CalibrationDataFlipped:
374 emit newLog(
i18n(
"Calibration Data Flipped."));
377 case LoopingExposures:
378 handlePHD2AppState(LOOPING);
381 case LoopingExposuresStopped:
382 handlePHD2AppState(STOPPED);
394 if (state == PHD2::STOPPED)
399 if (jsonEvent[
"Status"].toInt() != 0)
402 emit newLog(
i18n(
"PHD2: Settling failed (%1).", jsonEvent[
"Error"].
toString()));
405 bool wasDithering = isDitherActive;
407 isDitherActive =
false;
413 if (error && Options::ditherFailAbortsAutoGuide())
416 emit newStatus(GUIDE_DITHERING_ERROR);
421 emit newLog(
i18n(
"PHD2: There was a dithering error, but continue guiding."));
423 emit newStatus(Ekos::GUIDE_DITHERING_SUCCESS);
430 emit newLog(
i18n(
"PHD2: Settling failed, aborted."));
431 emit newStatus(GUIDE_ABORTED);
436 emit newLog(
i18n(
"PHD2: Settling complete, Guiding Started."));
437 emit newStatus(GUIDE_GUIDING);
444 handlePHD2AppState(SELECTED);
449 handlePHD2AppState(LOSTLOCK);
453 handlePHD2AppState(STOPPED);
457 handlePHD2AppState(GUIDING);
464 if (state == LOSTLOCK)
465 emit newLog(
i18n(
"PHD2: Star found, guiding is resuming..."));
470 double diff_ra_pixels, diff_de_pixels, diff_ra_arcsecs, diff_de_arcsecs, pulse_ra, pulse_dec, snr;
471 QString RADirection, DECDirection;
472 diff_ra_pixels = jsonEvent[
"RADistanceRaw"].toDouble();
473 diff_de_pixels = jsonEvent[
"DECDistanceRaw"].toDouble();
474 pulse_ra = jsonEvent[
"RADuration"].toDouble();
475 pulse_dec = jsonEvent[
"DECDuration"].toDouble();
476 RADirection = jsonEvent[
"RADirection"].toString();
477 DECDirection = jsonEvent[
"DECDirection"].toString();
478 snr = jsonEvent[
"SNR"].toDouble();
480 if (RADirection ==
"East")
481 pulse_ra = -pulse_ra;
482 if (DECDirection ==
"South")
483 pulse_dec = -pulse_dec;
488 diff_ra_arcsecs = diff_ra_pixels * pixelScale;
489 diff_de_arcsecs = diff_de_pixels * pixelScale;
493 diff_ra_arcsecs = 206.26480624709 * diff_ra_pixels * ccdPixelSizeX / mountFocalLength;
494 diff_de_arcsecs = 206.26480624709 * diff_de_pixels * ccdPixelSizeY / mountFocalLength;
497 if (std::isfinite(snr))
500 if (std::isfinite(diff_ra_arcsecs) && std::isfinite(diff_de_arcsecs))
502 errorLog.append(
QPointF(diff_ra_arcsecs, diff_de_arcsecs));
503 if(errorLog.size() > 50)
506 emit newAxisDelta(diff_ra_arcsecs, diff_de_arcsecs);
507 emit newAxisPulse(pulse_ra, pulse_dec);
510 emit guideStats(diff_ra_arcsecs, diff_de_arcsecs, pulse_ra, pulse_dec,
511 std::isfinite(snr) ? snr : 0, 0, 0);
513 double total_sqr_RA_error = 0.0;
514 double total_sqr_DE_error = 0.0;
516 for (
auto &point : errorLog)
518 total_sqr_RA_error += point.x() * point.x();
519 total_sqr_DE_error += point.y() * point.y();
522 emit newAxisSigma(sqrt(total_sqr_RA_error / errorLog.size()), sqrt(total_sqr_DE_error / errorLog.size()));
528 if ( Options::guideSubframeEnabled() || currentCameraIsNotInEkos )
529 requestStarImage(32);
531 requestLockPosition();
535 case GuidingDithered:
538 case LockPositionSet:
539 handlePHD2AppState(SELECTED);
542 case LockPositionLost:
543 handlePHD2AppState(LOSTLOCK);
550 case GuideParamChange:
551 case ConfigurationChange:
566 void PHD2::processPHD2State(
const QString &phd2State)
568 if (phd2State ==
"Stopped")
569 handlePHD2AppState(STOPPED);
570 else if (phd2State ==
"Selected")
571 handlePHD2AppState(SELECTED);
572 else if (phd2State ==
"Calibrating")
573 handlePHD2AppState(CALIBRATING);
574 else if (phd2State ==
"Guiding")
575 handlePHD2AppState(GUIDING);
576 else if (phd2State ==
"LostLock")
577 handlePHD2AppState(LOSTLOCK);
578 else if (phd2State ==
"Paused")
579 handlePHD2AppState(PAUSED);
580 else if (phd2State ==
"Looping")
581 handlePHD2AppState(LOOPING);
582 else emit newLog(
QString(
"PHD2: Unsupported app state ") + phd2State +
".");
585 void PHD2::handlePHD2AppState(PHD2State newstate)
588 if (state == newstate)
598 emit newStatus(Ekos::GUIDE_CALIBRATION_ERROR);
601 emit newLog(
i18n(
"PHD2: Looping Exposures Stopped."));
602 emit newStatus(Ekos::GUIDE_IDLE);
606 emit newLog(
i18n(
"PHD2: Guiding Stopped."));
607 emit newStatus(Ekos::GUIDE_ABORTED);
610 if (connection == DISCONNECTING)
612 emit newLog(
"PHD2: Disconnecting equipment and external guider...");
613 connectEquipment(
false);
625 emit newLog(
i18n(
"PHD2: Lock Position Set."));
628 newstate = CALIBRATING;
629 emit newStatus(Ekos::GUIDE_CALIBRATING);
636 emit newLog(
i18n(
"PHD2: Star Selected."));
637 emit newStatus(GUIDE_STAR_SELECT);
646 emit newLog(
i18n(
"PHD2: Guiding resumed."));
648 emit newStatus(Ekos::GUIDE_GUIDING);
651 emit newLog(
i18n(
"PHD2: Guiding started."));
653 emit newStatus(Ekos::GUIDE_GUIDING);
662 emit newLog(
i18n(
"PHD2: Lock Position Lost, continuing calibration."));
668 emit newLog(
i18n(
"PHD2: Star Lost. Trying to reacquire for %1s.", Options::guideLostStarTimeout()));
669 abortTimer->start(
static_cast<int>(Options::guideLostStarTimeout()) * 1000);
670 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: Lost star timeout started (" << Options::guideLostStarTimeout() <<
" sec)";
671 emit newStatus(Ekos::GUIDE_REACQUIRE);
674 emit newLog(
i18n(
"PHD2: Lock Position Lost."));
680 emit newLog(
i18n(
"PHD2: Guiding paused."));
681 emit newStatus(GUIDE_SUSPENDED);
685 emit newLog(
i18n(
"PHD2: Calibrating, timing out in %1s.", Options::guideCalibrationTimeout()));
686 abortTimer->start(
static_cast<int>(Options::guideCalibrationTimeout()) * 1000);
687 emit newStatus(GUIDE_CALIBRATING);
694 emit newLog(
i18n(
"PHD2: Calibration turned to looping, failed."));
695 emit newStatus(GUIDE_CALIBRATION_ERROR);
698 emit newLog(
i18n(
"PHD2: Looping Exposures."));
699 emit newStatus(GUIDE_LOOPING);
704 emit newLog(
i18n(
"PHD2: Dithering started."));
705 emit newStatus(GUIDE_DITHERING);
714 PHD2ResultType resultType = takeRequestFromList(jsonObj);
716 if (resultType == STAR_IMAGE)
717 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: received star image response, id" <<
718 jsonObj[
"id"].toInt();
720 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: response:" << line;
728 case CAPTURE_SINGLE_FRAME:
731 case CLEAR_CALIBRATION_COMMAND_RECEIVED:
732 emit newLog(
i18n(
"PHD2: Calibration is cleared"));
735 case DITHER_COMMAND_RECEIVED:
736 handlePHD2AppState(DITHERING);
744 case APP_STATE_RECEIVED:
746 QString state = jsonObj[
"State"].toString();
748 state = jsonObj[
"result"].toString();
750 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: received unsupported app state";
752 processPHD2State(state);
759 case IS_EQUIPMENT_CONNECTED:
761 bool isConnected = jsonObj[
"result"].toBool();
768 connection = CONNECTED;
769 setEquipmentConnected();
771 else connectEquipment(
true);
777 setEquipmentConnected();
784 connection = EQUIPMENT_DISCONNECTED;
787 else connectEquipment(
false);
790 case EQUIPMENT_CONNECTED:
795 connection = EQUIPMENT_DISCONNECTED;
796 emit newStatus(Ekos::GUIDE_DISCONNECTED);
801 case EQUIPMENT_DISCONNECTED:
804 setEquipmentConnected();
811 case GET_CURRENT_EQUIPMENT:
813 QJsonObject equipObject = jsonObj[
"result"].toObject();
814 currentCamera = equipObject[
"camera"].toObject()[
"name"].toString();
815 currentMount = equipObject[
"mount"].toObject()[
"name"].toString();
816 currentAuxMount = equipObject[
"aux_mount"].toObject()[
"name"].toString();
818 emit guideEquipmentUpdated();
826 QString mode = jsonObj[
"result"].toString();
827 Ekos::Manager::Instance()->guideModule()->updateDirectionsFromPHD2(mode);
828 emit newLog(
i18n(
"PHD2: DEC Guide Mode is Set to: %1", mode));
835 int exposurems = jsonObj[
"result"].toInt();
836 double exposureTime = exposurems / 1000.0;
837 Ekos::Manager::Instance()->guideModule()->setExposure(exposureTime);
843 case EXPOSURE_DURATIONS:
845 QVariantList exposureListArray = jsonObj[
"result"].toArray().toVariantList();
846 logValidExposureTimes =
i18n(
"PHD2: Valid Exposure Times: Auto, ");
848 for(
int i = 1; i < exposureListArray.size();
850 values << exposureListArray.at(i).toDouble() / 1000.0;
851 logValidExposureTimes += Ekos::Manager::Instance()->guideModule()->setRecommendedExposureValues(values);
852 emit newLog(logValidExposureTimes);
857 if(jsonObj[
"result"].toArray().count() == 2)
859 double x = jsonObj[
"result"].toArray().at(0).toDouble();
860 double y = jsonObj[
"result"].toArray().at(1).toDouble();
862 emit newStarPosition(newStarCenter,
true);
866 emit newStarPixmap(m_GuideFrame->getTrackingBoxPixmap());
875 pixelScale = jsonObj[
"result"].toDouble();
877 emit newLog(
i18n(
"PHD2: Please set CCD and telescope parameters in PHD2, Pixel Scale is invalid."));
879 emit newLog(
i18n(
"PHD2: Pixel Scale is %1 arcsec per pixel",
QString::number(pixelScale,
'f', 2)));
889 starImageRequested =
false;
890 QJsonObject jsonResult = jsonObj[
"result"].toObject();
891 processStarImage(jsonResult);
897 case GUIDE_COMMAND_RECEIVED:
898 if (0 != jsonObj[
"result"].toInt(0))
900 emit newLog(
"PHD2: Guide command was rejected.");
901 handlePHD2AppState(STOPPED);
908 handlePHD2AppState(jsonObj[
"result"].toBool() ? LOOPING : STOPPED);
914 case CONNECTION_RESULT:
915 checkIfEquipmentConnected();
918 case SET_DEC_GUIDE_MODE_COMMAND_RECEIVED:
922 case SET_EXPOSURE_COMMAND_RECEIVED:
923 requestExposureTime();
926 case SET_LOCK_POSITION:
927 handlePHD2AppState(SELECTED);
933 case SET_PAUSED_COMMAND_RECEIVED:
934 handlePHD2AppState(PAUSED);
939 case STOP_CAPTURE_COMMAND_RECEIVED:
940 handlePHD2AppState(STOPPED);
951 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: error:" << line;
953 QJsonObject jsonErrorObject = jsonError[
"error"].toObject();
955 PHD2ResultType resultType = takeRequestFromList(jsonError);
960 case SET_EXPOSURE_COMMAND_RECEIVED:
961 emit newLog(logValidExposureTimes);
965 case CONNECTION_RESULT:
966 connection = EQUIPMENT_DISCONNECTED;
967 emit newStatus(Ekos::GUIDE_DISCONNECTED);
970 case DITHER_COMMAND_RECEIVED:
973 isDitherActive =
false;
974 emit newStatus(GUIDE_DITHERING_ERROR);
976 if (Options::ditherFailAbortsAutoGuide())
979 emit newLog(
"PHD2: failing after dithering aborts.");
980 emit newStatus(GUIDE_ABORTED);
989 case GUIDE_COMMAND_RECEIVED:
994 emit newLog(
i18n(
"PHD2 Error: unhandled '%1'", jsonErrorObject[
"message"].
toString()));
1006 m_GuideFrame = guideView;
1009 void PHD2::processStarImage(
const QJsonObject &jsonStarFrame)
1012 int width = jsonStarFrame[
"width"].toInt();
1013 int height = jsonStarFrame[
"height"].toInt();
1016 fitsfile *fptr =
nullptr;
1018 long fpixel = 1, naxis = 2, nelements, exposure;
1019 long naxes[2] = { width, height };
1020 char error_status[512] = {0};
1022 void* fits_buffer =
nullptr;
1023 size_t fits_buffer_size = 0;
1024 if (fits_create_memfile(&fptr, &fits_buffer, &fits_buffer_size, 4096, realloc, &status))
1026 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_create_file failed:" << error_status;
1030 if (fits_create_img(fptr, USHORT_IMG, naxis, naxes, &status))
1032 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_create_img failed:" << error_status;
1034 fits_close_file(fptr, &status);
1041 fits_update_key(fptr, TLONG,
"EXPOSURE", &exposure,
"Total Exposure Time", &status);
1049 nelements = naxes[0] * naxes[1];
1050 if (fits_write_img(fptr, TUSHORT, fpixel, nelements, converted.
data(), &status))
1052 fits_get_errstatus(status, error_status);
1053 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_write_img failed:" << error_status;
1055 fits_close_file(fptr, &status);
1060 if (fits_flush_file(fptr, &status))
1062 fits_get_errstatus(status, error_status);
1063 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_flush_file failed:" << error_status;
1065 fits_close_file(fptr, &status);
1070 if (fits_close_file(fptr, &status))
1072 fits_get_errstatus(status, error_status);
1073 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_close_file failed:" << error_status;
1083 fdata->loadFromBuffer(buffer,
"fits");
1085 m_GuideFrame->loadData(fdata);
1087 m_GuideFrame->updateFrame();
1088 m_GuideFrame->setTrackingBox(
QRect(0, 0, width, height));
1089 emit newStarPixmap(m_GuideFrame->getTrackingBoxPixmap());
1092 void PHD2::setEquipmentConnected()
1094 if (connection != EQUIPMENT_CONNECTED)
1096 setConnectedRetries = 0;
1097 connection = EQUIPMENT_CONNECTED;
1098 emit newStatus(Ekos::GUIDE_CONNECTED);
1099 updateGuideParameters();
1100 requestExposureDurations();
1101 requestCurrentEquipmentUpdate();
1105 void PHD2::updateGuideParameters()
1107 if (pixelScale == 0)
1108 requestPixelScale();
1109 requestExposureTime();
1116 void PHD2::captureSingleFrame()
1118 sendPHD2Request(
"capture_single_frame");
1122 bool PHD2::clearCalibration()
1124 if (connection != EQUIPMENT_CONNECTED)
1126 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1127 emit newStatus(Ekos::GUIDE_ABORTED);
1134 sendPHD2Request(
"clear_calibration", args);
1140 bool PHD2::dither(
double pixels)
1142 if (connection != EQUIPMENT_CONNECTED)
1144 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1145 emit newStatus(Ekos::GUIDE_ABORTED);
1151 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: ignoring dither requested while already settling";
1153 if (!isDitherActive)
1157 handlePHD2AppState(DITHERING);
1158 isDitherActive =
true;
1166 int ditherTimeout =
static_cast<int>(Options::ditherTimeout());
1168 settle.
insert(
"pixels",
static_cast<double>(Options::ditherThreshold()));
1169 settle.
insert(
"time",
static_cast<int>(Options::ditherSettle()));
1170 settle.
insert(
"timeout", ditherTimeout);
1180 isDitherActive =
true;
1188 enum { TIMEOUT_EXTRA_SECONDS = 60 };
1189 int millis = (ditherTimeout + TIMEOUT_EXTRA_SECONDS) * 1000;
1190 ditherTimer->start(millis);
1192 sendPHD2Request(
"dither", args);
1194 handlePHD2AppState(DITHERING);
1205 void PHD2::requestAppState()
1207 sendPHD2Request(
"get_app_state");
1214 void PHD2::checkIfEquipmentConnected()
1216 sendPHD2Request(
"get_connected");
1221 void PHD2::requestCurrentEquipmentUpdate()
1223 sendPHD2Request(
"get_current_equipment");
1227 void PHD2::checkDEGuideMode()
1229 sendPHD2Request(
"get_dec_guide_mode");
1233 void PHD2::requestExposureTime()
1235 sendPHD2Request(
"get_exposure");
1239 void PHD2::requestExposureDurations()
1241 sendPHD2Request(
"get_exposure_durations");
1245 void PHD2::requestLockPosition()
1247 sendPHD2Request(
"get_lock_position");
1254 void PHD2::requestPixelScale()
1256 sendPHD2Request(
"get_pixel_scale");
1265 void PHD2::requestStarImage(
int size)
1267 if (starImageRequested)
1269 if (Options::verboseLogging())
1270 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: skip extra star image request";
1276 sendPHD2Request(
"get_star_image", args2);
1278 starImageRequested =
true;
1286 if (state == GUIDING)
1288 emit newLog(
i18n(
"PHD2: Guiding is already running."));
1289 emit newStatus(Ekos::GUIDE_GUIDING);
1293 if (connection != EQUIPMENT_CONNECTED)
1295 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1296 emit newStatus(Ekos::GUIDE_ABORTED);
1303 settle.
insert(
"pixels",
static_cast<double>(Options::ditherThreshold()));
1304 settle.
insert(
"time",
static_cast<int>(Options::ditherSettle()));
1305 settle.
insert(
"timeout",
static_cast<int>(Options::ditherTimeout()));
1315 sendPHD2Request(
"guide", args);
1324 sendPHD2Request(
"loop");
1330 void PHD2::connectEquipment(
bool enable)
1332 if (connection == EQUIPMENT_CONNECTED && enable ==
true)
1335 if (connection == EQUIPMENT_DISCONNECTED && enable ==
false)
1338 if (setConnectedRetries++ > MAX_SET_CONNECTED_RETRIES)
1340 setConnectedRetries = 0;
1341 connection = EQUIPMENT_DISCONNECTED;
1342 emit newStatus(Ekos::GUIDE_DISCONNECTED);
1354 emit newLog(
i18n(
"PHD2: Connecting Equipment. . ."));
1356 emit newLog(
i18n(
"PHD2: Disconnecting Equipment. . ."));
1358 sendPHD2Request(
"set_connected", args);
1362 void PHD2::requestSetDEGuideMode(
bool deEnabled,
bool nEnabled,
1369 if(nEnabled && sEnabled)
1383 sendPHD2Request(
"set_dec_guide_mode", args);
1387 void PHD2::requestSetExposureTime(
int time)
1391 sendPHD2Request(
"set_exposure", args);
1395 void PHD2::setLockPosition(
double x,
double y)
1399 args << x << y <<
false;
1400 sendPHD2Request(
"set_lock_position", args);
1406 bool PHD2::suspend()
1408 if (connection != EQUIPMENT_CONNECTED)
1410 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1411 emit newStatus(Ekos::GUIDE_ABORTED);
1422 sendPHD2Request(
"set_paused", args);
1424 if (abortTimer->isActive())
1427 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: Lost star timeout cancelled.";
1437 if (connection != EQUIPMENT_CONNECTED)
1439 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1440 emit newStatus(Ekos::GUIDE_ABORTED);
1449 sendPHD2Request(
"set_paused", args);
1451 if (state == LOSTLOCK)
1453 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: Lost star timeout restarted.";
1454 abortTimer->start(
static_cast<int>(Options::guideLostStarTimeout()) * 1000);
1466 if (connection != EQUIPMENT_CONNECTED)
1468 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1469 emit newStatus(Ekos::GUIDE_ABORTED);
1475 sendPHD2Request(
"stop_capture");
1480 bool PHD2::calibrate()
1489 void PHD2::sendRpcCall(
QJsonObject &call, PHD2ResultType resultType)
1491 assert(resultType != NO_RESULT);
1492 assert(pendingRpcResultType == NO_RESULT);
1494 int rpcId = nextRpcId++;
1495 call.
insert(
"id", rpcId);
1497 pendingRpcId = rpcId;
1498 pendingRpcResultType = resultType;
1502 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: request:" << request;
1508 qint64
const n = tcpSocket->write(request);
1510 if ((
int) n != request.
size())
1512 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: unexpected short write:" << n <<
"bytes of" << request.
size();
1517 void PHD2::sendNextRpcCall()
1519 if (pendingRpcResultType != NO_RESULT)
1522 if (rpcRequestQueue.empty())
1525 RpcCall &call = rpcRequestQueue.front();
1526 sendRpcCall(call.call, call.resultType);
1527 rpcRequestQueue.pop_front();
1532 assert(methodResults.contains(method));
1534 PHD2ResultType resultType = methodResults[method];
1538 jsonRPC.
insert(
"jsonrpc",
"2.0");
1539 jsonRPC.
insert(
"method", method);
1542 jsonRPC.
insert(
"params", args);
1544 if (pendingRpcResultType == NO_RESULT)
1547 sendRpcCall(jsonRPC, resultType);
1554 if (Options::verboseLogging())
1555 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: defer call" << method;
1557 rpcRequestQueue.
push_back(RpcCall(jsonRPC, resultType));
1561 PHD2::PHD2ResultType PHD2::takeRequestFromList(
const QJsonObject &response)
1563 if (Q_UNLIKELY(!response.
contains(
"id")))
1565 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: ignoring unexpected response with no id";
1569 int id = response[
"id"].toInt();
1571 if (Q_UNLIKELY(
id != pendingRpcId))
1575 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: ignoring unexpected response with id" << id;
1579 PHD2ResultType val = pendingRpcResultType;
1580 pendingRpcResultType = NO_RESULT;