1 /*
2     SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include "ui_guide.h"
10 #include "guideinterface.h"
11 #include "guidestatewidget.h"
12 #include "ekos/ekos.h"
13 #include "indi/indiccd.h"
14 #include "indi/inditelescope.h"
15 #include "ekos/auxiliary/darkprocessor.h"
16 
17 #include <QTime>
18 #include <QTimer>
19 #include <QtDBus>
20 
21 #include <random>
22 
23 class QProgressIndicator;
24 class QTabWidget;
25 
26 class FITSView;
27 class FITSViewer;
28 class ScrollGraph;
29 class GuideView;
30 
31 namespace Ekos
32 {
33 class OpsCalibration;
34 class OpsGuide;
35 class OpsDither;
36 class OpsGPG;
37 class InternalGuider;
38 class PHD2;
39 class LinGuider;
40 
41 /**
42  * @class Guide
43  * @short Performs calibration and autoguiding using an ST4 port or directly via the INDI driver. Can be used with the following external guiding applications:
44  * PHD2
45  * LinGuider
46  *
47  * @author Jasem Mutlaq
48  * @version 1.4
49  */
50 class Guide : public QWidget, public Ui::Guide
51 {
52         Q_OBJECT
53         Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Guide")
54         Q_PROPERTY(Ekos::GuideState status READ status NOTIFY newStatus)
55         Q_PROPERTY(QStringList logText READ logText NOTIFY newLog)
56         Q_PROPERTY(QString camera READ camera WRITE setCamera)
57         Q_PROPERTY(QString st4 READ st4 WRITE setST4)
58         Q_PROPERTY(double exposure READ exposure WRITE setExposure)
59         Q_PROPERTY(QList<double> axisDelta READ axisDelta NOTIFY newAxisDelta)
60         Q_PROPERTY(QList<double> axisSigma READ axisSigma NOTIFY newAxisSigma)
61 
62     public:
63         Guide();
64         ~Guide();
65 
66         enum GuiderStage
67         {
68             CALIBRATION_STAGE,
69             GUIDE_STAGE
70         };
71         enum GuiderType
72         {
73             GUIDE_INTERNAL,
74             GUIDE_PHD2,
75             GUIDE_LINGUIDER
76         };
77 
78         /** @defgroup GuideDBusInterface Ekos DBus Interface - Capture Module
79              * Ekos::Guide interface provides advanced scripting capabilities to calibrate and guide a mount via a CCD camera.
80             */
81 
82         /*@{*/
83 
84         /** DBUS interface function.
85              * select the CCD device from the available CCD drivers.
86              * @param device The CCD device name
87              * @return Returns true if CCD device is found and set, false otherwise.
88              */
89         Q_SCRIPTABLE bool setCamera(const QString &device);
90         Q_SCRIPTABLE QString camera();
91 
92         /** DBUS interface function.
93              * select the ST4 device from the available ST4 drivers.
94              * @param device The ST4 device name
95              * @return Returns true if ST4 device is found and set, false otherwise.
96              */
97         Q_SCRIPTABLE bool setST4(const QString &device);
98         Q_SCRIPTABLE QString st4();
99 
100         /** DBUS interface function.
101              * @return Returns List of registered ST4 devices.
102              */
103         Q_SCRIPTABLE QStringList getST4Devices();
104 
105         /** DBUS interface function.
106          * @brief connectGuider Establish connection to guider application. For internal guider, this always returns true.
107          * @return True if successfully connected, false otherwise.
108          */
109         Q_SCRIPTABLE bool connectGuider();
110 
111         /** DBUS interface function.
112          * @brief disconnectGuider Disconnect from guider application. For internal guider, this always returns true.
113          * @return True if successfully disconnected, false otherwise.
114          */
115         Q_SCRIPTABLE bool disconnectGuider();
116 
117         /**
118              * @brief getStatus Return guide module status
119              * @return state of guide module from Ekos::GuideState
120              */
status()121         Q_SCRIPTABLE Ekos::GuideState status()
122         {
123             return state;
124         }
125 
126         /** DBUS interface function.
127              * Set CCD exposure value
128              * @param value exposure value in seconds.
129              */
130         Q_SCRIPTABLE Q_NOREPLY void setExposure(double value);
exposure()131         double exposure()
132         {
133             return exposureIN->value();
134         }
135 
136         /** DBUS interface function.
137              * Set image filter to apply to the image after capture.
138              * @param value Image filter (Auto Stretch, High Contrast, Equalize, High Pass)
139              */
140         Q_SCRIPTABLE Q_NOREPLY void setImageFilter(const QString &value);
141 
142         /** DBUS interface function.
143              * Set calibration Use Two Axis option. The options must be set before starting the calibration operation. If no options are set, the options loaded from the user configuration are used.
144              * @param enable if true, calibration will be performed in both RA and DEC axis. Otherwise, only RA axis will be calibrated.
145              */
146         Q_SCRIPTABLE Q_NOREPLY void setCalibrationTwoAxis(bool enable);
147 
148         /** DBUS interface function.
149              * Set auto star calibration option. The options must be set before starting the calibration operation. If no options are set, the options loaded from the user configuration are used.
150              * @param enable if true, Ekos will attempt to automatically select the best guide star and proceed with the calibration procedure.
151              */
152         Q_SCRIPTABLE Q_NOREPLY void setCalibrationAutoStar(bool enable);
153 
154         /** DBUS interface function.
155              * In case of automatic star selection, calculate the appropriate square size given the selected star width. The options must be set before starting the calibration operation. If no options are set, the options loaded from the user configuration are used.
156              * @param enable if true, Ekos will attempt to automatically select the best square size for calibration and guiding phases.
157              */
158         Q_SCRIPTABLE Q_NOREPLY void setCalibrationAutoSquareSize(bool enable);
159 
160         /** DBUS interface function.
161              * Set calibration dark frame option. The options must be set before starting the calibration operation. If no options are set, the options loaded from the user configuration are used.
162              * @param enable if true, a dark frame will be captured to subtract from the light frame.
163              */
164         Q_SCRIPTABLE Q_NOREPLY void setDarkFrameEnabled(bool enable);
165 
166         /** DBUS interface function.
167              * Set calibration parameters.
168              * @param pulseDuration Pulse duration in milliseconds to use in the calibration steps.
169              */
170         Q_SCRIPTABLE Q_NOREPLY void setCalibrationPulseDuration(int pulseDuration);
171 
172         /** DBUS interface function.
173              * Set guiding box size. The options must be set before starting the guiding operation. If no options are set, the options loaded from the user configuration are used.
174              * @param index box size index (0 to 4) for box size from 8 to 128 pixels. The box size should be suitable for the size of the guide star selected. The boxSize is also used to select the subframe size around the guide star. Default is 16 pixels
175              */
176         Q_SCRIPTABLE Q_NOREPLY void setGuideBoxSizeIndex(int index);
177 
178         /** DBUS interface function.
179              * Set guiding algorithm. The options must be set before starting the guiding operation. If no options are set, the options loaded from the user configuration are used.
180              * @param index Select the algorithm used to calculate the centroid of the guide star (0 --> Smart, 1 --> Fast, 2 --> Auto, 3 --> No thresh).
181              */
182         Q_SCRIPTABLE Q_NOREPLY void setGuideAlgorithmIndex(int index);
183 
184         /** DBUS interface function.
185              * Enable or disables dithering. Set dither range
186              * @param enable if true, dithering is enabled and is performed after each exposure is complete. Otherwise, dithering is disabled.
187              * @param value dithering range in pixels. Ekos will move the guide star in a random direction for the specified dithering value in pixels.
188              */
189         Q_SCRIPTABLE Q_NOREPLY void setDitherSettings(bool enable, double value);
190 
191         /** @}*/
192 
193         void addCamera(ISD::GDInterface *newCCD);
194         void configurePHD2Camera();
195         void setTelescope(ISD::GDInterface *newTelescope);
196         void addST4(ISD::ST4 *setST4);
197         void setAO(ISD::ST4 *newAO);
198         void removeDevice(ISD::GDInterface *device);
199 
200         bool isDithering();
201 
202         void addGuideHead(ISD::GDInterface *newCCD);
203         void syncTelescopeInfo();
204         void syncCCDInfo();
205 
206         /**
207              * @brief clearLog As the name suggests
208              */
209         void clearLog();
logText()210         QStringList logText()
211         {
212             return m_LogText;
213         }
214 
215         /**
216              * @return Return current log text of guide module
217              */
getLogText()218         QString getLogText()
219         {
220             return m_LogText.join("\n");
221         }
222 
223         /**
224              * @brief getStarPosition Return star center as selected by the user or auto-detected by KStars
225              * @return QVector3D of starCenter. The 3rd parameter is used to store current bin settings and in unrelated to the star position.
226              */
getStarPosition()227         QVector3D getStarPosition()
228         {
229             return starCenter;
230         }
231 
232         // Tracking Box
getTrackingBoxSize()233         int getTrackingBoxSize()
234         {
235             return boxSizeCombo->currentText().toInt();
236         }
237 
getGuider()238         GuideInterface *getGuider()
239         {
240             return guider;
241         }
242 
243         QJsonObject getSettings() const;
244         void setSettings(const QJsonObject &settings);
245 
246     public slots:
247 
248         /** DBUS interface function.
249              * Start the autoguiding operation.
250              * @return Returns true if guiding started successfully, false otherwise.
251              */
252         Q_SCRIPTABLE bool guide();
253 
254         /** DBUS interface function.
255              * Stop any active calibration, guiding, or dithering operation
256              * @return Returns true if operation is stopped successfully, false otherwise.
257              */
258         Q_SCRIPTABLE bool abort();
259 
260         /** DBUS interface function.
261              * Start the calibration operation. Note that this will not start guiding automatically.
262              * @return Returns true if calibration started successfully, false otherwise.
263              */
264         Q_SCRIPTABLE bool calibrate();
265 
266         /** DBUS interface function.
267              * Clear calibration data. Next time any guide operation is performed, a calibration is first started.
268              */
269         Q_SCRIPTABLE Q_NOREPLY void clearCalibration();
270 
271         /** DBUS interface function.
272              * @brief dither Starts dithering process in a random direction restricted by the number of pixels specified in dither options
273              * @return True if dither started successfully, false otherwise.
274              */
275         Q_SCRIPTABLE bool dither();
276 
277         /** DBUS interface function.
278              * @brief suspend Suspend autoguiding
279              * @return True if successful, false otherwise.
280              */
281         Q_SCRIPTABLE bool suspend();
282 
283         /** DBUS interface function.
284              * @brief resume Resume autoguiding
285              * @return True if successful, false otherwise.
286              */
287         Q_SCRIPTABLE bool resume();
288 
289         /** DBUS interface function.
290              * Capture a guide frame
291              * @return Returns true if capture command is sent successfully to INDI server.
292              */
293         Q_SCRIPTABLE bool capture();
294 
295         /** DBUS interface function.
296              * Loop frames specified by the exposure control continuously until stopped.
297              */
298         Q_SCRIPTABLE Q_NOREPLY void loop();
299 
300         /** DBUS interface function.
301              * Set guiding options. The options must be set before starting the guiding operation. If no options are set, the options loaded from the user configuration are used.
302              * @param enable if true, it will select a subframe around the guide star depending on the boxSize size.
303              */
304         Q_SCRIPTABLE Q_NOREPLY void setSubFrameEnabled(bool enable);
305 
306         /** DBUS interface function.
307              * Selects which guiding process to utilize for calibration & guiding.
308              * @param type Type of guider process to use. 0 for internal guider, 1 for external PHD2, 2 for external lin_guider. Pass -1 to select default guider in options.
309              * @return True if guiding is switched to the new requested type. False otherwise.
310              */
311         Q_SCRIPTABLE bool setGuiderType(int type);
312 
313         /** DBUS interface function.
314          * @brief axisDelta returns the last immediate axis delta deviation in arcseconds. This is the deviation of locked star position when guiding started.
315          * @return List of doubles. First member is RA deviation. Second member is DE deviation.
316          */
317         Q_SCRIPTABLE QList<double> axisDelta();
318 
319         /** DBUS interface function.
320          * @brief axisSigma return axis sigma deviation in arcseconds RMS. This is the RMS deviation of locked star position when guiding started.
321          * @return List of doubles. First member is RA deviation. Second member is DE deviation.
322          */
323         Q_SCRIPTABLE QList<double> axisSigma();
324 
325         /**
326               * @brief checkCCD Check all CCD parameters and ensure all variables are updated to reflect the selected CCD
327               * @param ccdNum CCD index number in the CCD selection combo box
328               */
329         void checkCCD(int ccdNum = -1);
330 
331         /**
332              * @brief checkExposureValue This function is called by the INDI framework whenever there is a new exposure value. We use it to know if there is a problem with the exposure
333              * @param targetChip Chip for which the exposure is undergoing
334              * @param exposure numbers of seconds left in the exposure
335              * @param expState State of the exposure property
336              */
337         void checkExposureValue(ISD::CCDChip *targetChip, double exposure, IPState expState);
338 
339         /**
340              * @brief newFITS is called by the INDI framework whenever there is a new BLOB arriving
341              */
342         void processData(const QSharedPointer<FITSData> &data);
343 
344         /**
345              * @brief setST4 Sets a new ST4 device from the combobox index
346              * @param index Index of selected ST4 in the combobox
347              */
348         void setST4(int index);
349 
350         /**
351              * @brief Set telescope and guide scope info. All measurements is in millimeters.
352              * @param primaryFocalLength Primary Telescope Focal Length. Set to 0 to skip setting this value.
353              * @param primaryAperture Primary Telescope Aperture. Set to 0 to skip setting this value.
354              * @param guideFocalLength Guide Telescope Focal Length. Set to 0 to skip setting this value.
355              * @param guideAperture Guide Telescope Aperture. Set to 0 to skip setting this value.
356              */
357         void setTelescopeInfo(double primaryFocalLength, double primaryAperture, double guideFocalLength, double guideAperture);
358 
359         // This Function will allow PHD2 to update the exposure values to the recommended ones.
360         QString setRecommendedExposureValues(QList<double> values);
361 
362         // Append Log entry
363         void appendLogText(const QString &);
364 
365         // Update Guide module status
366         void setStatus(Ekos::GuideState newState);
367 
368         // Update Capture Module status
369         void setCaptureStatus(Ekos::CaptureState newState);
370         // Update Mount module status
371         void setMountStatus(ISD::Telescope::Status newState);
372         void setMountCoords(const SkyPoint &position, ISD::Telescope::PierSide pierSide, const dms &ha);
373 
374         // Update Pier Side
375         void setPierSide(ISD::Telescope::PierSide newSide);
376 
377         // Star Position
378         void setStarPosition(const QVector3D &newCenter, bool updateNow);
379 
380         // Capture
381         void setCaptureComplete();
382 
383         // Send pulse to ST4 driver
384         bool sendPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs);
385         bool sendPulse(GuideDirection dir, int msecs);
386 
387         /**
388              * @brief setDECSwap Change ST4 declination pulse direction. +DEC pulses increase DEC if swap is OFF. When on +DEC pulses result in decreasing DEC.
389              * @param enable True to enable DEC swap. Off to disable it.
390              */
391         void setDECSwap(bool enable);
392 
393         //plot slots
394         void handleVerticalPlotSizeChange();
395         void handleHorizontalPlotSizeChange();
396         void clearGuideGraphs();
397         void clearCalibrationGraphs();
398         void slotAutoScaleGraphs();
399         void buildTarget();
400         void guideHistory();
401         void setLatestGuidePoint(bool isChecked);
402 
403         void updateDirectionsFromPHD2(const QString &mode);
404 
405         void guideAfterMeridianFlip();
406 
407     protected slots:
408         void updateTelescopeType(int index);
409         void updateCCDBin(int index);
410 
411         /**
412                 * @brief processCCDNumber Process number properties arriving from CCD. Currently, binning changes are processed.
413                 * @param nvp pointer to number property.
414                 */
415         void processCCDNumber(INumberVectorProperty *nvp);
416 
417         /**
418              * @brief setTrackingStar Gets called when the user select a star in the guide frame
419              * @param x X coordinate of star
420              * @param y Y coordinate of star
421              */
422         void setTrackingStar(int x, int y);
423 
424         void saveDefaultGuideExposure();
425 
426         void updateTrackingBoxSize(int currentIndex);
427 
428         //void onXscaleChanged( int i );
429         //void onYscaleChanged( int i );
430         void onThresholdChanged(int i);
431         void onEnableDirRA(bool enable);
432         void onEnableDirDEC(bool enable);
433         void syncSettings();
434 
435         void setAxisDelta(double ra, double de);
436         void setAxisSigma(double ra, double de);
437         void setAxisPulse(double ra, double de);
438         void setSNR(double snr);
439         void calibrationUpdate(GuideInterface::CalibrationUpdateType type, const QString &message = QString(""), double dx = 0,
440                                double dy = 0);
441 
442         void processGuideOptions();
443         void configSEPMultistarOptions();
444 
445         void onControlDirectionChanged(bool enable);
446         void updatePHD2Directions();
447 
448         void showFITSViewer();
449 
450         void processCaptureTimeout();
451 
452         void nonGuidedDither();
453 
454     signals:
455         void newLog(const QString &text);
456         void newStatus(Ekos::GuideState status);
457 
458         void newImage(FITSView *view);
459         void newStarPixmap(QPixmap &);
460 
461         // Immediate deviations in arcsecs
462         void newAxisDelta(double ra, double de);
463         // Sigma deviations in arcsecs RMS
464         void newAxisSigma(double ra, double de);
465 
466         void guideStats(double raError, double decError, int raPulse, int decPulse,
467                         double snr, double skyBg, int numStars);
468 
469         void guideChipUpdated(ISD::CCDChip *);
470         void settingsUpdated(const QJsonObject &settings);
471         void driverTimedout(const QString &deviceName);
472 
473     private slots:
474         void setDefaultST4(const QString &driver);
475         void setDefaultCCD(const QString &ccd);
476 
477     private:
478 
479         void resizeEvent(QResizeEvent *event) override;
480 
481         /**
482              * @brief updateGuideParams Update the guider and frame parameters due to any changes in the mount and/or ccd frame
483              */
484         void updateGuideParams();
485 
486         /**
487              * @brief syncTrackingBoxPosition Sync the tracking box to the current selected star center
488              */
489         void syncTrackingBoxPosition();
490 
491         /**
492              * @brief loadSettings Loads and applies all settings from KStars options
493              */
494         void loadSettings();
495 
496         /**
497              * @brief saveSettings Saves all current settings into KStars options
498              */
499         void saveSettings();
500 
501         /**
502              * @brief setBusy Indicate busy status within the module visually
503              * @param enable True if module is busy, false otherwise
504              */
505         void setBusy(bool enable);
506 
507         /**
508          * @brief setBLOBEnabled Enable or disable BLOB reception from current CCD if using external guider
509          * @param enable True to enable BLOB reception, false to disable BLOB reception
510          * @param name CCD to enable to disable. If empty (default), then action is applied to all CCDs.
511          */
512         void setExternalGuiderBLOBEnabled(bool enable);
513 
514         /**
515          * @brief prepareCapture Set common settings for capture for guide module
516          * @param targetChip target Chip
517          */
518         void prepareCapture(ISD::CCDChip *targetChip);
519 
520 
521         void handleManualDither();
522 
523         // Operation stack
524         void buildOperationStack(GuideState operation);
525         bool executeOperationStack();
526         bool executeOneOperation(GuideState operation);
527 
528         // Init Functions
529         void initPlots();
530         void initDriftGraph();
531         void initCalibrationPlot();
532         void initView();
533         void initConnections();
534 
535         bool captureOneFrame();
536 
537         // Driver
538         void reconnectDriver(const QString &camera, const QString &via);
539 
540         // Operation Stack
541         QStack<GuideState> operationStack;
542 
543         // Devices
544         ISD::CCD *currentCCD { nullptr };
545         QString lastPHD2CameraName; //This is for the configure PHD2 camera method.
546         ISD::Telescope *currentTelescope { nullptr };
547         ISD::ST4 *ST4Driver { nullptr };
548         ISD::ST4 *GuideDriver { nullptr };
549 
550         // Device Containers
551         QList<ISD::ST4 *> ST4List;
552         QList<ISD::CCD *> CCDs;
553 
554         // Guider process
555         GuideInterface *guider { nullptr };
556         GuiderType guiderType { GUIDE_INTERNAL };
557 
558         // Star
559         QVector3D starCenter;
560 
561         // Guide Params
562         int guideBinIndex { 0 };    // Selected or saved binning for guiding
563         int guideFilterIndex { 0 }; // Selected or saved filter for post-treatment
564         double ccdPixelSizeX { -1 };
565         double ccdPixelSizeY { -1 };
566         double aperture { -1 };
567         double focal_length { -1 };
568         double guideDeviationRA { 0 };
569         double guideDeviationDEC { 0 };
570         double pixScaleX { -1 };
571         double pixScaleY { -1 };
572 
573         // State
574         GuideState state { GUIDE_IDLE };
575         GuideStateWidget *guideStateWidget { nullptr };
576 
577         // Guide timer
578         QTime guideTimer;
579 
580         // Capture timeout timer
581         QTimer captureTimeout;
582         uint8_t m_CaptureTimeoutCounter { 0 };
583         uint8_t m_DeviceRestartCounter { 0 };
584 
585         // Pulse Timer
586         QTimer pulseTimer;
587 
588         // Log
589         QStringList m_LogText;
590 
591         // Misc
592         bool useGuideHead { false };
593 
594         // Progress Activity Indicator
595         QProgressIndicator *pi { nullptr };
596 
597         // Options
598         OpsCalibration *opsCalibration { nullptr };
599         OpsGuide *opsGuide { nullptr };
600         OpsDither *opsDither { nullptr };
601         OpsGPG *opsGPG { nullptr };
602 
603         // Guide Frame
604         GuideView *guideView { nullptr };
605 
606         // Calibration done already?
607         bool calibrationComplete { false };
608 
609         // Was the modified frame subFramed?
610         bool subFramed { false };
611 
612         // CCD Chip frame settings
613         QMap<ISD::CCDChip *, QVariantMap> frameSettings;
614 
615         // Profile Pixmap
616         QPixmap profilePixmap;
617         // drift plot
618         QPixmap driftPlotPixmap;
619 
620         // Flag to start auto calibration followed immediately by guiding
621         //bool autoCalibrateGuide { false };
622 
623         // Pointers of guider processes
624         QPointer<InternalGuider> internalGuider;
625         QPointer<PHD2> phd2Guider;
626         QPointer<LinGuider> linGuider;
627         QPointer<FITSViewer> fv;
628         QSharedPointer<FITSData> m_ImageData;
629 
630         // Dark Processor
631         QPointer<DarkProcessor> m_DarkProcessor;
632 
633         double primaryFL = -1, primaryAperture = -1, guideFL = -1, guideAperture = -1;
634         ISD::Telescope::Status m_MountStatus { ISD::Telescope::MOUNT_IDLE };
635 
636         bool graphOnLatestPt = true;
637 
638         //This is for enforcing the PHD2 Star lock when Guide is pressed,
639         //autostar is not selected, and the user has chosen a star.
640         //This connection storage is so that the connection can be disconnected after enforcement
641         QMetaObject::Connection guideConnect;
642 
643         QCPItemText *calLabel  { nullptr };
644 
645         // The scales of these zoom levels are defined in Guide::zoomX().
646         static constexpr int defaultXZoomLevel = 3;
647         int driftGraphZoomLevel {defaultXZoomLevel};
648 
649 
650         // The accumulated non-guided dither offsets (in milliseconds) in the RA and DEC directions.
651         int nonGuidedDitherRaOffsetMsec = 0, nonGuidedDitherDecOffsetMsec = 0;
652 
653         // Random generator for non guided dithering
654         std::mt19937 nonGuidedPulseGenerator;
655 
656         // Flag to check if random generator for non guided dithering is initialized.
657         bool isNonGuidedDitherInitialized = false;
658 
659         // Reset non guided dithering properties and initialize the random generator seed if not already done.
660         // Should be called in Guide::Guide() for initial seed initialization, and then in setCaptureStatus to reset accumulated drift
661         // every time a capture task is completed or aborted.
662         void resetNonGuidedDither();
663 };
664 }
665