File indexing completed on 2024-04-28 15:09:52

0001 /*
0002     SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include "ui_guide.h"
0010 #include "guideinterface.h"
0011 #include "ekos/ekos.h"
0012 #include "indi/indicamera.h"
0013 #include "indi/indimount.h"
0014 
0015 #include <QTime>
0016 #include <QTimer>
0017 
0018 #include <random>
0019 
0020 class QProgressIndicator;
0021 class QTabWidget;
0022 
0023 class FITSView;
0024 class FITSViewer;
0025 class ScrollGraph;
0026 class GuideView;
0027 
0028 namespace Ekos
0029 {
0030 class OpsCalibration;
0031 class OpsGuide;
0032 class OpsDither;
0033 class OpsGPG;
0034 class InternalGuider;
0035 class PHD2;
0036 class LinGuider;
0037 class GuideStateWidget;
0038 class ManualPulse;
0039 class DarkProcessor;
0040 
0041 /**
0042  * @class Guide
0043  * @short Performs calibration and autoguiding using an ST4 port or directly via the INDI driver. Can be used with the following external guiding applications:
0044  * PHD2
0045  * LinGuider
0046  *
0047  * @author Jasem Mutlaq
0048  * @version 1.4
0049  */
0050 class Guide : public QWidget, public Ui::Guide
0051 {
0052         Q_OBJECT
0053         Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Guide")
0054         Q_PROPERTY(Ekos::GuideState status READ status NOTIFY newStatus)
0055         Q_PROPERTY(QStringList logText READ logText NOTIFY newLog)
0056         Q_PROPERTY(QString opticalTrain READ opticalTrain WRITE setOpticalTrain)
0057         Q_PROPERTY(QString camera READ camera)
0058         Q_PROPERTY(QString guider READ guider)
0059         Q_PROPERTY(double exposure READ exposure WRITE setExposure)
0060         Q_PROPERTY(QList<double> axisDelta READ axisDelta NOTIFY newAxisDelta)
0061         Q_PROPERTY(QList<double> axisSigma READ axisSigma NOTIFY newAxisSigma)
0062 
0063     public:
0064         Guide();
0065         ~Guide();
0066 
0067         enum GuiderStage
0068         {
0069             CALIBRATION_STAGE,
0070             GUIDE_STAGE
0071         };
0072         enum GuiderType
0073         {
0074             GUIDE_INTERNAL,
0075             GUIDE_PHD2,
0076             GUIDE_LINGUIDER
0077         };
0078 
0079         /** @defgroup GuideDBusInterface Ekos DBus Interface - Capture Module
0080              * Ekos::Guide interface provides advanced scripting capabilities to calibrate and guide a mount via a CCD camera.
0081             */
0082 
0083         /*@{*/
0084 
0085         /** DBUS interface function.
0086              * select the CCD device from the available CCD drivers.
0087              * @param device The CCD device name
0088              * @return Returns true if CCD device is found and set, false otherwise.
0089              */
0090         Q_SCRIPTABLE QString camera();
0091 
0092         /** DBUS interface function.
0093              * select the ST4 device from the available ST4 drivers.
0094              * @param device The ST4 device name
0095              * @return Returns true if ST4 device is found and set, false otherwise.
0096              */
0097         Q_SCRIPTABLE QString guider();
0098 
0099         /** DBUS interface function.
0100          * @brief connectGuider Establish connection to guider application. For internal guider, this always returns true.
0101          * @return True if successfully connected, false otherwise.
0102          */
0103         Q_SCRIPTABLE bool connectGuider();
0104 
0105         /** DBUS interface function.
0106          * @brief disconnectGuider Disconnect from guider application. For internal guider, this always returns true.
0107          * @return True if successfully disconnected, false otherwise.
0108          */
0109         Q_SCRIPTABLE bool disconnectGuider();
0110 
0111         /**
0112              * @brief getStatus Return guide module status
0113              * @return state of guide module from Ekos::GuideState
0114              */
0115         Q_SCRIPTABLE Ekos::GuideState status()
0116         {
0117             return m_State;
0118         }
0119 
0120         /** DBUS interface function.
0121              * Set CCD exposure value
0122              * @param value exposure value in seconds.
0123              */
0124         Q_SCRIPTABLE Q_NOREPLY void setExposure(double value);
0125         double exposure()
0126         {
0127             return guideExposure->value();
0128         }
0129 
0130         /** DBUS interface function.
0131              * 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.
0132              * @param enable if true, a dark frame will be captured to subtract from the light frame.
0133              */
0134         Q_SCRIPTABLE Q_NOREPLY void setDarkFrameEnabled(bool enable);        
0135 
0136         /** @}*/
0137 
0138         /**
0139          * @brief Add new Camera
0140          * @param device pointer to camera device.
0141          * @return True if added successfully, false if duplicate or failed to add.
0142         */
0143         bool setCamera(ISD::Camera *device);
0144 
0145 
0146         /**
0147          * @brief Add new Mount
0148          * @param device pointer to Mount device.
0149          * @return True if added successfully, false if duplicate or failed to add.
0150         */
0151         bool setMount(ISD::Mount *device);
0152 
0153         /**
0154          * @brief Add new Guider
0155          * @param device pointer to Guider device.
0156          * @return True if added successfully, false if duplicate or failed to add.
0157         */
0158         bool setGuider(ISD::Guider *device);
0159 
0160         /**
0161          * @brief Add new Adaptive Optics
0162          * @param device pointer to AO device.
0163          * @return True if added successfully, false if duplicate or failed to add.
0164         */
0165         bool setAdaptiveOptics(ISD::AdaptiveOptics *device);
0166 
0167         void removeDevice(const QSharedPointer<ISD::GenericDevice> &device);
0168         void configurePHD2Camera();
0169 
0170         bool isDithering();
0171         void syncTelescopeInfo();
0172         void syncCameraInfo();
0173 
0174         /**
0175              * @brief clearLog As the name suggests
0176              */
0177         void clearLog();
0178         QStringList logText()
0179         {
0180             return m_LogText;
0181         }
0182 
0183         /**
0184              * @return Return current log text of guide module
0185              */
0186         QString getLogText()
0187         {
0188             return m_LogText.join("\n");
0189         }
0190 
0191         /**
0192              * @brief getStarPosition Return star center as selected by the user or auto-detected by KStars
0193              * @return QVector3D of starCenter. The 3rd parameter is used to store current bin settings and in unrelated to the star position.
0194              */
0195         QVector3D getStarPosition()
0196         {
0197             return starCenter;
0198         }
0199 
0200         // Tracking Box
0201         int getTrackingBoxSize()
0202         {
0203             return guideSquareSize->currentText().toInt();
0204         }
0205 
0206         GuideInterface *getGuiderInstance()
0207         {
0208             return m_GuiderInstance;
0209         }
0210 
0211         // Settings
0212         QVariantMap getAllSettings() const;
0213         void setAllSettings(const QVariantMap &settings);
0214 
0215     public slots:
0216 
0217         /** DBUS interface function.
0218              * Start the autoguiding operation.
0219              * @return Returns true if guiding started successfully, false otherwise.
0220              */
0221         Q_SCRIPTABLE bool guide();
0222 
0223         /** DBUS interface function.
0224              * Stop any active calibration, guiding, or dithering operation
0225              * @return Returns true if operation is stopped successfully, false otherwise.
0226              */
0227         Q_SCRIPTABLE bool abort();
0228 
0229         /** DBUS interface function.
0230              * Start the calibration operation. Note that this will not start guiding automatically.
0231              * @return Returns true if calibration started successfully, false otherwise.
0232              */
0233         Q_SCRIPTABLE bool calibrate();
0234 
0235         /** DBUS interface function.
0236              * Clear calibration data. Next time any guide operation is performed, a calibration is first started.
0237              */
0238         Q_SCRIPTABLE Q_NOREPLY void clearCalibration();
0239 
0240         /** DBUS interface function.
0241              * @brief dither Starts dithering process in a random direction restricted by the number of pixels specified in dither options
0242              * @return True if dither started successfully, false otherwise.
0243              */
0244         Q_SCRIPTABLE bool dither();
0245 
0246         /** DBUS interface function.
0247              * @brief suspend Suspend autoguiding
0248              * @return True if successful, false otherwise.
0249              */
0250         Q_SCRIPTABLE bool suspend();
0251 
0252         /** DBUS interface function.
0253              * @brief resume Resume autoguiding
0254              * @return True if successful, false otherwise.
0255              */
0256         Q_SCRIPTABLE bool resume();
0257 
0258         /** DBUS interface function.
0259              * Capture a guide frame
0260              * @return Returns true if capture command is sent successfully to INDI server.
0261              */
0262         Q_SCRIPTABLE bool capture();
0263 
0264         /** DBUS interface function.
0265              * Loop frames specified by the exposure control continuously until stopped.
0266              */
0267         Q_SCRIPTABLE Q_NOREPLY void loop();
0268 
0269         /** DBUS interface function.
0270              * 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.
0271              * @param enable if true, it will select a subframe around the guide star depending on the boxSize size.
0272              */
0273         Q_SCRIPTABLE Q_NOREPLY void setSubFrameEnabled(bool enable);
0274 
0275         /** DBUS interface function.
0276              * 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.
0277              * @param enable if true, it will select a subframe around the guide star depending on the boxSize size.
0278              */
0279         Q_SCRIPTABLE Q_NOREPLY void setAutoStarEnabled(bool enable);
0280 
0281         /** DBUS interface function.
0282              * Selects which guiding process to utilize for calibration & guiding.
0283              * @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.
0284              * @return True if guiding is switched to the new requested type. False otherwise.
0285              */
0286         Q_SCRIPTABLE bool setGuiderType(int type);
0287 
0288         /** DBUS interface function.
0289          * @brief axisDelta returns the last immediate axis delta deviation in arcseconds. This is the deviation of locked star position when guiding started.
0290          * @return List of doubles. First member is RA deviation. Second member is DE deviation.
0291          */
0292         Q_SCRIPTABLE QList<double> axisDelta();
0293 
0294         /** DBUS interface function.
0295          * @brief axisSigma return axis sigma deviation in arcseconds RMS. This is the RMS deviation of locked star position when guiding started.
0296          * @return List of doubles. First member is RA deviation. Second member is DE deviation.
0297          */
0298         Q_SCRIPTABLE QList<double> axisSigma();
0299 
0300         /**
0301               * @brief checkCamera Check all CCD parameters and ensure all variables are updated to reflect the selected CCD
0302               * @param ccdNum CCD index number in the CCD selection combo box
0303               */
0304         void checkCamera();
0305 
0306         /**
0307              * @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
0308              * @param targetChip Chip for which the exposure is undergoing
0309              * @param exposure numbers of seconds left in the exposure
0310              * @param expState State of the exposure property
0311              */
0312         void checkExposureValue(ISD::CameraChip *targetChip, double exposure, IPState expState);
0313 
0314         /**
0315              * @brief newFITS is called by the INDI framework whenever there is a new BLOB arriving
0316              */
0317         void processData(const QSharedPointer<FITSData> &data);
0318 
0319         // Aborts the current exposure, if one is ongoing.
0320         void abortExposure();
0321 
0322         // This Function will allow PHD2 to update the exposure values to the recommended ones.
0323         QString setRecommendedExposureValues(QList<double> values);
0324 
0325         // Append Log entry
0326         void appendLogText(const QString &);
0327 
0328         // Update Guide module status
0329         void setStatus(Ekos::GuideState newState);
0330 
0331         // Update Capture Module status
0332         void setCaptureStatus(Ekos::CaptureState newState);
0333         // Update Mount module status
0334         void setMountStatus(ISD::Mount::Status newState);
0335         void setMountCoords(const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &ha);
0336 
0337         // Update Pier Side
0338         void setPierSide(ISD::Mount::PierSide newSide);
0339 
0340         // Star Position
0341         void setStarPosition(const QVector3D &newCenter, bool updateNow);
0342 
0343         // Capture
0344         void setCaptureComplete();
0345 
0346         // Pulse both RA and DEC axes
0347         bool sendMultiPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs, CaptureAfterPulses followWithCapture);
0348         // Pulse for one of the mount axes
0349         bool sendSinglePulse(GuideDirection dir, int msecs, CaptureAfterPulses followWithCapture);
0350 
0351         /**
0352              * @brief setDECSwap Change ST4 declination pulse direction. +DEC pulses increase DEC if swap is OFF. When on +DEC pulses result in decreasing DEC.
0353              * @param enable True to enable DEC swap. Off to disable it.
0354              */
0355         void setDECSwap(bool enable);
0356 
0357 
0358         /**
0359          * @brief updateSetting Update per-train and global setting
0360          * @param key Name of setting
0361          * @param value Value
0362          * @note per-train and global settings are updated. Changes are saved to database
0363          * and to disk immediately.
0364          */
0365         void updateSetting(const QString &key, const QVariant &value);
0366 
0367         //plot slots
0368         void handleVerticalPlotSizeChange();
0369         void handleHorizontalPlotSizeChange();
0370         void clearGuideGraphs();
0371         void clearCalibrationGraphs();
0372         void slotAutoScaleGraphs();
0373         void buildTarget();
0374         void guideHistory();
0375         void setLatestGuidePoint(bool isChecked);
0376 
0377         void updateDirectionsFromPHD2(const QString &mode);
0378 
0379         void guideAfterMeridianFlip();
0380 
0381         // Trains
0382         QString opticalTrain() const
0383         {
0384             return opticalTrainCombo->currentText();
0385         }
0386         void setOpticalTrain(const QString &value)
0387         {
0388             opticalTrainCombo->setCurrentText(value);
0389         }
0390 
0391     protected slots:
0392         void updateCCDBin(int index);
0393 
0394         /**
0395                 * @brief processCCDNumber Process number properties arriving from CCD. Currently, binning changes are processed.
0396                 * @param nvp pointer to number property.
0397                 */
0398         void updateProperty(INDI::Property prop);
0399 
0400         /**
0401              * @brief setTrackingStar Gets called when the user select a star in the guide frame
0402              * @param x X coordinate of star
0403              * @param y Y coordinate of star
0404              */
0405         void setTrackingStar(int x, int y);
0406 
0407         void saveDefaultGuideExposure();
0408 
0409         void updateTrackingBoxSize(int currentIndex);
0410 
0411         //void onXscaleChanged( int i );
0412         //void onYscaleChanged( int i );
0413         void onThresholdChanged(int i);
0414         void onEnableDirRA();
0415         void onEnableDirDEC();
0416 
0417         void setAxisDelta(double ra, double de);
0418         void setAxisSigma(double ra, double de);
0419         void setAxisPulse(double ra, double de);
0420         void setSNR(double snr);
0421         void calibrationUpdate(GuideInterface::CalibrationUpdateType type, const QString &message = QString(""), double dx = 0,
0422                                double dy = 0);
0423 
0424         void guideInfo(const QString &info);
0425 
0426         void processGuideOptions();
0427         void configSEPMultistarOptions();
0428 
0429         void onControlDirectionChanged();
0430 
0431         void showFITSViewer();
0432 
0433         void processCaptureTimeout();
0434 
0435         void nonGuidedDither();
0436 
0437     signals:
0438         void newLog(const QString &text);
0439         void newStatus(Ekos::GuideState status);
0440 
0441         void newImage(const QSharedPointer<FITSView> &view);
0442         void newStarPixmap(QPixmap &);
0443 
0444         void trainChanged();
0445 
0446         // Immediate deviations in arcsecs
0447         void newAxisDelta(double ra, double de);
0448         // Sigma deviations in arcsecs RMS
0449         void newAxisSigma(double ra, double de);
0450 
0451         void guideStats(double raError, double decError, int raPulse, int decPulse,
0452                         double snr, double skyBg, int numStars);
0453 
0454         void guideChipUpdated(ISD::CameraChip *);
0455         void settingsUpdated(const QVariantMap &settings);
0456         void driverTimedout(const QString &deviceName);
0457 
0458     private:
0459 
0460         void resizeEvent(QResizeEvent *event) override;
0461 
0462         /**
0463              * @brief updateGuideParams Update the guider and frame parameters due to any changes in the mount and/or ccd frame
0464              */
0465         void updateGuideParams();
0466 
0467         /**
0468          * @brief check if the guiding chip of the camera should be used (if present)
0469          */
0470         void checkUseGuideHead();
0471 
0472         /**
0473              * @brief syncTrackingBoxPosition Sync the tracking box to the current selected star center
0474              */
0475         void syncTrackingBoxPosition();   
0476 
0477         /**
0478              * @brief setBusy Indicate busy status within the module visually
0479              * @param enable True if module is busy, false otherwise
0480              */
0481         void setBusy(bool enable);
0482 
0483         /**
0484          * @brief setBLOBEnabled Enable or disable BLOB reception from current CCD if using external guider
0485          * @param enable True to enable BLOB reception, false to disable BLOB reception
0486          * @param name CCD to enable to disable. If empty (default), then action is applied to all CCDs.
0487          */
0488         void setExternalGuiderBLOBEnabled(bool enable);
0489 
0490         /**
0491          * @brief prepareCapture Set common settings for capture for guide module
0492          * @param targetChip target Chip
0493          */
0494         void prepareCapture(ISD::CameraChip *targetChip);
0495 
0496 
0497         void handleManualDither();
0498 
0499         ////////////////////////////////////////////////////////////////////
0500         /// Settings
0501         ////////////////////////////////////////////////////////////////////
0502 
0503         /**
0504          * @brief Connect GUI elements to sync settings once updated.
0505          */
0506         void connectSettings();
0507         /**
0508          * @brief Stop updating settings when GUI elements are updated.
0509          */
0510         void disconnectSettings();
0511         /**
0512          * @brief loadSettings Load setting from Options and set them accordingly.
0513          */
0514         void loadGlobalSettings();
0515 
0516         /**
0517          * @brief syncSettings When checkboxes, comboboxes, or spin boxes are updated, save their values in the
0518          * global and per-train settings.
0519          */
0520         void syncSettings();
0521 
0522         /**
0523          * @brief syncControl Sync setting to widget. The value depends on the widget type.
0524          * @param settings Map of all settings
0525          * @param key name of widget to sync
0526          * @param widget pointer of widget to set
0527          * @return True if sync successful, false otherwise
0528          */
0529         bool syncControl(const QVariantMap &settings, const QString &key, QWidget * widget);
0530 
0531         // Operation stack
0532         void buildOperationStack(GuideState operation);
0533         bool executeOperationStack();
0534         bool executeOneOperation(GuideState operation);
0535 
0536         // Init Functions
0537         void initPlots();
0538         void initDriftGraph();
0539         void initCalibrationPlot();
0540         void initView();
0541         void initConnections();
0542 
0543         bool captureOneFrame();
0544 
0545         void setupOpticalTrainManager();
0546         void refreshOpticalTrain();        
0547 
0548         // Driver
0549         void reconnectDriver(const QString &camera, QVariantMap settings);
0550 
0551         // Operation Stack
0552         QStack<GuideState> operationStack;
0553 
0554         // Devices
0555         ISD::Camera *m_Camera { nullptr };
0556         ISD::Mount *m_Mount { nullptr };
0557         ISD::Guider *m_Guider { nullptr };
0558         ISD::AdaptiveOptics *m_AO { nullptr };
0559 
0560         // Guider process
0561         GuideInterface *m_GuiderInstance { nullptr };
0562 
0563         //This is for the configure PHD2 camera method.
0564         QString m_LastPHD2CameraName, m_LastPHD2MountName;
0565         GuiderType guiderType { GUIDE_INTERNAL };
0566 
0567         // Star
0568         QVector3D starCenter;
0569 
0570         // Guide Params
0571         int guideBinIndex { 0 };    // Selected or saved binning for guiding
0572         double ccdPixelSizeX { -1 };
0573         double ccdPixelSizeY { -1 };
0574 
0575         // Scope info
0576         double m_Aperture { -1 };
0577         double m_FocalLength { -1 };
0578         double m_FocalRatio { -1 };
0579         double m_Reducer {-1};
0580 
0581         double guideDeviationRA { 0 };
0582         double guideDeviationDEC { 0 };
0583         double pixScaleX { -1 };
0584         double pixScaleY { -1 };
0585 
0586         // State
0587         GuideState m_State { GUIDE_IDLE };
0588         GuideStateWidget *guideStateWidget { nullptr };
0589 
0590         // Guide timer
0591         QElapsedTimer guideTimer;
0592 
0593         // Capture timeout timer
0594         QTimer captureTimeout;
0595         uint8_t m_CaptureTimeoutCounter { 0 };
0596         uint8_t m_DeviceRestartCounter { 0 };
0597 
0598         // Pulse Timer
0599         QTimer m_PulseTimer;
0600 
0601         // Log
0602         QStringList m_LogText;
0603 
0604         // Misc
0605         bool useGuideHead { false };
0606 
0607         // Progress Activity Indicator
0608         QProgressIndicator *pi { nullptr };
0609 
0610         // Options
0611         OpsCalibration *opsCalibration { nullptr };
0612         OpsGuide *opsGuide { nullptr };
0613         OpsDither *opsDither { nullptr };
0614         OpsGPG *opsGPG { nullptr };
0615 
0616         // Guide Frame
0617         QSharedPointer<GuideView> m_GuideView;
0618 
0619         // Calibration done already?
0620         bool calibrationComplete { false };
0621 
0622         // Was the modified frame subFramed?
0623         bool subFramed { false };
0624 
0625         // CCD Chip frame settings
0626         QMap<ISD::CameraChip *, QVariantMap> frameSettings;
0627 
0628         // Profile Pixmap
0629         QPixmap profilePixmap;
0630         // drift plot
0631         QPixmap driftPlotPixmap;
0632 
0633         // Flag to start auto calibration followed immediately by guiding
0634         //bool autoCalibrateGuide { false };
0635 
0636         // Pointers of guider processes
0637         QPointer<InternalGuider> internalGuider;
0638         QPointer<PHD2> phd2Guider;
0639         QPointer<LinGuider> linGuider;
0640         QSharedPointer<FITSViewer> fv;
0641         QSharedPointer<FITSData> m_ImageData;
0642 
0643         // Dark Processor
0644         QPointer<DarkProcessor> m_DarkProcessor;
0645 
0646         // Manual Pulse Dialog
0647         QPointer<ManualPulse> m_ManaulPulse;
0648 
0649         double primaryFL = -1, primaryAperture = -1, guideFL = -1, guideAperture = -1;
0650         ISD::Mount::Status m_MountStatus { ISD::Mount::MOUNT_IDLE };
0651 
0652         bool graphOnLatestPt = true;
0653 
0654         //This is for enforcing the PHD2 Star lock when Guide is pressed,
0655         //autostar is not selected, and the user has chosen a star.
0656         //This connection storage is so that the connection can be disconnected after enforcement
0657         QMetaObject::Connection guideConnect;
0658 
0659         QCPItemText *calLabel  { nullptr };
0660 
0661         // The scales of these zoom levels are defined in Guide::zoomX().
0662         static constexpr int defaultXZoomLevel = 3;
0663         int driftGraphZoomLevel {defaultXZoomLevel};
0664 
0665 
0666         // The accumulated non-guided dither offsets (in milliseconds) in the RA and DEC directions.
0667         int nonGuidedDitherRaOffsetMsec = 0, nonGuidedDitherDecOffsetMsec = 0;
0668 
0669         // Random generator for non guided dithering
0670         std::mt19937 nonGuidedPulseGenerator;
0671 
0672         // Flag to check if random generator for non guided dithering is initialized.
0673         bool isNonGuidedDitherInitialized = false;
0674 
0675         // Reset non guided dithering properties and initialize the random generator seed if not already done.
0676         // Should be called in Guide::Guide() for initial seed initialization, and then in setCaptureStatus to reset accumulated drift
0677         // every time a capture task is completed or aborted.
0678         void resetNonGuidedDither();
0679 
0680         QVariantMap m_Settings;
0681         QVariantMap m_GlobalSettings;
0682 };
0683 }