File indexing completed on 2024-04-28 03:43:39
0001 /* Ekos state machine for the meridian flip 0002 SPDX-FileCopyrightText: Wolfgang Reissenberger <sterne-jaeger@openfuture.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include <QObject> 0010 #include "skypoint.h" 0011 0012 #include <ekos_mount_debug.h> 0013 #include "ekos/ekos.h" 0014 0015 #include "indi/indistd.h" 0016 #include "indi/indimount.h" 0017 0018 /** 0019 * @brief A meridian flip is executed by issueing a scope motion to the target. 0020 * 0021 * Essentially, the meridian flip relies upon that the mount hardware selects the 0022 * pier side of the scope on the appropriate side depending whether the scope points east 0023 * or west of the meridian. 0024 * 0025 * While tracking a certain object a pier side change is necessary as soon as the position 0026 * crosses the meridian. As soon as this is detected, the meridian flip works as follows: 0027 * - A meridian flip request is sent to Capture so that a currently running capture can 0028 * be completed before the flip starts. 0029 * - Capture completes the currently running capture, stops capturing and sends a confirmation 0030 * so that the flip may start. 0031 * - Now a slew command is issued to the current target (i.e. the last position where a slew has 0032 * been executed). This will force the mount hardware to reconsider the pier side and slews 0033 * to the same position but changing the pier side. 0034 * - As soon as the slew has been completed alignment is executed, guiding restarted and Capture 0035 * informed that capturing may continue. 0036 */ 0037 0038 namespace Ekos 0039 { 0040 0041 class Mount; 0042 0043 class MeridianFlipState : public QObject 0044 { 0045 Q_OBJECT 0046 public: 0047 explicit MeridianFlipState(QObject *parent = nullptr); 0048 0049 0050 // Meridian flip state of the mount 0051 typedef enum 0052 { 0053 MOUNT_FLIP_NONE, // this is the default state, comparing the hour angle with the next flip position 0054 // it moves to MOUNT_FLIP_PLANNED when a flip is needed 0055 MOUNT_FLIP_PLANNED, // a meridian flip is ready to be started due to the target position and the 0056 // configured offsets and signals to the Capture class that a flip is required 0057 MOUNT_FLIP_WAITING, // step after FLUP_PLANNED waiting until Capture completes a running exposure 0058 MOUNT_FLIP_ACCEPTED, // Capture is idle or has completed the exposure and will wait until the flip 0059 // is completed. 0060 MOUNT_FLIP_RUNNING, // this signals that a flip slew is in progress, when the slew is completed 0061 // the state is set to MOUNT_FLIP_COMPLETED 0062 MOUNT_FLIP_COMPLETED, // this checks that the flip was completed successfully or not and after tidying up 0063 // moves to MOUNT_FLIP_NONE to wait for the next flip requirement. 0064 // Capture sees this and resumes. 0065 MOUNT_FLIP_ERROR // errors in the flip process should end up here 0066 } MeridianFlipMountState; 0067 0068 // overall meridian flip stage 0069 typedef enum { 0070 MF_NONE, /* no meridian flip planned */ 0071 MF_REQUESTED, /* meridian flip necessary, confirmation requested to start the flip */ 0072 MF_READY, /* confirmations received, the meridian flip may start */ 0073 MF_INITIATED, /* meridian flip started */ 0074 MF_FLIPPING, /* slew started to the target position */ 0075 MF_COMPLETED, /* meridian flip completed, re-calibration required */ 0076 MF_ALIGNING, /* alignment running after a successful flip */ 0077 MF_GUIDING /* guiding started after a successful flip */ 0078 } MFStage; 0079 0080 // mount position 0081 typedef struct MountPosition { 0082 SkyPoint position; 0083 ISD::Mount::PierSide pierSide; 0084 dms ha; 0085 bool valid = false; 0086 } MountPosition; 0087 0088 // flag if alignment should be executed after the meridian flip 0089 bool resumeAlignmentAfterFlip() { return m_resumeAlignmentAfterFlip; } 0090 void setResumeAlignmentAfterFlip(bool resume) { m_resumeAlignmentAfterFlip = resume; } 0091 0092 // flag if guiding should be resetarted after the meridian flip 0093 bool resumeGuidingAfterFlip() { return m_resumeGuidingAfterFlip; } 0094 void setResumeGuidingAfterFlip(bool resume) { m_resumeGuidingAfterFlip = resume; } 0095 0096 /** 0097 * @brief Translate the state to a string value. 0098 */ 0099 static QString MFStageString(MFStage stage); 0100 0101 // Is the meridian flip enabled? 0102 bool isEnabled() const { return m_enabled; } 0103 void setEnabled(bool value); 0104 // offset past the meridian 0105 double getOffset() const { return m_offset; } 0106 void setOffset(double newOffset) { m_offset = newOffset; } 0107 0108 /** 0109 * @brief connectMount Establish the connection to the mount 0110 */ 0111 void connectMount(Mount *mount); 0112 0113 MeridianFlipState::MFStage getMeridianFlipStage() const { return meridianFlipStage; }; 0114 const QString &getMeridianStatusText() 0115 { 0116 return m_lastStatusText; 0117 } 0118 0119 /** 0120 * @brief Update the meridian flip stage 0121 */ 0122 void updateMeridianFlipStage(const MFStage &stage); 0123 0124 /** 0125 * @brief Stop a meridian flip if necessary. 0126 * @return true if a meridian flip was running 0127 */ 0128 bool resetMeridianFlip(); 0129 0130 /** 0131 * @brief Execute actions after the flipping slew has completed. 0132 */ 0133 void processFlipCompleted(); 0134 0135 /** 0136 * @brief Check if a meridian flip has already been started 0137 * @return true iff the scope has started the meridian flip 0138 */ 0139 inline bool checkMeridianFlipRunning() 0140 { 0141 return meridianFlipStage == MF_INITIATED || meridianFlipStage == MF_FLIPPING; 0142 } 0143 0144 /** 0145 * @brief Check if a meridian flip is ready to start, running or some post flip actions are not comleted. 0146 */ 0147 inline bool checkMeridianFlipActive() 0148 { 0149 return meridianFlipStage != MF_NONE && meridianFlipStage != MF_REQUESTED; 0150 } 0151 0152 /** 0153 * @brief Update the current target position 0154 * @param pos new target (may be null if no target is set) 0155 */ 0156 void setTargetPosition(SkyPoint *pos); 0157 0158 /** 0159 * @brief Make current target position invalid 0160 */ 0161 void clearTargetPosition() { targetPosition.valid = false; } 0162 0163 /** 0164 * @brief Get the hour angle of that time the mount has slewed to the current position. 0165 * his is used to manage the meridian flip for mounts which do not report pier side. 0166 * (-12.0 < HA <= 12.0) 0167 */ 0168 double initialPositionHA() const; 0169 0170 /** 0171 * @brief access to the meridian flip mount state 0172 */ 0173 MeridianFlipMountState getMeridianFlipMountState() const { return meridianFlipMountState; } 0174 0175 double getFlipDelayHrs() const { return flipDelayHrs; } 0176 void setFlipDelayHrs(double value) { flipDelayHrs = value; } 0177 0178 /** 0179 * @brief Change the meridian flip mount state 0180 */ 0181 void updateMFMountState(MeridianFlipState::MeridianFlipMountState status); 0182 0183 /** 0184 * @brief return the string for the status 0185 */ 0186 static QString meridianFlipStatusString(MeridianFlipMountState status); 0187 0188 // Access to the current mount device 0189 void setMountConnected(bool connected) { m_hasMount = connected; } 0190 0191 // Access to the capture device 0192 void setHasCaptureInterface(bool present) { m_hasCaptureInterface = present; } 0193 0194 public slots: 0195 /** 0196 * @brief Slot for receiving an update of the capture status 0197 */ 0198 void setCaptureState(CaptureState state) { m_CaptureState = state; } 0199 /** 0200 * @brief Slot for receiving an update to the mount status 0201 */ 0202 void setMountStatus(ISD::Mount::Status status); 0203 /** 0204 * @brief Slot for receiving an update to the mount park status 0205 */ 0206 void setMountParkStatus(ISD::ParkStatus status); 0207 /** 0208 * @brief Slot for receiving a new telescope position 0209 * @param position new position 0210 * @param pierSide current pier side 0211 * @param ha current hour angle 0212 */ 0213 void updateTelescopeCoord(const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &ha); 0214 0215 signals: 0216 // mount meridian flip status update event 0217 void newMountMFStatus(MeridianFlipMountState status); 0218 // Communicate a new meridian flip mount status message 0219 void newMeridianFlipMountStatusText(const QString &text); 0220 // slew the telescope to a target 0221 void slewTelescope(SkyPoint &target); 0222 // new log text for the module log window 0223 void newLog(const QString &text); 0224 0225 private: 0226 // flag if meridian flip is enabled 0227 bool m_enabled = false; 0228 // offset post meridian (in degrees) 0229 double m_offset; 0230 // flag if alignment should be executed after the meridian flip 0231 bool m_resumeAlignmentAfterFlip { false }; 0232 // flag if guiding should be resetarted after the meridian flip 0233 bool m_resumeGuidingAfterFlip { false }; 0234 0235 // the mount device 0236 bool m_hasMount { false }; 0237 // capture interface 0238 bool m_hasCaptureInterface { false }; 0239 // the current capture status 0240 CaptureState m_CaptureState { CAPTURE_IDLE }; 0241 // the current mount status 0242 ISD::Mount::Status m_MountStatus = ISD::Mount::MOUNT_IDLE; 0243 // the previous mount status 0244 ISD::Mount::Status m_PrevMountStatus = ISD::Mount::MOUNT_IDLE; 0245 // mount park status 0246 ISD::ParkStatus m_MountParkStatus = ISD::PARK_UNKNOWN; 0247 // current overall meridian flip state 0248 MFStage meridianFlipStage { MF_NONE }; 0249 0250 // current mount meridian flip state 0251 MeridianFlipMountState meridianFlipMountState { MOUNT_FLIP_NONE }; 0252 // last message published to avoid double entries 0253 QString m_lastStatusText = ""; 0254 0255 SkyPoint initialMountCoords; 0256 0257 // current position of the mount 0258 MountPosition currentPosition; 0259 // current target position (might be different from current position!) 0260 MountPosition targetPosition; 0261 0262 static void updatePosition(MountPosition &pos, const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &ha, const bool isValid); 0263 0264 // A meridian flip requires a slew of 180 degrees in the hour angle axis so will take a certain 0265 // amount of time. Within this time, a slewing state change will be ignored for pier side change checks. 0266 QDateTime minMeridianFlipEndTime; 0267 0268 double flipDelayHrs = 0.0; // delays the next flip attempt if it fails 0269 0270 /** 0271 * @brief Internal method for changing the mount meridian flip state. From extermal, use {@see updateMFMountState()} 0272 */ 0273 void setMeridianFlipMountState(MeridianFlipMountState newMeridianFlipMountState); 0274 0275 /** 0276 * @brief Check if a meridian flip if necessary. 0277 * @param lst local sideral time 0278 */ 0279 bool checkMeridianFlip(dms lst); 0280 0281 /** 0282 * @brief Start a meridian flip if necessary. 0283 * @return true if a meridian flip was started 0284 */ 0285 void startMeridianFlip(); 0286 0287 /** 0288 * @brief Calculate the minimal end time from now plus {@see minMeridianFlipEndTime} 0289 */ 0290 void updateMinMeridianFlipEndTime(); 0291 0292 /** 0293 * @brief React upon a meridian flip status change of the mount. 0294 */ 0295 void publishMFMountStatus(MeridianFlipMountState status); 0296 0297 /** 0298 * @brief publish a new meridian flip mount status text 0299 */ 0300 void publishMFMountStatusText(QString text); 0301 0302 /** 0303 * @brief Add log message 0304 */ 0305 void appendLogText(QString message); 0306 0307 }; 0308 } // namespace