13#include "ekos/manager.h"
14#include "fitsviewer/fitsdata.h"
15#include "ekos/guide/guide.h"
16#include "fitsviewer/fitsview.h"
23#include <QJsonDocument>
24#include <QNetworkReply>
26#include <ekos_guide_debug.h>
28#define MAX_SET_CONNECTED_RETRIES 3
34 tcpSocket =
new QTcpSocket(
this);
38 events[
"Version"] = Version;
39 events[
"LockPositionSet"] = LockPositionSet;
40 events[
"Calibrating"] = Calibrating;
41 events[
"CalibrationComplete"] = CalibrationComplete;
42 events[
"StarSelected"] = StarSelected;
43 events[
"StartGuiding"] = StartGuiding;
44 events[
"Paused"] = Paused;
45 events[
"StartCalibration"] = StartCalibration;
46 events[
"AppState"] = AppState;
47 events[
"CalibrationFailed"] = CalibrationFailed;
48 events[
"CalibrationDataFlipped"] = CalibrationDataFlipped;
49 events[
"LoopingExposures"] = LoopingExposures;
50 events[
"LoopingExposuresStopped"] = LoopingExposuresStopped;
51 events[
"SettleBegin"] = SettleBegin;
52 events[
"Settling"] = Settling;
53 events[
"SettleDone"] = SettleDone;
54 events[
"StarLost"] = StarLost;
55 events[
"GuidingStopped"] = GuidingStopped;
56 events[
"Resumed"] = Resumed;
57 events[
"GuideStep"] = GuideStep;
58 events[
"GuidingDithered"] = GuidingDithered;
59 events[
"LockPositionLost"] = LockPositionLost;
60 events[
"Alert"] = Alert;
61 events[
"GuideParamChange"] = GuideParamChange;
62 events[
"ConfigurationChange"] = ConfigurationChange;
66 methodResults[
"capture_single_frame"] = CAPTURE_SINGLE_FRAME;
67 methodResults[
"clear_calibration"] = CLEAR_CALIBRATION_COMMAND_RECEIVED;
68 methodResults[
"dither"] = DITHER_COMMAND_RECEIVED;
73 methodResults[
"get_app_state"] = APP_STATE_RECEIVED;
76 methodResults[
"get_connected"] = IS_EQUIPMENT_CONNECTED;
78 methodResults[
"get_current_equipment"] = GET_CURRENT_EQUIPMENT;
79 methodResults[
"get_dec_guide_mode"] = DEC_GUIDE_MODE;
80 methodResults[
"get_exposure"] = EXPOSURE_TIME;
81 methodResults[
"get_exposure_durations"] = EXPOSURE_DURATIONS;
82 methodResults[
"get_lock_position"] = LOCK_POSITION;
86 methodResults[
"get_pixel_scale"] = PIXEL_SCALE;
91 methodResults[
"get_star_image"] = STAR_IMAGE;
93 methodResults[
"guide"] = GUIDE_COMMAND_RECEIVED;
95 methodResults[
"loop"] = LOOP;
98 methodResults[
"set_connected"] = CONNECTION_RESULT;
99 methodResults[
"set_dec_guide_mode"] = SET_DEC_GUIDE_MODE_COMMAND_RECEIVED;
100 methodResults[
"set_exposure"] = SET_EXPOSURE_COMMAND_RECEIVED;
101 methodResults[
"set_lock_position"] = SET_LOCK_POSITION;
104 methodResults[
"set_paused"] = SET_PAUSED_COMMAND_RECEIVED;
107 methodResults[
"stop_capture"] = STOP_CAPTURE_COMMAND_RECEIVED;
109 abortTimer =
new QTimer(
this);
112 if (state == CALIBRATING)
113 qCDebug(KSTARS_EKOS_GUIDE) <<
"Abort timeout expired while calibrating, retrying to guide.";
114 else if (state == LOSTLOCK)
115 qCDebug(KSTARS_EKOS_GUIDE) <<
"Abort timeout expired while reacquiring star, retrying to guide.";
117 qCDebug(KSTARS_EKOS_GUIDE) <<
"Abort timeout expired, stopping.";
121 ditherTimer =
new QTimer(
this);
124 qCDebug(KSTARS_EKOS_GUIDE) <<
"ditherTimer expired, state" << state <<
"dithering" << isDitherActive <<
"settling" << isSettling;
126 isDitherActive =
false;
128 if (Options::ditherFailAbortsAutoGuide())
131 emit newStatus(GUIDE_DITHERING_ERROR);
135 emit newLog(
i18n(
"PHD2: There was no dithering response from PHD2, but continue guiding."));
136 emit newStatus(Ekos::GUIDE_DITHERING_SUCCESS);
140 stateTimer =
new QTimer(
this);
147 m_PHD2ReconnectCounter++;
148 if (m_PHD2ReconnectCounter > PHD2_RECONNECT_THRESHOLD)
151 emit newLog(
i18n(
"Giving up reconnecting."));
155 emit newLog(
i18n(
"Reconnecting to PHD2 Host: %1, on port %2. . .", Options::pHD2Host(), Options::pHD2Port()));
158#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
164 tcpSocket->connectToHost(Options::pHD2Host(), Options::pHD2Port());
168 m_PHD2ReconnectCounter = 0;
169 checkIfEquipmentConnected();
173 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: TCP connection state:" << socketstate;
191 connection = CONNECTING;
192 emit newLog(
i18n(
"Connecting to PHD2 Host: %1, on port %2. . .", Options::pHD2Host(), Options::pHD2Port()));
195#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
202 tcpSocket->connectToHost(Options::pHD2Host(), Options::pHD2Port());
204 m_PHD2ReconnectCounter = 0;
205 stateTimer->start(PHD2_RECONNECT_TIMEOUT);
208 case EQUIPMENT_DISCONNECTED:
210 connectEquipment(
true);
222void PHD2::ResetConnectionState()
224 connection = DISCONNECTED;
227 pendingRpcResultType = NO_RESULT;
228 rpcRequestQueue.clear();
230 starImageRequested =
false;
232 isDitherActive =
false;
237 tcpSocket->disconnect(
this);
239 emit newStatus(GUIDE_DISCONNECTED);
242bool PHD2::Disconnect()
246 case EQUIPMENT_CONNECTED:
247 emit newLog(
i18n(
"Aborting any capture before disconnecting equipment..."));
249 connection = DISCONNECTING;
254 case EQUIPMENT_DISCONNECTED:
256 tcpSocket->disconnectFromHost();
257 ResetConnectionState();
259 tcpSocket->waitForDisconnected(5000);
260 emit newLog(
i18n(
"Disconnected from PHD2 Host: %1, on port %2.", Options::pHD2Host(), Options::pHD2Port()));
276 emit newLog(
i18n(
"The host disconnected."));
279 emit newLog(
i18n(
"The host was not found. Please check the host name and port settings in Guide options."));
282 emit newLog(
i18n(
"The connection was refused by the peer. Make sure the PHD2 is running, and check that "
283 "the host name and port settings are correct."));
286 emit newLog(
i18n(
"The following error occurred: %1.", tcpSocket->errorString()));
289 ResetConnectionState();
291 emit newStatus(GUIDE_DISCONNECTED);
296 while (!tcpSocket->atEnd() && tcpSocket->canReadLine())
298 QByteArray line = tcpSocket->readLine();
302 QJsonParseError qjsonError;
308 emit newLog(
i18n(
"PHD2: invalid response received: %1", QString(line)));
313 QJsonObject jsonObj = jdoc.
object();
316 processPHD2Event(jsonObj, line);
318 processPHD2Error(jsonObj, line);
319 else if (jsonObj.
contains(
"result"))
320 processPHD2Result(jsonObj, line);
324void PHD2::processPHD2Event(
const QJsonObject &jsonEvent,
const QByteArray &line)
326 if (Options::verboseLogging())
327 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: event:" << line;
329 QString eventName = jsonEvent[
"Event"].toString();
331 if (!events.contains(eventName))
333 emit newLog(
i18n(
"Unknown PHD2 event: %1", eventName));
337 event = events.
value(eventName);
342 emit newLog(
i18n(
"PHD2: Version %1", jsonEvent[
"PHDVersion"].
toString()));
345 case CalibrationComplete:
346 emit newLog(
i18n(
"PHD2: Calibration Complete."));
347 emit newStatus(Ekos::GUIDE_CALIBRATION_SUCCESS);
351 updateGuideParameters();
352 requestCurrentEquipmentUpdate();
356 emit newLog(
i18n(
"PHD2: Waiting for guiding to settle."));
360 handlePHD2AppState(PAUSED);
363 case StartCalibration:
364 handlePHD2AppState(CALIBRATING);
369 processPHD2State(jsonEvent[
"State"].
toString());
371 if (connection == CONNECTING)
373 emit newLog(
"PHD2: Connecting equipment and external guider...");
374 connectEquipment(
true);
378 case CalibrationFailed:
379 emit newLog(
i18n(
"PHD2: Calibration Failed (%1).", jsonEvent[
"Reason"].
toString()));
380 handlePHD2AppState(STOPPED);
383 case CalibrationDataFlipped:
384 emit newLog(
i18n(
"Calibration Data Flipped."));
387 case LoopingExposures:
388 handlePHD2AppState(LOOPING);
391 case LoopingExposuresStopped:
392 handlePHD2AppState(STOPPED);
403 emit newLog(
i18n(
"PHD2: SettleDone."));
406 if (state == PHD2::STOPPED)
411 if (jsonEvent[
"Status"].toInt() != 0)
414 emit newLog(
i18n(
"PHD2: Settling failed (%1).", jsonEvent[
"Error"].
toString()));
417 bool wasDithering = isDitherActive;
419 isDitherActive =
false;
425 if (error && Options::ditherFailAbortsAutoGuide())
428 emit newStatus(GUIDE_DITHERING_ERROR);
433 emit newLog(
i18n(
"PHD2: There was a dithering error, but continue guiding."));
435 emit newStatus(Ekos::GUIDE_DITHERING_SUCCESS);
442 emit newLog(
i18n(
"PHD2: Settling failed, aborted."));
443 emit newStatus(GUIDE_ABORTED);
448 emit newLog(
i18n(
"PHD2: Settling complete, Guiding Started."));
449 emit newStatus(GUIDE_GUIDING);
456 handlePHD2AppState(SELECTED);
461 handlePHD2AppState(LOSTLOCK);
465 handlePHD2AppState(STOPPED);
469 handlePHD2AppState(GUIDING);
476 if (state == LOSTLOCK)
477 emit newLog(
i18n(
"PHD2: Star found, guiding is resuming..."));
482 double diff_ra_pixels, diff_de_pixels, diff_ra_arcsecs, diff_de_arcsecs, pulse_ra, pulse_dec, snr;
483 QString RADirection, DECDirection;
484 diff_ra_pixels = jsonEvent[
"RADistanceRaw"].toDouble();
485 diff_de_pixels = jsonEvent[
"DECDistanceRaw"].toDouble();
486 pulse_ra = jsonEvent[
"RADuration"].toDouble();
487 pulse_dec = jsonEvent[
"DECDuration"].toDouble();
488 RADirection = jsonEvent[
"RADirection"].toString();
489 DECDirection = jsonEvent[
"DECDirection"].toString();
490 snr = jsonEvent[
"SNR"].toDouble();
492 if (RADirection ==
"East")
495 pulse_ra = -pulse_ra;
496 if (DECDirection ==
"South")
498 pulse_dec = -pulse_dec;
504 diff_ra_arcsecs = diff_ra_pixels * pixelScale;
505 diff_de_arcsecs = diff_de_pixels * pixelScale;
509 diff_ra_arcsecs = 206.26480624709 * diff_ra_pixels * ccdPixelSizeX / mountFocalLength;
510 diff_de_arcsecs = 206.26480624709 * diff_de_pixels * ccdPixelSizeY / mountFocalLength;
513 if (std::isfinite(snr))
516 if (std::isfinite(diff_ra_arcsecs) && std::isfinite(diff_de_arcsecs))
519 errorLog.append(QPointF(diff_ra_arcsecs, diff_de_arcsecs));
520 if(errorLog.size() > 100)
523 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: Error log size:" << errorLog.size();
529 emit newAxisDelta(diff_ra_arcsecs, diff_de_arcsecs);
530 emit newAxisPulse(pulse_ra, pulse_dec);
535 emit guideStats(-diff_ra_arcsecs, diff_de_arcsecs, pulse_ra, pulse_dec,
536 std::isfinite(snr) ? snr : 0, 0, 0);
542 QVector<QPointF> pixelErrorLog;
545 for (
auto &point : errorLog)
548 pixelErrorLog.
append(QPointF(point.x() / pixelScale, point.y() / pixelScale));
554 pixelErrorLog = errorLog;
558 double n = pixelErrorLog.
size();
561 double sumY_RA = 0.0;
562 double sumYSq_RA = 0.0;
563 for (
auto &point : pixelErrorLog)
565 sumY_RA += point.x();
566 sumYSq_RA += point.x() * point.x();
570 double sumY_DEC = 0.0;
571 double sumYSq_DEC = 0.0;
572 for (
auto &point : pixelErrorLog)
574 sumY_DEC += point.y();
575 sumYSq_DEC += point.y() * point.y();
579 double ra_sigma_pixels = 0.0;
580 double de_sigma_pixels = 0.0;
584 double variance_RA = (n * sumYSq_RA - sumY_RA * sumY_RA) / (n * n);
585 double variance_DEC = (n * sumYSq_DEC - sumY_DEC * sumY_DEC) / (n * n);
587 if (variance_RA >= 0.0)
588 ra_sigma_pixels = sqrt(variance_RA);
590 if (variance_DEC >= 0.0)
591 de_sigma_pixels = sqrt(variance_DEC);
595 double ra_sigma_arcsec = (pixelScale > 0) ? ra_sigma_pixels * pixelScale : ra_sigma_pixels;
596 double de_sigma_arcsec = (pixelScale > 0) ? de_sigma_pixels * pixelScale : de_sigma_pixels;
599 emit newAxisSigma(ra_sigma_arcsec, de_sigma_arcsec);
604 if ( Options::guideSubframe() || currentCameraIsNotInEkos )
605 requestStarImage(32);
607 requestLockPosition();
611 case GuidingDithered:
614 case LockPositionSet:
615 handlePHD2AppState(SELECTED);
618 case LockPositionLost:
619 handlePHD2AppState(LOSTLOCK);
626 case GuideParamChange:
627 case ConfigurationChange:
642void PHD2::processPHD2State(
const QString &phd2State)
644 if (phd2State ==
"Stopped")
645 handlePHD2AppState(STOPPED);
646 else if (phd2State ==
"Selected")
647 handlePHD2AppState(SELECTED);
648 else if (phd2State ==
"Calibrating")
649 handlePHD2AppState(CALIBRATING);
650 else if (phd2State ==
"Guiding")
651 handlePHD2AppState(GUIDING);
652 else if (phd2State ==
"LostLock")
653 handlePHD2AppState(LOSTLOCK);
654 else if (phd2State ==
"Paused")
655 handlePHD2AppState(PAUSED);
656 else if (phd2State ==
"Looping")
657 handlePHD2AppState(LOOPING);
658 else emit newLog(QString(
"PHD2: Unsupported app state ") + phd2State +
".");
661void PHD2::handlePHD2AppState(PHD2State newstate)
664 if (state == newstate)
674 emit newStatus(Ekos::GUIDE_CALIBRATION_ERROR);
677 emit newLog(
i18n(
"PHD2: Looping Exposures Stopped."));
678 emit newStatus(Ekos::GUIDE_IDLE);
682 emit newLog(
i18n(
"PHD2: Guiding Stopped."));
683 emit newStatus(Ekos::GUIDE_ABORTED);
686 if (connection == DISCONNECTING)
688 emit newLog(
"PHD2: Disconnecting equipment and external guider...");
689 connectEquipment(
false);
701 emit newLog(
i18n(
"PHD2: Lock Position Set."));
704 newstate = CALIBRATING;
705 emit newStatus(Ekos::GUIDE_CALIBRATING);
712 emit newLog(
i18n(
"PHD2: Star Selected."));
713 emit newStatus(GUIDE_STAR_SELECT);
721 emit newLog(
i18n(
"PHD2: Guiding...waiting for settle..."));
725 emit newLog(
i18n(
"PHD2: Dithering successful."));
728 emit newStatus(Ekos::GUIDE_DITHERING_SUCCESS);
731 emit newLog(
i18n(
"PHD2: Guiding started."));
733 emit newStatus(Ekos::GUIDE_GUIDING);
742 emit newLog(
i18n(
"PHD2: Lock Position Lost, continuing calibration."));
748 emit newLog(
i18n(
"PHD2: Star Lost. Trying to reacquire for %1s.", Options::guideLostStarTimeout()));
749 abortTimer->start(
static_cast<int>(Options::guideLostStarTimeout()) * 1000);
750 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: Lost star timeout started (" << Options::guideLostStarTimeout() <<
" sec)";
751 emit newStatus(Ekos::GUIDE_REACQUIRE);
754 emit newLog(
i18n(
"PHD2: Lock Position Lost."));
760 emit newLog(
i18n(
"PHD2: Guiding paused."));
761 emit newStatus(GUIDE_SUSPENDED);
765 emit newLog(
i18n(
"PHD2: Calibrating, timing out in %1s.", Options::guideCalibrationTimeout()));
766 abortTimer->start(
static_cast<int>(Options::guideCalibrationTimeout()) * 1000);
767 emit newStatus(GUIDE_CALIBRATING);
774 emit newLog(
i18n(
"PHD2: Calibration turned to looping, failed."));
775 emit newStatus(GUIDE_CALIBRATION_ERROR);
778 emit newLog(
i18n(
"PHD2: Looping Exposures."));
779 emit newStatus(GUIDE_LOOPING);
784 emit newLog(
i18n(
"PHD2: Dithering started."));
785 emit newStatus(GUIDE_DITHERING);
792void PHD2::processPHD2Result(
const QJsonObject &jsonObj,
const QByteArray &line)
794 PHD2ResultType resultType = takeRequestFromList(jsonObj);
796 if (resultType == STAR_IMAGE)
797 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: received star image response, id" <<
798 jsonObj[
"id"].toInt();
800 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: response:" << line;
808 case CAPTURE_SINGLE_FRAME:
811 case CLEAR_CALIBRATION_COMMAND_RECEIVED:
812 emit newLog(
i18n(
"PHD2: Calibration is cleared"));
815 case DITHER_COMMAND_RECEIVED:
816 handlePHD2AppState(DITHERING);
824 case APP_STATE_RECEIVED:
826 QString state = jsonObj[
"State"].toString();
828 state = jsonObj[
"result"].toString();
830 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: received unsupported app state";
832 processPHD2State(state);
839 case IS_EQUIPMENT_CONNECTED:
841 bool isConnected = jsonObj[
"result"].toBool();
848 connection = CONNECTED;
849 setEquipmentConnected();
851 else connectEquipment(
true);
857 setEquipmentConnected();
864 connection = EQUIPMENT_DISCONNECTED;
867 else connectEquipment(
false);
870 case EQUIPMENT_CONNECTED:
875 connection = EQUIPMENT_DISCONNECTED;
876 emit newStatus(Ekos::GUIDE_DISCONNECTED);
881 case EQUIPMENT_DISCONNECTED:
884 setEquipmentConnected();
891 case GET_CURRENT_EQUIPMENT:
893 QJsonObject equipObject = jsonObj[
"result"].toObject();
894 currentCamera = equipObject[
"camera"].toObject()[
"name"].toString();
895 currentMount = equipObject[
"mount"].toObject()[
"name"].toString();
896 currentAuxMount = equipObject[
"aux_mount"].toObject()[
"name"].toString();
898 emit guideEquipmentUpdated();
906 QString mode = jsonObj[
"result"].toString();
907 Ekos::Manager::Instance()->guideModule()->updateDirectionsFromPHD2(mode);
908 emit newLog(
i18n(
"PHD2: DEC Guide Mode is Set to: %1", mode));
915 int exposurems = jsonObj[
"result"].toInt();
916 double exposureTime = exposurems / 1000.0;
917 Ekos::Manager::Instance()->guideModule()->
setExposure(exposureTime);
923 case EXPOSURE_DURATIONS:
925 QVariantList exposureListArray = jsonObj[
"result"].toArray().toVariantList();
926 logValidExposureTimes =
i18n(
"PHD2: Valid Exposure Times: Auto, ");
927 QList<double> values;
928 for(
int i = 1; i < exposureListArray.size();
930 values << exposureListArray.at(i).toDouble() / 1000.0;
931 logValidExposureTimes += Ekos::Manager::Instance()->guideModule()->setRecommendedExposureValues(values);
932 emit newLog(logValidExposureTimes);
937 if(jsonObj[
"result"].toArray().count() == 2)
939 double x = jsonObj[
"result"].toArray().at(0).toDouble();
940 double y = jsonObj[
"result"].toArray().at(1).toDouble();
941 QVector3D newStarCenter(x, y, 0);
942 emit newStarPosition(newStarCenter,
true);
946 emit newStarPixmap(m_GuideFrame->getTrackingBoxPixmap());
955 pixelScale = jsonObj[
"result"].toDouble();
957 emit newLog(
i18n(
"PHD2: Please set CCD and telescope parameters in PHD2, Pixel Scale is invalid."));
959 emit newLog(
i18n(
"PHD2: Pixel Scale is %1 arcsec per pixel",
QString::number(pixelScale,
'f', 2)));
969 starImageRequested =
false;
970 QJsonObject jsonResult = jsonObj[
"result"].toObject();
971 processStarImage(jsonResult);
977 case GUIDE_COMMAND_RECEIVED:
978 if (0 != jsonObj[
"result"].toInt(0))
980 emit newLog(
"PHD2: Guide command was rejected.");
981 handlePHD2AppState(STOPPED);
988 handlePHD2AppState(jsonObj[
"result"].toBool() ? LOOPING : STOPPED);
994 case CONNECTION_RESULT:
995 checkIfEquipmentConnected();
998 case SET_DEC_GUIDE_MODE_COMMAND_RECEIVED:
1002 case SET_EXPOSURE_COMMAND_RECEIVED:
1003 requestExposureTime();
1006 case SET_LOCK_POSITION:
1007 handlePHD2AppState(SELECTED);
1013 case SET_PAUSED_COMMAND_RECEIVED:
1014 handlePHD2AppState(PAUSED);
1019 case STOP_CAPTURE_COMMAND_RECEIVED:
1020 handlePHD2AppState(STOPPED);
1029void PHD2::processPHD2Error(
const QJsonObject &jsonError,
const QByteArray &line)
1031 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: error:" << line;
1033 QJsonObject jsonErrorObject = jsonError[
"error"].toObject();
1035 PHD2ResultType resultType = takeRequestFromList(jsonError);
1040 case SET_EXPOSURE_COMMAND_RECEIVED:
1041 emit newLog(logValidExposureTimes);
1045 case CONNECTION_RESULT:
1046 connection = EQUIPMENT_DISCONNECTED;
1047 emit newStatus(Ekos::GUIDE_DISCONNECTED);
1050 case DITHER_COMMAND_RECEIVED:
1051 ditherTimer->stop();
1053 isDitherActive =
false;
1054 emit newStatus(GUIDE_DITHERING_ERROR);
1056 if (Options::ditherFailAbortsAutoGuide())
1059 emit newLog(
"PHD2: failing after dithering aborts.");
1060 emit newStatus(GUIDE_ABORTED);
1069 case GUIDE_COMMAND_RECEIVED:
1074 emit newLog(
i18n(
"PHD2 Error: unhandled '%1'", jsonErrorObject[
"message"].
toString()));
1084void PHD2::setGuideView(
const QSharedPointer<FITSView> &guideView)
1086 m_GuideFrame = guideView;
1089void PHD2::processStarImage(
const QJsonObject &jsonStarFrame)
1092 int width = jsonStarFrame[
"width"].toInt();
1093 int height = jsonStarFrame[
"height"].toInt();
1096 fitsfile *fptr =
nullptr;
1098 long fpixel = 1, naxis = 2, nelements, exposure;
1099 long naxes[2] = { width, height };
1100 char error_status[512] = {0};
1102 void* fits_buffer =
nullptr;
1103 size_t fits_buffer_size = 0;
1104 if (fits_create_memfile(&fptr, &fits_buffer, &fits_buffer_size, 4096, realloc, &status))
1106 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_create_file failed:" << error_status;
1110 if (fits_create_img(fptr, USHORT_IMG, naxis, naxes, &status))
1112 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_create_img failed:" << error_status;
1114 fits_close_file(fptr, &status);
1121 fits_update_key(fptr, TLONG,
"EXPOSURE", &exposure,
"Total Exposure Time", &status);
1129 nelements = naxes[0] * naxes[1];
1130 if (fits_write_img(fptr, TUSHORT, fpixel, nelements, converted.
data(), &status))
1132 fits_get_errstatus(status, error_status);
1133 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_write_img failed:" << error_status;
1135 fits_close_file(fptr, &status);
1140 if (fits_flush_file(fptr, &status))
1142 fits_get_errstatus(status, error_status);
1143 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_flush_file failed:" << error_status;
1145 fits_close_file(fptr, &status);
1150 if (fits_close_file(fptr, &status))
1152 fits_get_errstatus(status, error_status);
1153 qCWarning(KSTARS_EKOS_GUIDE) <<
"fits_close_file failed:" << error_status;
1160 QSharedPointer<FITSData> fdata;
1163 fdata->setExtension(QString(
"fits"));
1164 fdata->loadFromBuffer(buffer);
1166 m_GuideFrame->loadData(fdata);
1168 m_GuideFrame->updateFrame();
1169 m_GuideFrame->setTrackingBox(QRect(0, 0, width, height));
1170 emit newStarPixmap(m_GuideFrame->getTrackingBoxPixmap());
1173void PHD2::setEquipmentConnected()
1175 if (connection != EQUIPMENT_CONNECTED)
1177 setConnectedRetries = 0;
1178 connection = EQUIPMENT_CONNECTED;
1179 emit newStatus(Ekos::GUIDE_CONNECTED);
1180 updateGuideParameters();
1181 requestExposureDurations();
1182 requestCurrentEquipmentUpdate();
1186void PHD2::updateGuideParameters()
1188 if (pixelScale == 0)
1189 requestPixelScale();
1190 requestExposureTime();
1197void PHD2::captureSingleFrame()
1199 sendPHD2Request(
"capture_single_frame");
1203bool PHD2::clearCalibration()
1205 if (connection != EQUIPMENT_CONNECTED)
1207 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1208 emit newStatus(Ekos::GUIDE_ABORTED);
1215 sendPHD2Request(
"clear_calibration", args);
1221bool PHD2::dither(
double pixels)
1223 if (connection != EQUIPMENT_CONNECTED)
1225 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1226 emit newStatus(Ekos::GUIDE_ABORTED);
1232 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: ignoring dither requested while already settling";
1234 if (!isDitherActive)
1238 handlePHD2AppState(DITHERING);
1239 isDitherActive =
true;
1247 int ditherTimeout =
static_cast<int>(Options::ditherTimeout());
1249 settle.
insert(
"pixels",
static_cast<double>(Options::ditherThreshold()));
1250 settle.
insert(
"time",
static_cast<int>(Options::ditherSettle()));
1251 settle.
insert(
"timeout", ditherTimeout);
1261 isDitherActive =
true;
1269 enum { TIMEOUT_EXTRA_SECONDS = 60 };
1270 int millis = (ditherTimeout + TIMEOUT_EXTRA_SECONDS) * 1000;
1271 ditherTimer->start(millis);
1273 sendPHD2Request(
"dither", args);
1275 handlePHD2AppState(DITHERING);
1286void PHD2::requestAppState()
1288 sendPHD2Request(
"get_app_state");
1295void PHD2::checkIfEquipmentConnected()
1297 sendPHD2Request(
"get_connected");
1302void PHD2::requestCurrentEquipmentUpdate()
1304 sendPHD2Request(
"get_current_equipment");
1308void PHD2::checkDEGuideMode()
1310 sendPHD2Request(
"get_dec_guide_mode");
1314void PHD2::requestExposureTime()
1316 sendPHD2Request(
"get_exposure");
1320void PHD2::requestExposureDurations()
1322 sendPHD2Request(
"get_exposure_durations");
1326void PHD2::requestLockPosition()
1328 sendPHD2Request(
"get_lock_position");
1335void PHD2::requestPixelScale()
1337 sendPHD2Request(
"get_pixel_scale");
1346void PHD2::requestStarImage(
int size)
1348 if (starImageRequested)
1350 if (Options::verboseLogging())
1351 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: skip extra star image request";
1357 sendPHD2Request(
"get_star_image", args2);
1359 starImageRequested =
true;
1367 if (state == GUIDING)
1369 emit newLog(
i18n(
"PHD2: Guiding is already running."));
1370 emit newStatus(Ekos::GUIDE_GUIDING);
1374 if (connection != EQUIPMENT_CONNECTED)
1376 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1377 emit newStatus(Ekos::GUIDE_ABORTED);
1384 settle.
insert(
"pixels",
static_cast<double>(Options::ditherThreshold()));
1385 settle.
insert(
"time",
static_cast<int>(Options::ditherSettle()));
1386 settle.
insert(
"timeout",
static_cast<int>(Options::ditherTimeout()));
1396 sendPHD2Request(
"guide", args);
1405 sendPHD2Request(
"loop");
1411void PHD2::connectEquipment(
bool enable)
1413 if (connection == EQUIPMENT_CONNECTED && enable ==
true)
1416 if (connection == EQUIPMENT_DISCONNECTED && enable ==
false)
1419 if (setConnectedRetries++ > MAX_SET_CONNECTED_RETRIES)
1421 setConnectedRetries = 0;
1422 connection = EQUIPMENT_DISCONNECTED;
1423 emit newStatus(Ekos::GUIDE_DISCONNECTED);
1435 emit newLog(
i18n(
"PHD2: Connecting Equipment. . ."));
1437 emit newLog(
i18n(
"PHD2: Disconnecting Equipment. . ."));
1439 sendPHD2Request(
"set_connected", args);
1443void PHD2::requestSetDEGuideMode(
bool deEnabled,
bool nEnabled,
1450 if(nEnabled && sEnabled)
1464 sendPHD2Request(
"set_dec_guide_mode", args);
1468void PHD2::requestSetExposureTime(
int time)
1472 sendPHD2Request(
"set_exposure", args);
1476void PHD2::setLockPosition(
double x,
double y)
1480 args << x << y <<
false;
1481 sendPHD2Request(
"set_lock_position", args);
1489 if (connection != EQUIPMENT_CONNECTED)
1491 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1492 emit newStatus(Ekos::GUIDE_ABORTED);
1503 sendPHD2Request(
"set_paused", args);
1505 if (abortTimer->isActive())
1508 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: Lost star timeout cancelled.";
1518 if (connection != EQUIPMENT_CONNECTED)
1520 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1521 emit newStatus(Ekos::GUIDE_ABORTED);
1530 sendPHD2Request(
"set_paused", args);
1532 if (state == LOSTLOCK)
1534 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: Lost star timeout restarted.";
1535 abortTimer->start(
static_cast<int>(Options::guideLostStarTimeout()) * 1000);
1547 if (connection != EQUIPMENT_CONNECTED)
1549 emit newLog(
i18n(
"PHD2 Error: Equipment not connected."));
1550 emit newStatus(Ekos::GUIDE_ABORTED);
1556 sendPHD2Request(
"stop_capture");
1561bool PHD2::calibrate()
1570void PHD2::sendRpcCall(QJsonObject &call, PHD2ResultType resultType)
1572 assert(resultType != NO_RESULT);
1573 assert(pendingRpcResultType == NO_RESULT);
1577 int rpcId = nextRpcId++;
1578 call.
insert(
"id", rpcId);
1582 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: request:" << request;
1586 qint64
const n = tcpSocket->write(request);
1588 if ((
int) n == request.
size())
1591 pendingRpcId = rpcId;
1592 pendingRpcResultType = resultType;
1596 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: unexpected short write:" << n <<
"bytes of" << request.
size();
1601void PHD2::sendNextRpcCall()
1603 if (pendingRpcResultType != NO_RESULT)
1606 if (rpcRequestQueue.empty())
1609 RpcCall &call = rpcRequestQueue.front();
1610 sendRpcCall(call.call, call.resultType);
1611 rpcRequestQueue.pop_front();
1614void PHD2::sendPHD2Request(
const QString &method,
const QJsonArray &args)
1616 assert(methodResults.contains(method));
1618 PHD2ResultType resultType = methodResults[method];
1620 QJsonObject jsonRPC;
1622 jsonRPC.
insert(
"jsonrpc",
"2.0");
1623 jsonRPC.
insert(
"method", method);
1626 jsonRPC.
insert(
"params", args);
1628 if (pendingRpcResultType == NO_RESULT)
1631 sendRpcCall(jsonRPC, resultType);
1638 if (Options::verboseLogging())
1639 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: defer call" << method;
1641 rpcRequestQueue.
push_back(RpcCall(jsonRPC, resultType));
1645PHD2::PHD2ResultType PHD2::takeRequestFromList(
const QJsonObject &response)
1647 if (Q_UNLIKELY(!response.
contains(
"id")))
1649 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: ignoring unexpected response with no id";
1653 int id = response[
"id"].toInt();
1655 if (Q_UNLIKELY(
id != pendingRpcId))
1659 qCDebug(KSTARS_EKOS_GUIDE) <<
"PHD2: ignoring unexpected response with id" << id;
1663 PHD2ResultType val = pendingRpcResultType;
1664 pendingRpcResultType = NO_RESULT;
Q_SCRIPTABLE Q_NOREPLY void setExposure(double value)
DBUS interface function.
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
void errorOccurred(QAbstractSocket::SocketError socketError)
QByteArray & append(QByteArrayView data)
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
QByteArray fromRawData(const char *data, qsizetype size)
bool isEmpty() const const
qsizetype size() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
bool contains(QLatin1StringView key) const const
iterator insert(QLatin1StringView key, const QJsonValue &value)
QJsonValue value(QLatin1StringView key) const const
QString errorString() const const
void append(QList< T > &&value)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString number(double n, char format, int precision)