1 /*
2     SPDX-FileCopyrightText: 2015 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #ifndef MOUNT_H
8 #define MOUNT_H
9 
10 #include <QQmlContext>
11 #include <QtDBus>
12 #include "ui_mount.h"
13 
14 #include "indi/indistd.h"
15 #include "indi/indifocuser.h"
16 #include "indi/inditelescope.h"
17 
18 class QQuickView;
19 class QQuickItem;
20 
21 namespace Ekos
22 {
23 /**
24  *@class Mount
25  *@short Supports controlling INDI telescope devices including setting/retrieving mount properties, slewing, motion and speed controls, in addition to enforcing altitude limits and parking/unparking.
26  *@author Jasem Mutlaq
27  *@version 1.4
28  */
29 
30 class Mount : public QWidget, public Ui::Mount
31 {
32         Q_OBJECT
33         Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Mount")
34         Q_PROPERTY(ISD::Telescope::Status status READ status NOTIFY newStatus)
35         Q_PROPERTY(ISD::ParkStatus parkStatus READ parkStatus NOTIFY newParkStatus)
36         Q_PROPERTY(QStringList logText READ logText NOTIFY newLog)
37         Q_PROPERTY(QList<double> altitudeLimits READ altitudeLimits WRITE setAltitudeLimits)
38         Q_PROPERTY(bool altitudeLimitsEnabled READ altitudeLimitsEnabled WRITE setAltitudeLimitsEnabled)
39         Q_PROPERTY(double hourAngleLimit READ hourAngleLimit WRITE setHourAngleLimit)
40         Q_PROPERTY(bool hourAngleLimitEnabled READ hourAngleLimitEnabled WRITE setHourAngleLimitEnabled)
41         Q_PROPERTY(bool autoParkEnabled READ autoParkEnabled WRITE setAutoParkEnabled)
42         Q_PROPERTY(QList<double> equatorialCoords READ equatorialCoords)
43         Q_PROPERTY(QList<double> horizontalCoords READ horizontalCoords)
44         Q_PROPERTY(QList<double> telescopeInfo READ telescopeInfo WRITE setTelescopeInfo)
45         Q_PROPERTY(double hourAngle READ hourAngle)
46         Q_PROPERTY(int slewRate READ slewRate WRITE setSlewRate)
47         Q_PROPERTY(int slewStatus READ slewStatus)
48         Q_PROPERTY(bool canPark READ canPark)
49         Q_PROPERTY(ISD::Telescope::PierSide pierSide READ pierSide NOTIFY pierSideChanged)
50 
51     public:
52         Mount();
53         ~Mount();
54 
55         //typedef enum { PARKING_IDLE, PARKING_OK, UNPARKING_OK, PARKING_BUSY, UNPARKING_BUSY, PARKING_ERROR } ParkingStatus;
56 
57         // This enum defines the meridian flip state machine, this is implemented in
58         typedef enum
59         {
60             FLIP_NONE,      // this is the default state, comparing the hour angle with the next flip position
61             // it moves to FLIP_PLANNED when a flip is needed.
62             FLIP_PLANNED,   // this signals to the Capture class that a flip is required, the Capture class will
63             // move to FLIP_ACCEPTED when it has completed everything that needs to be done.
64             FLIP_WAITING,   // Capture seems to set this state to signal that the flip will have to wait
65             FLIP_ACCEPTED,  // Capture signals to the mount that a flip slew can be started
66             FLIP_RUNNING,   // this signals that a flip slew is in progress, when the slew stops the state
67             // is set to FLIP_COMPLETED
68             FLIP_COMPLETED, // this checks that the flip was completed successfully or not and after tidying up
69             // moves to FLIP_NONE to wait for the next flip requirement.
70             // Capture sees this and resumes.
71             FLIP_ERROR      // errors in the flip process should end up here
72         } MeridianFlipStatus;
73 
74         /**
75              * @brief setTelescope Sets the mount module telescope interface
76              * @param newTelescope pointer to telescope interface object
77              */
78         void setTelescope(ISD::GDInterface *newTelescope);
79 
80         void setGPS(ISD::GDInterface *newGPS);
81 
82         void removeDevice(ISD::GDInterface *device);
83 
84         // Log functions
85         void appendLogText(const QString &);
86         void clearLog();
logText()87         QStringList logText()
88         {
89             return m_LogText;
90         }
getLogText()91         QString getLogText() const
92         {
93             return m_LogText.join("\n");
94         }
95 
status()96         ISD::Telescope::Status status() const
97         {
98             return m_Status;
99         }
pierSide()100         ISD::Telescope::PierSide pierSide() const
101         {
102             if (currentTelescope)
103                 return currentTelescope->pierSide();
104             else
105                 return ISD::Telescope::PIER_UNKNOWN;
106         }
parkStatus()107         ISD::ParkStatus parkStatus() const
108         {
109             return m_ParkStatus;
110         }
111 
meridianFlipStatus()112         MeridianFlipStatus meridianFlipStatus() const
113         {
114             return m_MFStatus;
115         }
116 
117         /** @defgroup MountDBusInterface Ekos Mount DBus Interface
118              * Mount interface provides advanced scripting capabilities to control INDI mounts.
119             */
120 
121         /*@{*/
122 
123         /** DBUS interface function.
124              * Returns the mount altitude limits.
125              * @return Returns array of doubles. First item is minimum altitude in degrees. Second item is maximum altitude limit in degrees.
126              */
127         Q_SCRIPTABLE QList<double> altitudeLimits();
128 
129         /** DBUS interface function.
130              * Sets the mount altitude limits, and whether they are enabled or disabled.
131              * @param limits is a list of double values. 2 values are expected: minAltitude & maxAltitude
132              */
133         Q_SCRIPTABLE Q_NOREPLY void setAltitudeLimits(QList<double> limits);
134 
135         /** DBUS interface function.
136              * Enable or disable mount altitude limits.
137              */
138         Q_SCRIPTABLE void setAltitudeLimitsEnabled(bool enable);
139 
140         /** DBUS interface function.
141              * Returns whether the mount limits are enabled or disabled.
142              * @return True if enabled, false otherwise.
143              */
144         Q_SCRIPTABLE bool altitudeLimitsEnabled();
145 
146         /** DBUS interface function.
147              * Returns the mount hour angle limit.
148              * @return Returns hour angle limit in hours.
149              */
150         Q_SCRIPTABLE double hourAngleLimit();
151 
152         /** DBUS interface function.
153              * Sets the mount altitude limits, and whether they are enabled or disabled.
154              * @param limits is a list of double values. 2 values are expected: minAltitude & maxAltitude
155              */
156         Q_SCRIPTABLE Q_NOREPLY void setHourAngleLimit(double limit);
157 
158         /** DBUS interface function.
159              * Enable or disable mount hour angle limit. Mount cannot slew and/or track past this
160              * hour angle distance.
161              */
162         Q_SCRIPTABLE void setHourAngleLimitEnabled(bool enable);
163 
164         /** DBUS interface function.
165              * Returns whether the mount limits are enabled or disabled.
166              * @return True if enabled, false otherwise.
167              */
168         Q_SCRIPTABLE bool hourAngleLimitEnabled();
169 
170         /**
171          * @brief autoParkEnabled Check if auto-park is enabled.
172          * @return True if enabled.
173          */
174         Q_SCRIPTABLE bool autoParkEnabled();
175 
176         /**
177          * @brief setAutoParkEnabled Toggle Auto Park
178          * @param enable True to start, false to stop
179          */
180         Q_SCRIPTABLE void setAutoParkEnabled(bool enable);
181 
182         /**
183          * @brief setAutoParkDailyEnabled toggles everyday Auto Park
184          * @param enable true to activate, false to deactivate
185          */
186         Q_SCRIPTABLE void setAutoParkDailyEnabled(bool enabled);
187 
188         /**
189          * @brief setAutoParkStartup Set time when automatic parking is activated.
190          * @param startup Startup time. should not be more than 12 hours away.
191          */
192         Q_SCRIPTABLE void setAutoParkStartup(QTime startup);
193 
194         Q_SCRIPTABLE bool meridianFlipEnabled();
195         Q_SCRIPTABLE double meridianFlipValue();
196 
197         /** DBUS interface function.
198              * Slew the mount to the RA/DEC (JNow).
199              * @param RA Right ascention is hours.
200              * @param DEC Declination in degrees.
201              * @return true if the command is sent successfully, false otherwise.
202              */
203         Q_INVOKABLE Q_SCRIPTABLE bool slew(double RA, double DEC);
204 
205         /**
206               @brief Like above but RA and DEC are strings HH:MM:SS and DD:MM:SS
207             */
208         Q_INVOKABLE bool slew(const QString &RA, const QString &DEC);
209 
210         /** DBUS interface function.
211              * Slew the mount to the target. Target name must be valid in KStars.
212              * @param target name
213              * @return true if the command is sent successfully, false otherwise.
214              */
215         Q_INVOKABLE Q_SCRIPTABLE bool gotoTarget(const QString &target);
216 
217         /** DBUS interface function.
218              * Sync the mount to the RA/DEC (JNow).
219              * @param RA Right ascention is hours.
220              * @param DEC Declination in degrees.
221              * @return true if the command is sent successfully, false otherwise.
222              */
223         Q_INVOKABLE Q_SCRIPTABLE bool sync(double RA, double DEC);
224 
225         /** DBUS interface function.
226              * Sync the mount to the target. Target name must be valid in KStars.
227              * @param target name
228              * @return true if the command is sent successfully, false otherwise.
229              */
230         Q_INVOKABLE Q_SCRIPTABLE bool syncTarget(const QString &target);
231 
232         /**
233               @brief Like above but RA and DEC are strings HH:MM:SS and DD:MM:SS
234             */
235         Q_INVOKABLE bool sync(const QString &RA, const QString &DEC);
236 
237         /** DBUS interface function.
238              * Get equatorial coords (JNow). An array of doubles is returned. First element is RA in hours. Second elements is DEC in degrees.
239              */
240         Q_SCRIPTABLE QList<double> equatorialCoords();
241 
242         /** DBUS interface function.
243              * Get Horizontal coords. An array of doubles is returned. First element is Azimuth in degrees. Second elements is Altitude in degrees.
244              */
245         Q_SCRIPTABLE QList<double> horizontalCoords();
246 
247         /** DBUS interface function.
248              * Get Horizontal coords.
249              */
250         Q_SCRIPTABLE SkyPoint currentTarget();
251 
252         /** DBUS interface function.
253              * Get mount hour angle in hours (-12 to +12).
254              */
255         Q_SCRIPTABLE double hourAngle();
256 
257         double initialPositionHA;
258         /** DBUS interface function.
259              * Get the hour angle of that time the mount has slewed to the current position.
260              * This is used to manage the meridian flip for mounts which do not report pier side.
261              * only one attempt to flip is done.
262              */
initialHA()263         Q_SCRIPTABLE double initialHA()
264         {
265             return initialPositionHA;
266         }
267 
setInitialHA(double ha)268         Q_SCRIPTABLE void setInitialHA(double ha)
269         {
270             initialPositionHA = ha;
271         }
272 
273         /** DBUS interface function.
274              * Aborts the mount motion
275              * @return true if the command is sent successfully, false otherwise.
276              */
277         Q_INVOKABLE Q_SCRIPTABLE bool abort();
278 
279         /** DBUS interface function.
280              * Get the mount slew status ("Idle","Complete", "Busy", "Error")
281              */
282         Q_INVOKABLE Q_SCRIPTABLE IPState slewStatus();
283 
284 
285         /** DBUS interface function.
286              * Get the mount slew rate index 0 to N-1, or -1 if slew rates are not supported.
287              */
288         Q_INVOKABLE Q_SCRIPTABLE int slewRate();
289 
290         Q_INVOKABLE Q_SCRIPTABLE bool setSlewRate(int index);
291 
292         /** DBUS interface function.
293              * Get telescope and guide scope info. An array of doubles is returned in order.
294              * Primary Telescope Focal Length (mm), Primary Telescope Aperture (mm), Guide Telescope Focal Length (mm), Guide Telescope Aperture (mm)
295              */
296         Q_INVOKABLE Q_SCRIPTABLE QList<double> telescopeInfo();
297 
298         /** DBUS interface function.
299              * Set telescope and guide scope info and save them in INDI mount driver. All measurements is in millimeters.
300              * @param info An ordered 4-item list as following:
301              * primaryFocalLength Primary Telescope Focal Length. Set to 0 to skip setting this value.
302              * primaryAperture Primary Telescope Aperture. Set to 0 to skip setting this value.
303              * guideFocalLength Guide Telescope Focal Length. Set to 0 to skip setting this value.
304              * guideAperture Guide Telescope Aperture. Set to 0 to skip setting this value.
305              */
306         Q_SCRIPTABLE Q_NOREPLY void setTelescopeInfo(const QList<double> &info);
307 
308         /** DBUS interface function.
309              * Reset mount model if supported by the mount.
310              * @return true if the command is executed successfully, false otherwise.
311              */
312         Q_INVOKABLE Q_SCRIPTABLE bool resetModel();
313 
314         /** DBUS interface function.
315              * Can mount park?
316              */
317         Q_INVOKABLE Q_SCRIPTABLE bool canPark();
318 
319         /** DBUS interface function.
320              * Park mount
321              */
322         Q_INVOKABLE Q_SCRIPTABLE bool park();
323 
324         /** DBUS interface function.
325              * Unpark mount
326              */
327         Q_INVOKABLE Q_SCRIPTABLE bool unpark();
328 
329         /** DBUS interface function.
330              * Return parking status of the mount.
331              */
332         //Q_INVOKABLE Q_SCRIPTABLE ParkingStatus getParkingStatus();
333 
334         Q_INVOKABLE void setTrackEnabled(bool enabled);
335 
336         Q_INVOKABLE void setJ2000Enabled(bool enabled);
337 
338         /** @}*/
339 
340         Q_INVOKABLE void findTarget();
341 
342         // target coord conversions for displaying
343         Q_INVOKABLE bool raDecToAzAlt(QString qsRA, QString qsDec);
344         Q_INVOKABLE bool raDecToHaDec(QString qsRA);
345         Q_INVOKABLE bool azAltToRaDec(QString qsAz, QString qsAlt);
346         Q_INVOKABLE bool azAltToHaDec(QString qsAz, QString qsAlt);
347         Q_INVOKABLE bool haDecToRaDec(QString qsHA);
348         Q_INVOKABLE bool haDecToAzAlt(QString qsHA, QString qsDec);
349 
350         // Center mount in Sky Map
351         Q_INVOKABLE void centerMount();
352 
353         // Get list of scopes
354         //QJsonArray getScopes() const;
355 
356         /*
357          * @brief Check if a meridian flip if necessary.
358          * @param lst current local sideral time
359          * @return true if a meridian flip is necessary
360          */
361         bool checkMeridianFlip(dms lst);
362 
363         /*
364          * @brief Execute a meridian flip if necessary.
365          * @return true if a meridian flip was necessary
366          */
367         Q_INVOKABLE bool executeMeridianFlip();
368 
369         Q_INVOKABLE void setUpDownReversed(bool enabled);
370         Q_INVOKABLE void setLeftRightReversed(bool enabled);
371 
meridianFlipStatusDescription()372         QString meridianFlipStatusDescription()
373         {
374             return meridianFlipStatusText->text();
375         }
376 
377         ///
378         /// \brief meridianFlipStatusString
379         /// \param status
380         /// \return return the string for the status
381         ///
382         static QString meridianFlipStatusString(MeridianFlipStatus status);
383 
384     public slots:
385 
386         /**
387              * @brief syncTelescopeInfo Update telescope information to reflect any property changes
388              */
389         void syncTelescopeInfo();
390         /**
391              * @brief updateNumber Update number properties under watch in the mount module
392              * @param nvp pointer to number property
393              */
394         void updateNumber(INumberVectorProperty *nvp);
395 
396         /**
397              * @brief updateSwitch Update switch properties under watch in the mount module
398              * @param svp pointer to switch property
399              */
400         void updateSwitch(ISwitchVectorProperty *svp);
401 
402 
403         /**
404              * @brief updateText Update text properties under watch in the mount module
405              * @param tvp pointer to text property
406              */
407         void updateText(ITextVectorProperty *tvp);
408 
409         /**
410              * @brief updateLog Update mount module log to include any messages arriving for the telescope driver
411              * @param messageID ID of the new message
412              */
413         void updateLog(int messageID);
414 
415         /**
416              * @brief updateTelescopeCoords is triggered by the ISD::Telescope::newCoord() event and updates the displayed
417              * coordinates of the mount and to ensure mount is within altitude limits if the altitude limits are enabled.
418              * The frequency of this update depends on the REFRESH parameter of the INDI mount device.
419              * @param position latest coordinates the mount reports it is pointing to
420              * @param pierSide pierSide
421              * @param ha hour angle of the latest coordinates
422              */
423         void updateTelescopeCoords(const SkyPoint &position, ISD::Telescope::PierSide pierSide, const dms &ha);
424 
425         /**
426              * @brief move Issues motion command to the mount to move in a particular direction based the request NS and WE values
427              * @param command Either ISD::Telescope::MOTION_START (0) or ISD::Telescope::MOTION_STOP (1)
428              * @param NS is either -1 for no direction, or ISD::Telescope::MOTION_NORTH (0), or ISD::Telescope::MOTION_SOUTH (1)
429              * @param WE is either -1 for no direction, or ISD::Telescope::MOTION_WEST (0), or ISD::Telescope::MOTION_EAST (1)
430              */
431         void motionCommand(int command, int NS, int WE);
432 
433         /**
434          * @brief Send a guide pulse to the telescope.
435          * @param ra_dir RA guide direction
436          * @param ra_msecs duration of the RA guiding pulse in milliseconds
437          * @param dec_dir dec guide direction
438          * @param dec_msecs duration of the DEC guiding pulse in milliseconds
439          */
440         void doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs);
441 
442         /**
443              * @brief save Save telescope focal length and aperture in the INDI telescope driver configuration.
444              */
445         void save();
446 
447         /**
448              * @brief saveLimits Saves altitude limit to the user options and updates the INDI telescope driver limits
449              */
450         void saveLimits();
451 
452         /**
453              * @brief enableAltitudeLimits Enable or disable altitude limits
454              * @param enable True to enable, false to disable.
455              */
456         void enableAltitudeLimits(bool enable);
457 
458         /**
459              * @brief enableAltLimits calls enableAltitudeLimits(true). This function is mostly used to enable altitude limit after a meridian flip is complete.
460              */
461         void enableAltLimits();
462 
463         /**
464              * @brief disableAltLimits calls enableAltitudeLimits(false). This function is mostly used to disable altitude limit once a meridial flip process is started.
465              */
466         void disableAltLimits();
467 
468         /**
469              * @brief enableHourAngleLimits Enable or disable hour angle limits
470              * @param enable True to enable, false to disable.
471              */
472         void enableHourAngleLimits(bool enable);
473 
474         /**
475              * @brief enableHaLimits calls enableHourAngleLimits(true). This function is mostly used to enable hour angle limit after a meridian flip is complete.
476              */
477         void enableHaLimits();
478 
479         /**
480              * @brief disableAltLimits calls enableHourAngleLimits(false). This function is mostly used to disable altitude limit once a meridial flip process is started.
481              */
482         void disableHaLimits();
483 
484         bool setScopeConfig(int index);
485 
486         void toggleMountToolBox();
487 
488         void meridianFlipStatusChanged(MeridianFlipStatus status);
489 
490         /*
491          * @brief set meridian flip activation and hours
492          * @param activate true iff the meridian flip should be executed
493          * @param hours angle past the meridian when the flip should be delayed
494          */
495         void setMeridianFlipValues(bool activate, double hours);
496 
497         /**
498          * @brief registerNewModule Register an Ekos module as it arrives via DBus
499          * and create the appropriate DBus interface to communicate with it.
500          * @param name of module
501          */
502         void registerNewModule(const QString &name);
503 
504     private slots:
505         void startParkTimer();
506         void stopParkTimer();
507         void startAutoPark();
508 
509         void meridianFlipSetupChanged();
510 
511     signals:
512         void newLog(const QString &text);
513         /**
514          * @brief Update event with the current telescope position
515          * @param position mount position. Independent from the mount type,
516          * the EQ coordinates(both JNow and J2000) as well as the alt/az values are filled.
517          * @param pierside for GEMs report the pier side the scope is currently (PierSide::PIER_WEST means
518          * the mount is on the western side of the pier pointing east of the meridian).
519          * @param ha current hour angle
520          */
521         void newCoords(const SkyPoint &position, ISD::Telescope::PierSide pierSide, const dms &ha);
522         /**
523          * @brief The mount has finished the slew to a new target.
524          * @param currentObject object close to the position the mount is pointing to
525          * @param currentCoords exact position where the mount is positioned
526          */
527         void newTarget(SkyObject &currentObject, SkyPoint &currentCoord);
528         /**
529          * @brief Change in the mount status.
530          */
531         void newStatus(ISD::Telescope::Status status);
532         void newParkStatus(ISD::ParkStatus status);
533         void pierSideChanged(ISD::Telescope::PierSide side);
534         void slewRateChanged(int index);
535         void ready();
536         void newMeridianFlipStatus(MeridianFlipStatus status);
537         void newMeridianFlipText(const QString &text);
538 
539     private:
540         void syncGPS();
541         void setScopeStatus(ISD::Telescope::Status status);
542         MeridianFlipStatus m_MFStatus = FLIP_NONE;
543         void setMeridianFlipStatus(MeridianFlipStatus status);
544         void meridianFlipStatusChangedInternal(MeridianFlipStatus status);
545         QString pierSideStateString();
546 
547         // A meridian flip requires a slew of 180 degrees in the hour angle axis so will take at least
548         // the time for that, currently set to 20 seconds
549         // not reliable for pointing state change detection but reported if the pier side is unknown
550         QDateTime minMeridianFlipEndTime;
551         int minMeridianFlipDurationSecs = 20;
552 
553         double flipDelayHrs = 0.0;      // delays the next flip attempt if it fails
554 
555         QPointer<QDBusInterface> captureInterface { nullptr };
556 
557         ISD::Telescope *currentTelescope = nullptr;
558         ISD::GDInterface *currentGPS = nullptr;
559         QStringList m_LogText;
560         SkyPoint *currentTargetPosition = nullptr;
561         SkyPoint telescopeCoord;
562         QString lastNotificationMessage;
563 
564         // Auto Park
565         QTimer autoParkTimer;
566 
567         // Limits
568         int m_AbortDispatch {-1};
569         bool m_AltitudeLimitEnabled {false};
570         double m_LastAltitude {0};
571         bool m_HourAngleLimitEnabled {false};
572         double m_LastHourAngle {0};
573 
574         // GPS
575         bool GPSInitialized = {false};
576 
577         ISD::Telescope::Status m_Status = ISD::Telescope::MOUNT_IDLE;
578         ISD::ParkStatus m_ParkStatus = ISD::PARK_UNKNOWN;
579 
580         QQuickView *m_BaseView = nullptr;
581         QQuickItem *m_BaseObj  = nullptr;
582         QQmlContext *m_Ctxt    = nullptr;
583 
584         QQuickItem *m_SpeedSlider = nullptr, *m_SpeedLabel = nullptr,
585                     *m_raValue = nullptr, *m_deValue = nullptr, *m_azValue = nullptr,
586                      *m_altValue = nullptr, *m_haValue = nullptr, *m_zaValue = nullptr,
587                       *m_targetText = nullptr, *m_targetRAText = nullptr,
588                        *m_targetDEText = nullptr, *m_Park = nullptr, *m_Unpark = nullptr,
589                         *m_statusText = nullptr, *m_J2000Check = nullptr,
590                          *m_JNowCheck = nullptr, *m_equatorialCheck = nullptr,
591                           *m_horizontalCheck = nullptr, *m_haEquatorialCheck = nullptr,
592                            *m_leftRightCheck = nullptr, *m_upDownCheck = nullptr;
593 };
594 }
595 
596 
597 #endif // Mount
598