Kstars

analyze.h
1 /*
2  SPDX-FileCopyrightText: 2020 Hy Murveit <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #ifndef ANALYZE_H
8 #define ANALYZE_H
9 
10 #include <QtDBus>
11 #include <memory>
12 
13 #include "ekos/ekos.h"
14 #include "ekos/mount/mount.h"
15 #include "indi/indimount.h"
16 #include "ui_analyze.h"
17 #include "ekos/scheduler/scheduler.h"
18 
19 class FITSViewer;
20 class OffsetDateTimeTicker;
21 
22 namespace Ekos
23 {
24 
25 class RmsFilter;
26 
27 /**
28  *@class Analyze
29  *@short Analysis tab for Ekos sessions.
30  *@author Hy Murveit
31  *@version 1.0
32  */
33 class Analyze : public QWidget, public Ui::Analyze
34 {
35  Q_OBJECT
36 
37  public:
38  Analyze();
39  ~Analyze();
40 
41  // Baseclass used to represent a segment of Timeline data.
42  class Session
43  {
44  public:
45  // Start and end time in seconds since start of the log.
46  double start, end;
47  // y-offset for the timeline plot. Each line uses a different integer.
48  int offset;
49  // Variables used in temporary sessions. A temporary session
50  // represents a process that has started but not yet finished.
51  // Those are plotted "for now", but will be replaced by the
52  // finished line when the process completes.
53  // Rect is the temporary graphic on the Timeline, and
54  // temporaryBrush defines its look.
56  QBrush temporaryBrush;
57 
58  Session(double s, double e, int o, QCPItemRect *r)
59  : start(s), end(e), offset(o), rect(r) {}
60 
61  // These 2 are used to build tables for the details display.
62  void setupTable(const QString &name, const QString &status,
63  const QDateTime &startClock, const QDateTime &endClock,
64  QTableWidget *table);
65  void addRow(const QString &key, const QString &value);
66 
67  // True if this session is temporary.
68  bool isTemporary() const;
69 
70  private:
71  QTableWidget *details;
72  QString htmlString;
73  };
74  // Below are subclasses of Session used to represent all the different
75  // lines in the Timeline. Each of those hold different types of information
76  // about the process it represents.
77  class CaptureSession : public Session
78  {
79  public:
80  bool aborted;
81  QString filename;
82  double duration;
83  QString filter;
84  double hfr;
85  CaptureSession(double start_, double end_, QCPItemRect *rect,
86  bool aborted_, const QString &filename_,
87  double duration_, const QString &filter_)
88  : Session(start_, end_, CAPTURE_Y, rect),
89  aborted(aborted_), filename(filename_),
90  duration(duration_), filter(filter_), hfr(0) {}
91  CaptureSession() : Session(0, 0, CAPTURE_Y, nullptr) {}
92  };
93  // Guide sessions collapse some of the possible guiding states.
94  // SimpleGuideState are those collapsed states.
95  typedef enum
96  {
97  G_IDLE, G_GUIDING, G_CALIBRATING, G_SUSPENDED, G_DITHERING, G_IGNORE
98  } SimpleGuideState;
99  class GuideSession : public Session
100  {
101  public:
102  SimpleGuideState simpleState;
103  GuideSession(double start_, double end_, QCPItemRect *rect, SimpleGuideState state_)
104  : Session(start_, end_, GUIDE_Y, rect), simpleState(state_) {}
105  GuideSession() : Session(0, 0, GUIDE_Y, nullptr) {}
106  };
107  class AlignSession : public Session
108  {
109  public:
110  AlignState state;
111  AlignSession(double start_, double end_, QCPItemRect *rect, AlignState state_)
112  : Session(start_, end_, ALIGN_Y, rect), state(state_) {}
113  AlignSession() : Session(0, 0, ALIGN_Y, nullptr) {}
114  };
115  class MountSession : public Session
116  {
117  public:
118  ISD::Mount::Status state;
119  MountSession(double start_, double end_, QCPItemRect *rect, ISD::Mount::Status state_)
120  : Session(start_, end_, MOUNT_Y, rect), state(state_) {}
121  MountSession() : Session(0, 0, MOUNT_Y, nullptr) {}
122  };
123  class MountFlipSession : public Session
124  {
125  public:
126  Mount::MeridianFlipStatus state;
127  MountFlipSession(double start_, double end_, QCPItemRect *rect, Mount::MeridianFlipStatus state_)
128  : Session(start_, end_, MERIDIAN_FLIP_Y, rect), state(state_) {}
129  MountFlipSession() : Session(0, 0, MERIDIAN_FLIP_Y, nullptr) {}
130  };
131  class SchedulerJobSession : public Session
132  {
133  public:
134  SchedulerJobSession(double start_, double end_, QCPItemRect *rect,
135  const QString &jobName_, const QString &reason_)
136  : Session(start_, end_, SCHEDULER_Y, rect), jobName(jobName_), reason(reason_) {}
137  SchedulerJobSession() : Session(0, 0, SCHEDULER_Y, nullptr) {}
138  QString jobName;
139  QString reason;
140  };
141  class FocusSession : public Session
142  {
143  public:
144  bool success;
145  double temperature;
146  QString filter;
147  QString points;
148  QVector<double> positions; // Double to be more friendly to QCustomPlot addData.
149  QVector<double> hfrs;
150  FocusSession() : Session(0, 0, FOCUS_Y, nullptr) {}
151  FocusSession(double start_, double end_, QCPItemRect *rect, bool ok, double temperature_,
152  const QString &filter_, const QString &points_);
153  };
154 
155  public slots:
156  // These slots are messages received from the different Ekos processes
157  // used to gather data about those processes.
158 
159  // From Capture
160  void captureComplete(const QVariantMap &metadata);
161  void captureStarting(double exposureSeconds, const QString &filter);
162  void captureAborted(double exposureSeconds);
163 
164  // From Guide
165  void guideState(Ekos::GuideState status);
166  void guideStats(double raError, double decError, int raPulse, int decPulse,
167  double snr, double skyBg, int numStars);
168 
169  // From Focus
170  void autofocusStarting(double temperature, const QString &filter);
171  void autofocusComplete(const QString &filter, const QString &points);
172  void autofocusAborted(const QString &filter, const QString &points);
173  void newTemperature(double temperatureDelta, double temperature);
174 
175  // From Align
176  void alignState(Ekos::AlignState state);
177 
178  // From Mount
179  void mountState(ISD::Mount::Status status);
180  void mountCoords(const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &haValue);
181  void mountFlipStatus(Ekos::Mount::MeridianFlipStatus status);
182 
183  void schedulerJobStarted(const QString &jobName);
184  void schedulerJobEnded(const QString &jobName, const QString &endReason);
185  void newTargetDistance(double targetDistance);
186 
187  private slots:
188 
189  signals:
190 
191  private:
192 
193  // The file-reading, processInputLine(), and signal-slot codepaths share the methods below
194  // to process their messages. Time is the offset in seconds from the start of the log.
195  // BatchMode is true in the file reading path. It means don't call replot() as there may be
196  // many more messages to come. The rest of the args are specific to the message type.
197  void processCaptureStarting(double time, double exposureSeconds, const QString &filter, bool batchMode = false);
198  void processCaptureComplete(double time, const QString &filename, double exposureSeconds, const QString &filter,
199  double hfr, int numStars, int median, double eccentricity, bool batchMode = false);
200  void processCaptureAborted(double time, double exposureSeconds, bool batchMode = false);
201  void processAutofocusStarting(double time, double temperature, const QString &filter, bool batchMode = false);
202  void processAutofocusComplete(double time, const QString &filter, const QString &points, bool batchMode = false);
203  void processAutofocusAborted(double time, const QString &filter, const QString &points, bool batchMode = false);
204  void processTemperature(double time, double temperature, bool batchMode = false);
205  void processGuideState(double time, const QString &state, bool batchMode = false);
206  void processGuideStats(double time, double raError, double decError, int raPulse,
207  int decPulse, double snr, double skyBg, int numStars, bool batchMode = false);
208  void processMountCoords(double time, double ra, double dec, double az, double alt,
209  int pierSide, double ha, bool batchMode = false);
210 
211  void processMountState(double time, const QString &statusString, bool batchMode = false);
212  void processAlignState(double time, const QString &statusString, bool batchMode = false);
213  void processMountFlipState(double time, const QString &statusString, bool batchMode = false);
214 
215  void processSchedulerJobStarted(double time, const QString &jobName, bool batchMode = false);
216  void processSchedulerJobEnded(double time, const QString &jobName, const QString &reason, bool batchMode = false);
217  void checkForMissingSchedulerJobEnd(double time);
218  void processTargetDistance(double time, double targetDistance, bool batchMode = false);
219 
220  // Plotting primatives.
221  void replot(bool adjustSlider = true);
222  void zoomIn();
223  void zoomOut();
224  void scroll(int value);
225  void scrollRight();
226  void scrollLeft();
227 
228  // maxXValue keeps the largest time offset we've received so far.
229  // It represents the extent of the plots (0 -> maxXValue).
230  // This is called each time a message is received in case that message's
231  // time is past the current value of maxXValue.
232  void updateMaxX(double time);
233 
234  // Callbacks for when the timeline is clicked. ProcessTimelineClick
235  // will determine which segment on which line was clicked and then
236  // call captureSessionClicked() or focusSessionClicked, etc.
237  void processTimelineClick(QMouseEvent *event, bool doubleClick);
238  void captureSessionClicked(CaptureSession &c, bool doubleClick);
239  void focusSessionClicked(FocusSession &c, bool doubleClick);
240  void guideSessionClicked(GuideSession &c, bool doubleClick);
241  void mountSessionClicked(MountSession &c, bool doubleClick);
242  void alignSessionClicked(AlignSession &c, bool doubleClick);
243  void mountFlipSessionClicked(MountFlipSession &c, bool doubleClick);
244  void schedulerSessionClicked(SchedulerJobSession &c, bool doubleClick);
245 
246  // Low-level callbacks.
247  // These two call processTimelineClick().
248  void timelineMousePress(QMouseEvent *event);
249  void timelineMouseDoubleClick(QMouseEvent *event);
250  // Calls zoomIn or zoomOut.
251  void timelineMouseWheel(QWheelEvent *event);
252 
253  void processStatsClick(QMouseEvent *event, bool doubleClick);
254  void statsMousePress(QMouseEvent *event);
255  void statsMouseDoubleClick(QMouseEvent *event);
256  void statsMouseMove(QMouseEvent *event);
257  void setupKeyboardShortcuts(QCustomPlot *plot);
258 
259  // (Un)highlights a segment on the timeline after one is clicked.
260  // This indicates which segment's data is displayed in the
261  // graphicsPlot and details table.
262  void highlightTimelineItem(double y, double start, double end);
263  void unhighlightTimelineItem();
264 
265  // logTime() returns the number of seconds between "now" or "time" and
266  // the start of the log. They are useful for recording signal and storing
267  // them to file. They are not useful when reading data from files.
268  double logTime();
269  // Returns the number of seconds between time and the start of the log.
270  double logTime(const QDateTime &time);
271  // Goes back from logSeconds to human-readable clock time.
272  QDateTime clockTime(double logSeconds);
273 
274  // Add a new segment to the Timeline graph.
275  // Returns a rect item, which is only important temporary objects, who
276  // need to erase the item when the temporary session is removed.
277  // This memory is owned by QCustomPlot and shouldn't be freed.
278  // This pointer is stored in Session::rect.
279  QCPItemRect * addSession(double start, double end, double y,
280  const QBrush &brush, const QBrush *stripeBrush = nullptr);
281 
282  // Manage temporary sessions (only used for live data--file-reading doesn't
283  // need temporary sessions). For example, when an image capture has started
284  // but not yet completed, a temporary session is added to the timeline to
285  // represent the not-yet-completed capture.
286  void addTemporarySession(Session *session, double time, double duration,
287  int y_offset, const QBrush &brush);
288  void removeTemporarySession(Session *session);
289  void removeTemporarySessions();
290  void adjustTemporarySession(Session *session);
291  void adjustTemporarySessions();
292 
293  // Add new stats to the statsPlot.
294  void addGuideStats(double raDrift, double decDrift, int raPulse, int decPulse,
295  double snr, int numStars, double skyBackground, double time);
296  void addGuideStatsInternal(double raDrift, double decDrift, double raPulse,
297  double decPulse, double snr, double numStars,
298  double skyBackground, double drift, double rms, double time);
299  void addMountCoords(double ra, double dec, double az, double alt, int pierSide,
300  double ha, double time);
301  void addHFR(double hfr, int numCaptureStars, int median, double eccentricity,
302  const double time, double startTime);
303  void addTemperature(double temperature, const double time);
304  void addTargetDistance(double targetDistance, const double time);
305 
306  // Initialize the graphs (axes, linestyle, pen, name, checkbox callbacks).
307  // Returns the graph index.
308  int initGraph(QCustomPlot *plot, QCPAxis *yAxis, QCPGraph::LineStyle lineStyle,
309  const QColor &color, const QString &name);
310  template <typename Func>
311  int initGraphAndCB(QCustomPlot *plot, QCPAxis *yAxis, QCPGraph::LineStyle lineStyle,
312  const QColor &color, const QString &name, QCheckBox *cb, Func setCb);
313 
314  // Make graphs visible/invisible & add/delete them from the legend.
315  void toggleGraph(int graph_id, bool show);
316 
317  // Initializes the main QCustomPlot windows.
318  void initStatsPlot();
319  void initTimelinePlot();
320  void initGraphicsPlot();
321  void initInputSelection();
322 
323  // Displays the focus positions and HFRs on the graphics plot.
324  void displayFocusGraphics(const QVector<double> &positions, const QVector<double> &hfrs, bool success);
325  // Displays the guider ra and dec drift plot, and computes RMS errors.
326  void displayGuideGraphics(double start, double end, double *raRMS,
327  double *decRMS, double *totalRMS, int *numSamples);
328 
329  // Updates the stats value display boxes next to their checkboxes.
330  void updateStatsValues();
331  // Manages the statsPlot cursor.
332  void setStatsCursor(double time);
333  void removeStatsCursor();
334  void keepCurrent(int state);
335 
336  // Restore checkboxs from Options.
337  void initStatsCheckboxes();
338 
339  // Clears the data, resets the various plots & displays.
340  void reset();
341  void resetGraphicsPlot();
342 
343  // Resets the variables used to process the signals received.
344  void resetCaptureState();
345  void resetAutofocusState();
346  void resetGuideState();
347  void resetGuideStats();
348  void resetAlignState();
349  void resetMountState();
350  void resetMountCoords();
351  void resetMountFlipState();
352  void resetSchedulerJob();
353  void resetTemperature();
354 
355  // Read and display an input .analyze file.
356  double readDataFromFile(const QString &filename);
357  double processInputLine(const QString &line);
358 
359  // Opens a FITS file for viewing.
360  void displayFITS(const QString &filename);
361 
362  // Pop up a help-message window.
363  void helpMessage();
364 
365  // Write the analyze log file message.
366  void saveMessage(const QString &type, const QString &message);
367  // low level file writing.
368  void startLog();
369  void appendToLog(const QString &lines);
370 
371  // The .analyze log file being written.
372  QString logFilename { "" };
373  QFile logFile;
374  bool logInitialized { false };
375 
376  // These define the view for the timeline and stats plots.
377  // The plots start plotStart seconds from the start of the session, and
378  // are plotWidth seconds long. The end of the X-axis is maxXValue.
379  double plotStart { 0.0 };
380  double plotWidth { 10.0 };
381  double maxXValue { 10.0 };
382 
383  // Data are displayed in seconds since the session started.
384  // analyzeStartTime is when the session started, used to translate to clock time.
385  QDateTime analyzeStartTime;
386  QString analyzeTimeZone { "" };
387  bool startTimeInitialized { false };
388 
389  // displayStartTime is similar to analyzeStartTime, but references the
390  // start of the log being displayed (e.g. if reading from a file).
391  // When displaying the current session it should equal analyzeStartTime.
392  QDateTime displayStartTime;
393 
394  // AddGuideStats uses RmsFilter to compute RMS values of the squared
395  // RA and DEC errors, thus calculating the RMS error.
396  std::unique_ptr<RmsFilter> guiderRms;
397  std::unique_ptr<RmsFilter> captureRms;
398 
399  // Y-axes for the for several plots where we rescale based on data.
400  // QCustomPlot owns these pointers' memory, don't free it.
401  QCPAxis *snrAxis;
402  QCPAxis *numStarsAxis;
403  QCPAxis *skyBgAxis;
404  QCPAxis *medianAxis;
405  QCPAxis *numCaptureStarsAxis;
406  QCPAxis *temperatureAxis;
407  QCPAxis *targetDistanceAxis;
408  // Used to keep track of the y-axis position when moving it with the mouse.
409  double yAxisInitialPos = { 0 };
410 
411  // Used to display clock-time on the X-axis.
413 
414  // The rectangle over the current selection.
415  // Memory owned by QCustomPlot.
416  QCPItemRect *selectionHighlight { nullptr };
417 
418  // FITS Viewer to display FITS images.
419  QPointer<FITSViewer> fitsViewer;
420  // When trying to load a FITS file, if the original file path doesn't
421  // work, Analyze tries to find the file under the alternate folder.
422  QString alternateFolder;
423 
424  // The vertical line in the stats plot.
425  QCPItemLine *statsCursor { nullptr };
426  double statsCursorTime { -1 };
427 
428  // Keeps the directory from the last time the user loaded a .analyze file.
429  QUrl dirPath;
430 
431  // True if Analyze is displaying data as it comes in from the other modules.
432  // False if Analyze is displaying data read from a file.
433  bool runtimeDisplay { true };
434 
435  // When a module's session is ongoing, we represent it as a "temporary session"
436  // which will be replaced once the session is done.
437  CaptureSession temporaryCaptureSession;
438  FocusSession temporaryFocusSession;
439  GuideSession temporaryGuideSession;
440  AlignSession temporaryAlignSession;
441  MountSession temporaryMountSession;
442  MountFlipSession temporaryMountFlipSession;
443  SchedulerJobSession temporarySchedulerJobSession;
444 
445  // Capture state-machine variables.
446  double captureStartedTime { -1 };
447  QString captureStartedFilter { "" };
448 
449  // Autofocus state-machine variables.
450  double autofocusStartedTime { -1 };
451  QString autofocusStartedFilter { "" };
452  double autofocusStartedTemperature { 0 };
453 
454  // GuideState state-machine variables.
455  SimpleGuideState lastGuideStateStarted { G_IDLE };
456  double guideStateStartedTime { -1 };
457 
458  // GuideStats state-machine variables.
459  double lastGuideStatsTime { -1 };
460  double lastCaptureRmsTime { -1 };
461  int numStarsMax { 0 };
462  double snrMax { 0 };
463  double skyBgMax { 0 };
464  int medianMax { 0 };
465  int numCaptureStarsMax { 0 };
466  double lastTemperature { -1000 };
467 
468  // AlignState state-machine variables.
469  AlignState lastAlignStateReceived { ALIGN_IDLE };
470  AlignState lastAlignStateStarted { ALIGN_IDLE };
471  double lastAlignStateStartedTime { -1 };
472 
473  // MountState state-machine variables.
474  double mountStateStartedTime { -1 };
475  ISD::Mount::Status lastMountState { ISD::Mount::Status::MOUNT_IDLE };
476 
477  // Mount coords state machine variables.
478  // Used to filter out mount Coords messages--we only process ones
479  // where the values have changed significantly.
480  double lastMountRa { -1 };
481  double lastMountDec { -1 };
482  double lastMountHa { -1 };
483  double lastMountAz { -1 };
484  double lastMountAlt { -1 };
485  int lastMountPierSide { -1 };
486 
487  // Flip state machine variables
488  Mount::MeridianFlipStatus lastMountFlipStateReceived { Mount::FLIP_NONE};
489  Mount::MeridianFlipStatus lastMountFlipStateStarted { Mount::FLIP_NONE };
490  double mountFlipStateStartedTime { -1 };
491 
492  // SchedulerJob state machine variables
493  double schedulerJobStartedTime;
494  QString schedulerJobStartedJobName;
495 
496  QMap<QString, QColor> schedulerJobColors;
497  QBrush schedulerJobBrush(const QString &jobName, bool temporary);
498 
499  // Y-offsets for the timeline plot for the various modules.
500  static constexpr int CAPTURE_Y = 1;
501  static constexpr int FOCUS_Y = 2;
502  static constexpr int ALIGN_Y = 3;
503  static constexpr int GUIDE_Y = 4;
504  static constexpr int MERIDIAN_FLIP_Y = 5;
505  static constexpr int MOUNT_Y = 6;
506  static constexpr int SCHEDULER_Y = 7;
507  static constexpr int LAST_Y = 8;
508 };
509 }
510 
511 
512 #endif // Analyze
Q_OBJECTQ_OBJECT
Manages a single axis inside a QCustomPlot.
Definition: qcustomplot.h:2067
Ekos is an advanced Astrophotography tool for Linux. It is based on a modular extensible framework to...
Definition: align.cpp:70
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
virtual bool event(QEvent *event) override
Primary window to view monochrome and color FITS images. The FITSviewer can open multiple images each...
Definition: fitsviewer.h:48
void show()
A line from one point to another.
Definition: qcustomplot.h:6411
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
Analysis tab for Ekos sessions.
Definition: analyze.h:33
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:3735
A rectangle.
Definition: qcustomplot.h:6512
QString message
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Aug 11 2022 03:59:56 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.