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

0001 /*  Ekos Polar Alignment Assistant Tool
0002     SPDX-FileCopyrightText: 2018-2021 Jasem Mutlaq
0003     SPDX-FileCopyrightText: 2020-2021 Hy Murveit
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #pragma once
0009 
0010 #include "ui_polaralignmentassistant.h"
0011 #include "ekos/ekos.h"
0012 #include "ekos/guide/internalguide/starcorrespondence.h"
0013 #include "polaralign.h"
0014 #include "align.h"
0015 #include "indi/indimount.h"
0016 
0017 class AlignView;
0018 class QProgressIndicator;
0019 class SolverUtils;
0020 
0021 namespace Ekos
0022 {
0023 
0024 class PolarAlignWidget;
0025 
0026 /**
0027  * @brief The PolarAlignmentAssistant class
0028  *
0029  * Captures three images rotated by a set number of degrees decided by the user (default 30).
0030  * Each image is plate solver to find the center RA,DE coordinates. The three points are then
0031  * used to generate a unique circle with its center as the RA axis. As the mount rotated around
0032  * these point, we can identify the RA rotational axis. From there, we compare the distance from RA axis
0033  * to the celestial pole axis. For a perfectly aligned mount, the two points would overlap exactly.
0034  * In reality, there is also some differences due to the mount mechanical limitations and measurements
0035  * errors.
0036  *
0037  * The user is then presented with a triangle that couples the corrections required in Altitude and Azimuth
0038  * knobs to move the RA axis to the celestial pole. An optional feature is available the calculates the error
0039  * in real time as the user move the mount around during refresh, but this feature is computationally intensive
0040  * as we need to extract stars from each frame.
0041  *
0042  * @author Jasem Mutlaq
0043  * @author Hy Murveit
0044  */
0045 class PolarAlignmentAssistant : public QWidget, public Ui::PolarAlignmentAssistant
0046 {
0047         Q_OBJECT
0048 
0049     public:
0050         explicit PolarAlignmentAssistant(Align *parent, const QSharedPointer<AlignView> &view);
0051         ~PolarAlignmentAssistant();
0052 
0053         typedef enum
0054         {
0055             PAH_IDLE,
0056             PAH_FIRST_CAPTURE,
0057             PAH_FIRST_SOLVE,
0058             PAH_FIND_CP,
0059             PAH_FIRST_ROTATE,
0060             PAH_FIRST_SETTLE,
0061             PAH_SECOND_CAPTURE,
0062             PAH_SECOND_SOLVE,
0063             PAH_SECOND_ROTATE,
0064             PAH_SECOND_SETTLE,
0065             PAH_THIRD_CAPTURE,
0066             PAH_THIRD_SOLVE,
0067             PAH_STAR_SELECT,
0068             PAH_REFRESH,
0069             PAH_POST_REFRESH
0070         } Stage;
0071 
0072         // Algorithm choice in UI
0073         typedef enum
0074         {
0075             PLATE_SOLVE_ALGORITHM,
0076             MOVE_STAR_ALGORITHM,
0077             MOVE_STAR_UPDATE_ERR_ALGORITHM
0078         } RefreshAlgorithm;
0079 
0080         enum CircleSolution
0081         {
0082             NO_CIRCLE_SOLUTION,
0083             ONE_CIRCLE_SOLUTION,
0084             TWO_CIRCLE_SOLUTION,
0085             INFINITE_CIRCLE_SOLUTION
0086         };
0087         typedef enum { NORTH_HEMISPHERE, SOUTH_HEMISPHERE } HemisphereType;
0088 
0089         // Set the mount used in Align class.
0090         void setCurrentTelescope(ISD::Mount *scope)
0091         {
0092             m_CurrentTelescope = scope;
0093         }
0094         // Sync mount slew speed and available rates from the telescope object
0095         void syncMountSpeed(const QString &speed);
0096         // Enable PAA if the FOV is sufficient
0097         void setEnabled(bool enabled);
0098         // Return the exposure used in the refresh phase.
0099         double getPAHExposureDuration() const
0100         {
0101             return pAHExposure->value();
0102         }
0103         // Handle updates during the refresh phase such as error estimation.
0104         void processPAHRefresh();
0105         // Handle solver failure and retry to capture until a preset number of retries is met.
0106         bool processSolverFailure();
0107         // Handle both automated and manual mount rotations.
0108         void processMountRotation(const dms &ra, double settleDuration);
0109         // After solver is complete, handle PAH Stage processing
0110         void processPAHStage(double orientation, double ra, double dec, double pixscale, bool eastToTheRight, short healpix,
0111                              short index);
0112         // Return current PAH stage
0113         Stage getPAHStage() const
0114         {
0115             return m_PAHStage;
0116         }
0117         // Set active stage.
0118         void setPAHStage(Stage stage);
0119         // Start the polar alignment process.
0120         void startPAHProcess();
0121         // Stops the polar alignment process.
0122         void stopPAHProcess();
0123         // Process the results of WCS from the solving process. If the results are good, we continue to the next phase.
0124         // Otherwise, we abort the operation.
0125         void setWCSToggled(bool result);
0126         // Update GUI to reflect mount status.
0127         void setMountStatus(ISD::Mount::Status newState);
0128         // Update the correction offset by this percentage in order to move the triangle around when clicking on
0129         // for example.
0130         void setPAHCorrectionOffsetPercentage(double dx, double dy);
0131         // Update the PAH refresh duration
0132         void setPAHRefreshDuration(double value)
0133         {
0134             pAHExposure->setValue(value);
0135         }
0136         // Start the refresh process.
0137         void startPAHRefreshProcess();
0138         // This should be called when manual slewing is complete.
0139         void setPAHSlewDone();
0140         // Return current active stage label
0141         QString getPAHStageString(bool translated = true) const
0142         {
0143             return translated ? i18n(PAHStages[m_PAHStage]) : PAHStages[m_PAHStage];
0144         }
0145         // Return last message
0146         QString getPAHMessage() const;
0147         // Set image data from align class
0148         void setImageData(const QSharedPointer<FITSData> &image);
0149 
0150         void setPAHRefreshAlgorithm(RefreshAlgorithm value);
0151 
0152     protected:
0153         // Polar Alignment Helper slots
0154         void rotatePAH();
0155         void setPAHCorrectionOffset(int x, int y);
0156 
0157     private:
0158         /**
0159             * @brief Warns the user if the polar alignment might cross the meridian.
0160             */
0161         bool checkPAHForMeridianCrossing();
0162 
0163         /**
0164              * @brief calculatePAHError Calculate polar alignment error in the Polar Alignment Helper (PAH) method
0165              * @return True if calculation is successsful, false otherwise.
0166              */
0167         bool calculatePAHError();
0168 
0169         /**
0170          * @brief syncCorrectionVector Flip correction vector based on user settings.
0171          */
0172         void syncCorrectionVector();
0173 
0174         /**
0175          * @brief setupCorrectionGraphics Update align view correction graphics.
0176          * @param pixel
0177          */
0178         void setupCorrectionGraphics(const QPointF &pixel);
0179 
0180         /**
0181          * @brief supdateRefreshDisplay Updates the UI's refresh error stats.
0182          * @param azError the azimuth error in degrees
0183          * @param altError the altitude error in degrees
0184          */
0185         void updateRefreshDisplay(double azError, double altError);
0186 
0187     signals:
0188         // Report new log
0189         void newLog(const QString &);
0190         // Request new capture and solve
0191         void captureAndSolve();
0192         // Report correction vector and original errors
0193         void polarResultUpdated(QLineF correctionVector, double polarError, double azError, double altError);
0194         // Report updated errors
0195         void updatedErrorsChanged(double total, double az, double alt);
0196         // Report new correction vector
0197         void newCorrectionVector(QLineF correctionVector);
0198         // Report new PAH stage
0199         void newPAHStage(Stage stage);
0200         // Report new PAH message
0201         void newPAHMessage(const QString &message);
0202         // Report whether the tool is enabled or not
0203         void PAHEnabled(bool);
0204         // Request to set alignment table result
0205         void newAlignTableResult(Align::AlignResult result);
0206         // Report that the align view was updated.
0207         void newFrame(const QSharedPointer<FITSView> &view);
0208 
0209     private:
0210         void updateDisplay(Stage stage, const QString &message);
0211         void drawArrows(double altError, double azError);
0212         void showUpdatedError(bool show);
0213         // These are only used in the plate-solve refresh scheme.
0214         void solverDone(bool timedOut, bool success, const FITSImage::Solution &solution, double elapsedSeconds);
0215         void startSolver();
0216         void updatePlateSolveTriangle(const QSharedPointer<FITSData> &image);
0217 
0218         // Polar Alignment Helper
0219         Stage m_PAHStage { PAH_IDLE };
0220 
0221         SkyPoint targetPAH;
0222 
0223         // Which hemisphere are we located on?
0224         HemisphereType hemisphere;
0225 
0226         // Polar alignment will retry capture & solve a few times if solve fails.
0227         int m_PAHRetrySolveCounter { 0 };
0228 
0229         // Points on the image to correct mount's ra axis.
0230         // correctionFrom is the star the user selected (or center of the image at start).
0231         // correctionTo is where theuser should move that star.
0232         // correctionAltTo is where the use should move that star to only fix altitude.
0233         QPointF correctionFrom, correctionTo, correctionAltTo;
0234 
0235         // RA/DEC coordinates where the image center needs to be move to to correct the RA axis.
0236         SkyPoint refreshSolution, altOnlyRefreshSolution;
0237 
0238 
0239         bool detectStarsPAHRefresh(QList<Edge> *stars, int num, int x, int y, int *xyIndex);
0240 
0241         // Incremented every time sufficient # of stars are detected (for move-star refresh) or
0242         // when solver is successful (for plate-solve refresh).
0243         int refreshIteration { 0 };
0244         // Incremented on every image received.
0245         int imageNumber { 0 };
0246         StarCorrespondence starCorrespondencePAH;
0247 
0248         // Class used to estimate alignment error.
0249         PolarAlign polarAlign;
0250 
0251         // Pointer to image data
0252         QSharedPointer<FITSData> m_ImageData;
0253 
0254         // Reference to parent
0255         Align *m_AlignInstance {nullptr};
0256 
0257         // Reference to current active telescope
0258         ISD::Mount *m_CurrentTelescope { nullptr };
0259 
0260         // Reference to align view
0261         QSharedPointer<AlignView> m_AlignView;
0262 
0263         // PAH Stage Map
0264         static const QMap<Stage, const char *> PAHStages;
0265 
0266         // Threshold to stop PAH rotation in degrees
0267         static constexpr uint8_t PAH_ROTATION_THRESHOLD { 5 };
0268 
0269         PolarAlignWidget *polarAlignWidget {nullptr};
0270 
0271         // Used in the refresh part of polar alignment.
0272         QSharedPointer<SolverUtils> m_Solver;
0273         double m_LastRa {0};
0274         double m_LastDec {0};
0275         double m_LastOrientation {0};
0276         double m_LastPixscale {0};
0277 
0278         // Restricts (the internal solver) to using the index and healpix
0279         // from the previous solve, if that solve was successful.
0280         int m_IndexToUse { -1 };
0281         int m_HealpixToUse { -1 };
0282         int m_NumHealpixFailures { 0 };
0283 };
0284 }