Kstars

guidedriftgraph.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 SPDX-FileCopyrightText: 2021 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8#include "guidedriftgraph.h"
9#include "klocalizedstring.h"
10#include "ksnotification.h"
11#include "kstarsdata.h"
12#include "guideinterface.h"
13#include "Options.h"
14
15// Qt version calming
16#include <qtendl.h>
17#include <QToolTip>
18#include <QFileDialog>
19
20GuideDriftGraph::GuideDriftGraph(QWidget *parent)
21{
22 Q_UNUSED(parent);
23 // Drift Graph Color Settings
24 setBackground(QBrush(Qt::black));
25 xAxis->setBasePen(QPen(Qt::white, 1));
26 yAxis->setBasePen(QPen(Qt::white, 1));
27 xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
28 yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
29 xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
30 yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
31 xAxis->grid()->setZeroLinePen(Qt::NoPen);
32 yAxis->grid()->setZeroLinePen(QPen(Qt::white, 1));
33 xAxis->setBasePen(QPen(Qt::white, 1));
34 yAxis->setBasePen(QPen(Qt::white, 1));
35 yAxis2->setBasePen(QPen(Qt::white, 1));
36 xAxis->setTickPen(QPen(Qt::white, 1));
37 yAxis->setTickPen(QPen(Qt::white, 1));
38 yAxis2->setTickPen(QPen(Qt::white, 1));
39 xAxis->setSubTickPen(QPen(Qt::white, 1));
40 yAxis->setSubTickPen(QPen(Qt::white, 1));
41 yAxis2->setSubTickPen(QPen(Qt::white, 1));
42 xAxis->setTickLabelColor(Qt::white);
43 yAxis->setTickLabelColor(Qt::white);
44 yAxis2->setTickLabelColor(Qt::white);
45 xAxis->setLabelColor(Qt::white);
46 yAxis->setLabelColor(Qt::white);
47 yAxis2->setLabelColor(Qt::white);
48
49 RApulseAxis = axisRect()->addAxis(QCPAxis::atRight, 0);
50 RApulseAxis->setVisible(false);
51 RApulseAxis->setRange(-150, 150);
52
53 DECpulseAxis = axisRect()->addAxis(QCPAxis::atLeft, 0);
54 DECpulseAxis->setVisible(false);
55 DECpulseAxis->setRange(-150, 150);
56
57 snrAxis = axisRect()->addAxis(QCPAxis::atLeft, 0);
58 snrAxis->setVisible(false);
59 // This will be reset to the actual data values.
60 snrAxis->setRange(-100, 100);
61
62 //Horizontal Axis Time Ticker Settings
63 QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime);
64 timeTicker->setTimeFormat("%m:%s");
65 xAxis->setTicker(timeTicker);
66
67 // Axis Labels Settings
68 yAxis2->setVisible(true);
69 yAxis2->setTickLabels(true);
70 yAxis->setLabelFont(QFont(font().family(), 10));
71 yAxis2->setLabelFont(QFont(font().family(), 10));
72 xAxis->setTickLabelFont(QFont(font().family(), 9));
73 yAxis->setTickLabelFont(QFont(font().family(), 9));
74 yAxis2->setTickLabelFont(QFont(font().family(), 9));
75 yAxis->setLabelPadding(1);
76 yAxis2->setLabelPadding(1);
77 yAxis->setLabel(i18n("Mount DE Drift (arcsec)"));
78 yAxis2->setLabel(i18n("Mount RA Drift (arcsec)"));
79
80 setupNSEWLabels();
81
82 // Sets the default ranges
83 xAxis->setRange(0, 120, Qt::AlignRight);
84 // DEC-axis
85 yAxis->setRange(-3, 3);
86 // RA-axis
87 yAxis2->setRange(-3, 3);
88 yAxis2->setRangeReversed(true);
89 // Make DEC-axis transfer its range to RA-axis maintaining reversed range
90 connect(yAxis, static_cast<void (QCPAxis::*)(const QCPRange&)>(&QCPAxis::rangeChanged),
91 this, [ = ](QCPRange newRange)
92 {
93 yAxis2->setRangeLower(-newRange.upper);
94 yAxis2->setRangeUpper(-newRange.lower);
95 });
96
97
98 //This sets up the legend
99 legend->setVisible(true);
100 legend->setFont(QFont(font().family(), 7));
101 legend->setTextColor(Qt::white);
102 legend->setBrush(QBrush(Qt::black));
103 legend->setIconSize(4, 12);
104 legend->setFillOrder(QCPLegend::foColumnsFirst);
106
107 // RA Curve
109 graph(GuideGraph::G_RA)->setPen(QPen(KStarsData::Instance()->colorScheme()
110 ->colorNamed("RAGuideError")));
111 graph(GuideGraph::G_RA)->setName("RA");
112 graph(GuideGraph::G_RA)->setLineStyle(QCPGraph::lsLine);
113
114 // DE Curve
116 graph(GuideGraph::G_DEC)->setPen(QPen(KStarsData::Instance()->colorScheme()
117 ->colorNamed("DEGuideError")));
118 graph(GuideGraph::G_DEC)->setName("DE");
119 graph(GuideGraph::G_DEC)->setLineStyle(QCPGraph::lsLine);
120
121 // RA highlighted Point
123 graph(GuideGraph::G_RA_HIGHLIGHT)->setLineStyle(QCPGraph::lsNone);
124 graph(GuideGraph::G_RA_HIGHLIGHT)->setPen(QPen(KStarsData::Instance()
125 ->colorScheme()->colorNamed("RAGuideError")));
126 graph(GuideGraph::G_RA_HIGHLIGHT)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssPlusCircle,
127 QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"), 2), QBrush(), 10));
128
129 // DE highlighted Point
131 graph(GuideGraph::G_DEC_HIGHLIGHT)->setLineStyle(QCPGraph::lsNone);
132 graph(GuideGraph::G_DEC_HIGHLIGHT)->setPen(QPen(KStarsData::Instance()
133 ->colorScheme()->colorNamed("DEGuideError")));
134 graph(GuideGraph::G_DEC_HIGHLIGHT)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssPlusCircle,
135 QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"), 2), QBrush(), 10));
136
137 // RA Pulse
138 addGraph(xAxis, RApulseAxis);
139 QColor raPulseColor(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"));
140 raPulseColor.setAlpha(75);
141 graph(GuideGraph::G_RA_PULSE)->setPen(QPen(raPulseColor));
142 graph(GuideGraph::G_RA_PULSE)->setBrush(QBrush(raPulseColor, Qt::Dense4Pattern));
143 graph(GuideGraph::G_RA_PULSE)->setName("RA Pulse");
144 graph(GuideGraph::G_RA_PULSE)->setLineStyle(QCPGraph::lsStepLeft);
145
146 // DEC Pulse
147 addGraph(xAxis, DECpulseAxis);
148 QColor dePulseColor(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"));
149 dePulseColor.setAlpha(75);
150 graph(GuideGraph::G_DEC_PULSE)->setPen(QPen(dePulseColor));
151 graph(GuideGraph::G_DEC_PULSE)->setBrush(QBrush(dePulseColor, Qt::Dense4Pattern));
152 graph(GuideGraph::G_DEC_PULSE)->setName("DEC Pulse");
153 graph(GuideGraph::G_DEC_PULSE)->setLineStyle(QCPGraph::lsStepLeft);
154
155 // SNR
156 addGraph(xAxis, snrAxis);
157 graph(GuideGraph::G_SNR)->setPen(QPen(Qt::yellow));
158 graph(GuideGraph::G_SNR)->setName("SNR");
159 graph(GuideGraph::G_SNR)->setLineStyle(QCPGraph::lsLine);
160
161 // RA RMS
163 graph(GuideGraph::G_RA_RMS)->setPen(QPen(Qt::red));
164 graph(GuideGraph::G_RA_RMS)->setName("RA RMS");
165 graph(GuideGraph::G_RA_RMS)->setLineStyle(QCPGraph::lsLine);
166
167 // DEC RMS
169 graph(GuideGraph::G_DEC_RMS)->setPen(QPen(Qt::red));
170 graph(GuideGraph::G_DEC_RMS)->setName("DEC RMS");
171 graph(GuideGraph::G_DEC_RMS)->setLineStyle(QCPGraph::lsLine);
172
173 // Total RMS
175 graph(GuideGraph::G_RMS)->setPen(QPen(Qt::red));
176 graph(GuideGraph::G_RMS)->setName("RMS");
177 graph(GuideGraph::G_RMS)->setLineStyle(QCPGraph::lsLine);
178
179 //This will prevent the highlighted points and Pulses from showing up in the legend.
180 legend->removeItem(GuideGraph::G_DEC_RMS);
181 legend->removeItem(GuideGraph::G_RA_RMS);
182 legend->removeItem(GuideGraph::G_DEC_PULSE);
183 legend->removeItem(GuideGraph::G_RA_PULSE);
184 legend->removeItem(GuideGraph::G_DEC_HIGHLIGHT);
185 legend->removeItem(GuideGraph::G_RA_HIGHLIGHT);
186
188 axisRect()->setRangeZoom(Qt::Orientation::Vertical);
190 //This sets the visibility of graph components to the stored values.
191 graph(GuideGraph::G_RA)->setVisible(Options::rADisplayedOnGuideGraph()); //RA data
192 graph(GuideGraph::G_DEC)->setVisible(Options::dEDisplayedOnGuideGraph()); //DEC data
193 graph(GuideGraph::G_RA_HIGHLIGHT)->setVisible(Options::rADisplayedOnGuideGraph()); //RA highlighted point
194 graph(GuideGraph::G_DEC_HIGHLIGHT)->setVisible(Options::dEDisplayedOnGuideGraph()); //DEC highlighted point
195 graph(GuideGraph::G_RA_PULSE)->setVisible(Options::rACorrDisplayedOnGuideGraph()); //RA Pulses
196 graph(GuideGraph::G_DEC_PULSE)->setVisible(Options::dECorrDisplayedOnGuideGraph()); //DEC Pulses
197 graph(GuideGraph::G_SNR)->setVisible(Options::sNRDisplayedOnGuideGraph()); //SNR
198 setRMSVisibility();
199
200 // updateCorrectionsScaleVisibility();
201}
202
203void GuideDriftGraph::guideHistory(int sliderValue, bool graphOnLatestPt)
204{
205 graph(GuideGraph::G_RA_HIGHLIGHT)->data()->clear(); //Clear RA highlighted point
206 graph(GuideGraph::G_DEC_HIGHLIGHT)->data()->clear(); //Clear DEC highlighted point
207 double t = graph(GuideGraph::G_RA)->dataMainKey(sliderValue); //Get time from RA data
208 double ra = graph(GuideGraph::G_RA)->dataMainValue(sliderValue); //Get RA from RA data
209 double de = graph(GuideGraph::G_DEC)->dataMainValue(sliderValue); //Get DEC from DEC data
210 double raPulse = graph(GuideGraph::G_RA_PULSE)->dataMainValue(sliderValue); //Get RA Pulse from RA pulse data
211 double dePulse = graph(GuideGraph::G_DEC_PULSE)->dataMainValue(sliderValue); //Get DEC Pulse from DEC pulse data
212 graph(GuideGraph::G_RA_HIGHLIGHT)->addData(t, ra); //Set RA highlighted point
213 graph(GuideGraph::G_DEC_HIGHLIGHT)->addData(t, de); //Set DEC highlighted point
214
215 //This will allow the graph to scroll left and right along with the guide slider
216 if (xAxis->range().contains(t) == false)
217 {
218 if(t < xAxis->range().lower)
219 {
220 xAxis->setRange(t, t + xAxis->range().size());
221 }
222 if(t > xAxis->range().upper)
223 {
224 xAxis->setRange(t - xAxis->range().size(), t);
225 }
226 }
227 replot();
228 double snr = 0;
229 if (graph(GuideGraph::G_SNR)->data()->size() > 0)
230 snr = graph(GuideGraph::G_SNR)->dataMainValue(sliderValue);
231 double rms = graph(GuideGraph::G_RMS)->dataMainValue(sliderValue);
232
233 if(!graphOnLatestPt)
234 {
235 QTime localTime = guideTimer;
236 localTime = localTime.addSecs(t);
237
238 QPoint localTooltipCoordinates = graph(GuideGraph::G_RA)->dataPixelPosition(sliderValue).toPoint();
239 QPoint globalTooltipCoordinates = mapToGlobal(localTooltipCoordinates);
240
241 if(raPulse == 0 && dePulse == 0)
242 {
244 globalTooltipCoordinates,
245 i18nc("Drift graphics tooltip; %1 is local time; %2 is RA deviation; %3 is DE deviation in arcseconds; %4 is the RMS error in arcseconds; %5 is the SNR",
246 "<table>"
247 "<tr><td>LT: </td><td>%1</td></tr>"
248 "<tr><td>RA: </td><td>%2 \"</td></tr>"
249 "<tr><td>DE: </td><td>%3 \"</td></tr>"
250 "<tr><td>RMS: </td><td>%4 \"</td></tr>"
251 "<tr><td>SNR: </td><td>%5 \"</td></tr>"
252 "</table>",
253 localTime.toString("hh:mm:ss AP"),
254 QString::number(ra, 'f', 2),
255 QString::number(de, 'f', 2),
256 QString::number(rms, 'f', 2),
257 QString::number(snr, 'f', 1)
258 ));
259 }
260 else
261 {
263 globalTooltipCoordinates,
264 i18nc("Drift graphics tooltip; %1 is local time; %2 is RA deviation; %3 is DE deviation in arcseconds; %4 is the RMS error in arcseconds; %5 is the SNR; %6 is RA Pulse in ms; %7 is DE Pulse in ms",
265 "<table>"
266 "<tr><td>LT: </td><td>%1</td></tr>"
267 "<tr><td>RA: </td><td>%2 \"</td></tr>"
268 "<tr><td>DE: </td><td>%3 \"</td></tr>"
269 "<tr><td>RMS: </td><td>%4 \"</td></tr>"
270 "<tr><td>SNR: </td><td>%5 \"</td></tr>"
271 "<tr><td>RA Pulse: </td><td>%6 ms</td></tr>"
272 "<tr><td>DE Pulse: </td><td>%7 ms</td></tr>"
273 "</table>",
274 localTime.toString("hh:mm:ss AP"),
275 QString::number(ra, 'f', 2),
276 QString::number(de, 'f', 2),
277 QString::number(rms, 'f', 2),
278 QString::number(snr, 'f', 1),
279 QString::number(raPulse, 'f', 2),
280 QString::number(dePulse, 'f', 2)
281 )); //The pulses were divided by 100 before they were put on the graph.
282 }
283
284 }
285}
286
287void GuideDriftGraph::handleVerticalPlotSizeChange()
288{
289}
290
291void GuideDriftGraph::handleHorizontalPlotSizeChange()
292{
293}
294
295void GuideDriftGraph::setupNSEWLabels()
296{
297 //Labels for N/S/E/W
298 QColor raLabelColor(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"));
299 QColor deLabelColor(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"));
300
301 QCPItemText *northLabel = new QCPItemText(this);
302 northLabel->setColor(deLabelColor);
303 northLabel->setFont(QFont(font().family(), 9));
304 northLabel->setText(i18nc("North", "N"));
305 northLabel->position->setType(QCPItemPosition::ptViewportRatio);
306 northLabel->position->setCoords(0.15, 0.12);
307 northLabel->setVisible(true);
308
309 QCPItemText *southLabel = new QCPItemText(this);
310 southLabel->setColor(deLabelColor);
311 southLabel->setFont(QFont(font().family(), 9));
312 southLabel->setText(i18nc("South", "S"));
313 southLabel->position->setType(QCPItemPosition::ptViewportRatio);
314 southLabel->position->setCoords(0.15, 0.85);
315 southLabel->setVisible(true);
316
317 QCPItemText *westLabel = new QCPItemText(this);
318 westLabel->setColor(raLabelColor);
319 westLabel->setFont(QFont(font().family(), 9));
320 westLabel->setText(i18nc("West", "W"));
321 westLabel->position->setType(QCPItemPosition::ptViewportRatio);
322 westLabel->position->setCoords(0.85, 0.12);
323 westLabel->setVisible(true);
324
325 QCPItemText *eastLabel = new QCPItemText(this);
326 eastLabel->setColor(raLabelColor);
327 eastLabel->setFont(QFont(font().family(), 9));
328 eastLabel->setText(i18nc("East", "E"));
329 eastLabel->position->setType(QCPItemPosition::ptViewportRatio);
330 eastLabel->position->setCoords(0.85, 0.85);
331 eastLabel->setVisible(true);
332
333}
334
335void GuideDriftGraph::autoScaleGraphs()
336{
337 yAxis->setRange(-3, 3);
338 yAxis2->setRange(-3, 3);
339 // First bool below is only_enlarge, 2nd is only look at values that are visible in X.
340 // Net result is all RA & DEC points within the times being plotted should be visible.
341 // This is only called when the autoScale button is pressed.
342 graph(GuideGraph::G_RA)->rescaleValueAxis(false, true);
343 graph(GuideGraph::G_DEC)->rescaleValueAxis(true, true);
344 replot();
345}
346
347void GuideDriftGraph::zoomX(int zoomLevel)
348{
349 double key = (guideElapsedTimer.isValid() || guideTimer.isValid()
350 || guideTimer.isNull()) ? 0 : guideElapsedTimer.elapsed() / 1000.0;
351
352 // The # of seconds displayd on the x-axis of the drift-graph for the various zoom levels.
353 static std::vector<int> zoomLevels = {15, 30, 60, 120, 300, 900, 1800, 3600, 7200, 14400};
354
355 zoomLevel = std::max(0, zoomLevel);
356 driftGraphZoomLevel = std::min(static_cast<int>(zoomLevels.size() - 1), zoomLevel);
357
358 xAxis->setRange(key - zoomLevels[driftGraphZoomLevel], key);
359}
360
361void GuideDriftGraph::zoomInX()
362{
363 zoomX(driftGraphZoomLevel - 1);
364 replot();
365}
366
367void GuideDriftGraph::zoomOutX()
368{
369 zoomX(driftGraphZoomLevel + 1);
370 replot();
371}
372
373void GuideDriftGraph::setCorrectionGraphScale(int value)
374{
375 RApulseAxis->setRange(yAxis->range().lower * value,
376 yAxis->range().upper * value);
377 DECpulseAxis->setRange(yAxis->range().lower * value,
378 yAxis->range().upper * value);
379 replot();
380}
381
382void GuideDriftGraph::clear()
383{
384 graph(GuideGraph::G_RA)->data()->clear(); //RA data
385 graph(GuideGraph::G_DEC)->data()->clear(); //DEC data
386 graph(GuideGraph::G_RA_HIGHLIGHT)->data()->clear(); //RA highlighted point
387 graph(GuideGraph::G_DEC_HIGHLIGHT)->data()->clear(); //DEC highlighted point
388 graph(GuideGraph::G_RA_PULSE)->data()->clear(); //RA Pulses
389 graph(GuideGraph::G_DEC_PULSE)->data()->clear(); //DEC Pulses
390 graph(GuideGraph::G_SNR)->data()->clear(); //SNR
391 graph(GuideGraph::G_RA_RMS)->data()->clear(); //RA RMS
392 graph(GuideGraph::G_DEC_RMS)->data()->clear(); //DEC RMS
393 graph(GuideGraph::G_RMS)->data()->clear(); //RMS
394 clearItems(); //Clears dither text items from the graph
395 setupNSEWLabels();
396 replot();
397}
398
399void GuideDriftGraph::toggleShowPlot(GuideGraph::DRIFT_GRAPH_INDICES plot, bool isChecked)
400{
401 switch (plot)
402 {
403 case GuideGraph::G_RA:
404 Options::setRADisplayedOnGuideGraph(isChecked);
405 graph(GuideGraph::G_RA)->setVisible(isChecked);
406 graph(GuideGraph::G_RA_HIGHLIGHT)->setVisible(isChecked);
407 setRMSVisibility();
408 replot();
409 break;
410 case GuideGraph::G_DEC:
411 Options::setDEDisplayedOnGuideGraph(isChecked);
412 graph(GuideGraph::G_DEC)->setVisible(isChecked);
413 graph(GuideGraph::G_DEC_HIGHLIGHT)->setVisible(isChecked);
414 setRMSVisibility();
415 replot();
416 break;
417 case GuideGraph::G_RA_PULSE:
418 Options::setRACorrDisplayedOnGuideGraph(isChecked);
419 graph(GuideGraph::G_RA_PULSE)->setVisible(isChecked);
420 // updateCorrectionsScaleVisibility();
421 break;
422 case GuideGraph::G_DEC_PULSE:
423 Options::setDECorrDisplayedOnGuideGraph(isChecked);
424 graph(GuideGraph::G_DEC_PULSE)->setVisible(isChecked);
425 // updateCorrectionsScaleVisibility();
426 break;
427 case GuideGraph::G_SNR:
428 Options::setSNRDisplayedOnGuideGraph(isChecked);
429 graph(GuideGraph::G_SNR)->setVisible(isChecked);
430 replot();
431 break;
432 case GuideGraph::G_RMS:
433 Options::setRMSDisplayedOnGuideGraph(isChecked);
434 setRMSVisibility();
435 replot();
436 break;
437 default:
438 break;
439 }
440}
441
442void GuideDriftGraph::setRMSVisibility()
443{
444 if (!Options::rMSDisplayedOnGuideGraph())
445 {
446 graph(GuideGraph::G_RA_RMS)->setVisible(false);
447 graph(GuideGraph::G_DEC_RMS)->setVisible(false);
448 graph(GuideGraph::G_RMS)->setVisible(false);
449 return;
450 }
451
452 if ((Options::dEDisplayedOnGuideGraph() && Options::rADisplayedOnGuideGraph()) ||
453 (!Options::dEDisplayedOnGuideGraph() && !Options::rADisplayedOnGuideGraph()))
454 {
455 graph(GuideGraph::G_RA_RMS)->setVisible(false);
456 graph(GuideGraph::G_DEC_RMS)->setVisible(false);
457 graph(GuideGraph::G_RMS)->setVisible(true);
458 }
459 else if (!Options::dEDisplayedOnGuideGraph() && Options::rADisplayedOnGuideGraph())
460 {
461 graph(GuideGraph::G_RA_RMS)->setVisible(true);
462 graph(GuideGraph::G_DEC_RMS)->setVisible(false);
463 graph(GuideGraph::G_RMS)->setVisible(false);
464 }
465 else
466 {
467 graph(GuideGraph::G_RA_RMS)->setVisible(false);
468 graph(GuideGraph::G_DEC_RMS)->setVisible(true);
469 graph(GuideGraph::G_RMS)->setVisible(false);
470 }
471}
472
473void GuideDriftGraph::exportGuideData()
474{
475 int numPoints = graph(GuideGraph::G_RA)->dataCount();
476 if (numPoints == 0)
477 return;
478
479 QUrl exportFile = QFileDialog::getSaveFileUrl(this, i18nc("@title:window", "Export Guide Data"), guideURLPath,
480 "CSV File (*.csv)");
481 if (exportFile.isEmpty()) // if user presses cancel
482 return;
483 if (exportFile.toLocalFile().endsWith(QLatin1String(".csv")) == false)
484 exportFile.setPath(exportFile.toLocalFile() + ".csv");
485
486 QString path = exportFile.toLocalFile();
487
488 if (QFile::exists(path))
489 {
491 i18n("A file named \"%1\" already exists. "
492 "Overwrite it?",
493 exportFile.fileName()),
494 i18n("Overwrite File?"), KStandardGuiItem::overwrite());
495 if (r == KMessageBox::Cancel)
496 return;
497 }
498
499 if (!exportFile.isValid())
500 {
501 QString message = i18n("Invalid URL: %1", exportFile.url());
502 KSNotification::sorry(message, i18n("Invalid URL"));
503 return;
504 }
505
506 QFile file;
507 file.setFileName(path);
508 if (!file.open(QIODevice::WriteOnly))
509 {
510 QString message = i18n("Unable to write to file %1", path);
511 KSNotification::sorry(message, i18n("Could Not Open File"));
512 return;
513 }
514
515 QTextStream outstream(&file);
516
517 outstream <<
518 "Frame #, Time Elapsed (sec), Local Time (HMS), RA Error (arcsec), DE Error (arcsec), RA Pulse (ms), DE Pulse (ms)" <<
519 Qt::endl;
520
521 for (int i = 0; i < numPoints; i++)
522 {
523 double t = graph(GuideGraph::G_RA)->dataMainKey(i);
524 double ra = graph(GuideGraph::G_RA)->dataMainValue(i);
525 double de = graph(GuideGraph::G_DEC)->dataMainValue(i);
526 double raPulse = graph(GuideGraph::G_RA_PULSE)->dataMainValue(i);
527 double dePulse = graph(GuideGraph::G_DEC_PULSE)->dataMainValue(i);
528
529 QTime localTime = guideTimer;
530 localTime = localTime.addSecs(t);
531
532 outstream << i << ',' << t << ',' << localTime.toString("hh:mm:ss AP") << ',' << ra << ',' << de << ',' << raPulse << ',' <<
533 dePulse << ',' << Qt::endl;
534 }
535 file.close();
536}
537
538void GuideDriftGraph::resetTimer()
539{
540 guideTimer = QTime::currentTime();
541 guideElapsedTimer.start();
542}
543
544void GuideDriftGraph::connectGuider(Ekos::GuideInterface *guider)
545{
546 connect(guider, &Ekos::GuideInterface::newAxisDelta, this, &GuideDriftGraph::setAxisDelta);
547 connect(guider, &Ekos::GuideInterface::newAxisPulse, this, &GuideDriftGraph::setAxisPulse);
548 connect(guider, &Ekos::GuideInterface::newAxisSigma, this, &GuideDriftGraph::setAxisSigma);
549 connect(guider, &Ekos::GuideInterface::newSNR, this, &GuideDriftGraph::setSNR);
550
551 resetTimer();
552}
553
554void GuideDriftGraph::setAxisDelta(double ra, double de)
555{
556 // Time since timer started.
557 double key = guideElapsedTimer.elapsed() / 1000.0;
558
559 graph(GuideGraph::G_RA)->addData(key, ra);
560 graph(GuideGraph::G_DEC)->addData(key, de);
561
562 if(graphOnLatestPt)
563 {
564 xAxis->setRange(key, xAxis->range().size(), Qt::AlignRight);
565 graph(GuideGraph::G_RA_HIGHLIGHT)->data()->clear(); //Clear highlighted RA point
566 graph(GuideGraph::G_DEC_HIGHLIGHT)->data()->clear(); //Clear highlighted DEC point
567 graph(GuideGraph::G_RA_HIGHLIGHT)->addData(key, ra); //Set highlighted RA point to latest point
568 graph(GuideGraph::G_DEC_HIGHLIGHT)->addData(key, de); //Set highlighted DEC point to latest point
569 }
570 replot();
571}
572
573void GuideDriftGraph::setAxisSigma(double ra, double de)
574{
575 const double key = guideElapsedTimer.elapsed() / 1000.0;
576 const double total = std::hypot(ra, de);
577 graph(GuideGraph::G_RA_RMS)->addData(key, ra);
578 graph(GuideGraph::G_DEC_RMS)->addData(key, de);
579 graph(GuideGraph::G_RMS)->addData(key, total);
580}
581
582void GuideDriftGraph::setAxisPulse(double ra, double de)
583{
584 double key = guideElapsedTimer.elapsed() / 1000.0;
585 graph(GuideGraph::G_RA_PULSE)->addData(key, ra);
586 graph(GuideGraph::G_DEC_PULSE)->addData(key, de);
587}
588
589void GuideDriftGraph::setSNR(double snr)
590{
591 double key = guideElapsedTimer.elapsed() / 1000.0;
592 graph(GuideGraph::G_SNR)->addData(key, snr);
593
594 // Sets the SNR axis to have the maximum be 95% of the way up from the middle to the top.
595 QCPGraphData snrMax = *std::min_element(graph(GuideGraph::G_SNR)->data()->begin(),
596 graph(GuideGraph::G_SNR)->data()->end(),
597 [](QCPGraphData const & s1, QCPGraphData const & s2)
598 {
599 return s1.value > s2.value;
600 });
601 snrAxis->setRange(-1.05 * snrMax.value, 1.05 * snrMax.value);
602}
603
604void GuideDriftGraph::updateCorrectionsScaleVisibility()
605{
606 bool isVisible = (Options::rACorrDisplayedOnGuideGraph() || Options::dECorrDisplayedOnGuideGraph());
607 yAxis2->setVisible(isVisible);
608 replot();
609}
610
611void GuideDriftGraph::mouseOverLine(QMouseEvent *event)
612{
613 double key = xAxis->pixelToCoord(event->localPos().x());
614
615 if (xAxis->range().contains(key))
616 {
617 if (plottableAt(event->pos(), false))
618 {
619 int raIndex = graph(GuideGraph::G_RA)->findBegin(key);
620 int deIndex = graph(GuideGraph::G_DEC)->findBegin(key);
621 int rmsIndex = graph(GuideGraph::G_RMS)->findBegin(key);
622
623 double raDelta = graph(GuideGraph::G_RA)->dataMainValue(raIndex);
624 double deDelta = graph(GuideGraph::G_DEC)->dataMainValue(deIndex);
625
626 double raPulse = graph(GuideGraph::G_RA_PULSE)->dataMainValue(raIndex); //Get RA Pulse from RA pulse data
627 double dePulse = graph(GuideGraph::G_DEC_PULSE)->dataMainValue(deIndex); //Get DEC Pulse from DEC pulse data
628
629 double rms = graph(GuideGraph::G_RMS)->dataMainValue(rmsIndex);
630 double snr = 0;
631 if (graph(GuideGraph::G_SNR)->data()->size() > 0)
632 {
633 int snrIndex = graph(GuideGraph::G_SNR)->findBegin(key);
634 snr = graph(GuideGraph::G_SNR)->dataMainValue(snrIndex);
635 }
636
637 // Compute time value:
638 QTime localTime = guideTimer;
639
640 localTime = localTime.addSecs(key);
641
643 if(raPulse == 0 && dePulse == 0)
644 {
646 event->globalPos(),
647 i18nc("Drift graphics tooltip; %1 is local time; %2 is RA deviation; %3 is DE deviation in arcseconds; %4 is the RMS error in arcseconds; %5 is the SNR",
648 "<table>"
649 "<tr><td>LT: </td><td>%1</td></tr>"
650 "<tr><td>RA: </td><td>%2 \"</td></tr>"
651 "<tr><td>DE: </td><td>%3 \"</td></tr>"
652 "<tr><td>RMS: </td><td>%4 \"</td></tr>"
653 "<tr><td>SNR: </td><td>%5 \"</td></tr>"
654 "</table>",
655 localTime.toString("hh:mm:ss AP"),
656 QString::number(raDelta, 'f', 2), QString::number(deDelta, 'f', 2),
657 QString::number(rms, 'f', 2), QString::number(snr, 'f', 1)));
658 }
659 else
660 {
662 event->globalPos(),
663 i18nc("Drift graphics tooltip; %1 is local time; %2 is RA deviation; %3 is DE deviation in arcseconds; %4 is the RMS error in arcseconds; %5 is the SNR; %6 is RA Pulse in ms; %7 is DE Pulse in ms",
664 "<table>"
665 "<tr><td>LT: </td><td>%1</td></tr>"
666 "<tr><td>RA: </td><td>%2 \"</td></tr>"
667 "<tr><td>DE: </td><td>%3 \"</td></tr>"
668 "<tr><td>RMS: </td><td>%4 \"</td></tr>"
669 "<tr><td>SNR: </td><td>%5 \"</td></tr>"
670 "<tr><td>RA Pulse: </td><td>%6 ms</td></tr>"
671 "<tr><td>DE Pulse: </td><td>%7 ms</td></tr>"
672 "</table>",
673 localTime.toString("hh:mm:ss AP"),
674 QString::number(raDelta, 'f', 2),
675 QString::number(deDelta, 'f', 2),
676 QString::number(rms, 'f', 2),
677 QString::number(snr, 'f', 1),
678 QString::number(raPulse, 'f', 2),
679 QString::number(dePulse, 'f', 2))); //The pulses were divided by 100 before they were put on the graph.
680 }
681 }
682 else
684
685 replot();
686 }
687
688 if (xAxis->range().contains(key))
689 {
690 QCPGraph *qcpgraph = qobject_cast<QCPGraph *>(plottableAt(event->pos(), false));
691
692 if (qcpgraph)
693 {
694 int raIndex = graph(GuideGraph::G_RA)->findBegin(key);
695 int deIndex = graph(GuideGraph::G_DEC)->findBegin(key);
696 int rmsIndex = graph(GuideGraph::G_RMS)->findBegin(key);
697
698 double raDelta = graph(GuideGraph::G_RA)->dataMainValue(raIndex);
699 double deDelta = graph(GuideGraph::G_DEC)->dataMainValue(deIndex);
700
701 double raPulse = graph(GuideGraph::G_RA_PULSE)->dataMainValue(raIndex); //Get RA Pulse from RA pulse data
702 double dePulse = graph(GuideGraph::G_DEC_PULSE)->dataMainValue(deIndex); //Get DEC Pulse from DEC pulse data
703
704 double rms = graph(GuideGraph::G_RMS)->dataMainValue(rmsIndex);
705 double snr = 0;
706 if (graph(GuideGraph::G_SNR)->data()->size() > 0)
707 {
708 int snrIndex = graph(GuideGraph::G_SNR)->findBegin(key);
709 snr = graph(GuideGraph::G_SNR)->dataMainValue(snrIndex);
710 }
711
712 // Compute time value:
713 QTime localTime = guideTimer;
714
715 localTime = localTime.addSecs(key);
716
718 if(raPulse == 0 && dePulse == 0)
719 {
721 event->globalPos(),
722 i18nc("Drift graphics tooltip; %1 is local time; %2 is RA deviation; %3 is DE deviation in arcseconds; %4 is the RMS error in arcseconds; %5 is the SNR",
723 "<table>"
724 "<tr><td>LT: </td><td>%1</td></tr>"
725 "<tr><td>RA: </td><td>%2 \"</td></tr>"
726 "<tr><td>DE: </td><td>%3 \"</td></tr>"
727 "<tr><td>RMS: </td><td>%4 \"</td></tr>"
728 "<tr><td>SNR: </td><td>%5 \"</td></tr>"
729 "</table>",
730 localTime.toString("hh:mm:ss AP"),
731 QString::number(raDelta, 'f', 2), QString::number(deDelta, 'f', 2),
732 QString::number(rms, 'f', 2), QString::number(snr, 'f', 1)));
733 }
734 else
735 {
737 event->globalPos(),
738 i18nc("Drift graphics tooltip; %1 is local time; %2 is RA deviation; %3 is DE deviation in arcseconds; %4 is the RMS error in arcseconds; %5 is the SNR; %6 is RA Pulse in ms; %7 is DE Pulse in ms",
739 "<table>"
740 "<tr><td>LT: </td><td>%1</td></tr>"
741 "<tr><td>RA: </td><td>%2 \"</td></tr>"
742 "<tr><td>DE: </td><td>%3 \"</td></tr>"
743 "<tr><td>RMS: </td><td>%4 \"</td></tr>"
744 "<tr><td>SNR: </td><td>%5 \"</td></tr>"
745 "<tr><td>RA Pulse: </td><td>%6 ms</td></tr>"
746 "<tr><td>DE Pulse: </td><td>%7 ms</td></tr>"
747 "</table>",
748 localTime.toString("hh:mm:ss AP"),
749 QString::number(raDelta, 'f', 2),
750 QString::number(deDelta, 'f', 2),
751 QString::number(rms, 'f', 2),
752 QString::number(snr, 'f', 1),
753 QString::number(raPulse, 'f', 2),
754 QString::number(dePulse, 'f', 2))); //The pulses were divided by 100 before they were put on the graph.
755 }
756 }
757 else
759
760 replot();
761 }
762}
763
764void GuideDriftGraph::mouseClicked(QMouseEvent *event)
765{
766 if (event->buttons() & Qt::RightButton)
767 {
768 yAxis->setRange(-3, 3);
769 }
770}
771
772void GuideDriftGraph::refreshColorScheme()
773{
774 if (graph(GuideGraph::G_RA) && graph(GuideGraph::G_DEC) && graph(GuideGraph::G_RA_HIGHLIGHT)
775 && graph(GuideGraph::G_DEC_HIGHLIGHT) && graph(GuideGraph::G_RA_PULSE)
776 && graph(GuideGraph::G_DEC_PULSE))
777 {
778 graph(GuideGraph::G_RA)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError")));
779 graph(GuideGraph::G_DEC)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError")));
780 graph(GuideGraph::G_RA_HIGHLIGHT)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError")));
781 graph(GuideGraph::G_RA_HIGHLIGHT)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssPlusCircle,
782 QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"), 2), QBrush(), 10));
783 graph(GuideGraph::G_DEC_HIGHLIGHT)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError")));
784 graph(GuideGraph::G_DEC_HIGHLIGHT)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssPlusCircle,
785 QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"), 2), QBrush(), 10));
786
787 QColor raPulseColor(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"));
788 raPulseColor.setAlpha(75);
789 graph(GuideGraph::G_RA_PULSE)->setPen(QPen(raPulseColor));
790 graph(GuideGraph::G_RA_PULSE)->setBrush(QBrush(raPulseColor, Qt::Dense4Pattern));
791
792 QColor dePulseColor(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"));
793 dePulseColor.setAlpha(75);
794 graph(GuideGraph::G_DEC_PULSE)->setPen(QPen(dePulseColor));
795 graph(GuideGraph::G_DEC_PULSE)->setBrush(QBrush(dePulseColor, Qt::Dense4Pattern));
796 }
797}
Interface skeleton for implementation of different guiding applications and/or routines.
virtual QPointF dataPixelPosition(int index) const override
virtual int findBegin(double sortKey, bool expandedRange=true) const override
virtual int dataCount() const override
virtual double dataMainKey(int index) const override
virtual double dataMainValue(int index) const override
void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const
void setBrush(const QBrush &brush)
void setPen(const QPen &pen)
void setName(const QString &name)
QCPAxis * addAxis(QCPAxis::AxisType type, QCPAxis *axis=nullptr)
void setRangeZoom(Qt::Orientations orientations)
QCPLayoutInset * insetLayout() const
void rangeChanged(const QCPRange &newRange)
@ atRight
0x02 Axis is vertical and on the right side of the axis rect
@ atLeft
0x01 Axis is vertical and on the left side of the axis rect
void setScatterStyle(const QCPScatterStyle &style)
QSharedPointer< QCPGraphDataContainer > data() const
void setLineStyle(LineStyle ls)
@ lsLine
data points are connected by a straight line
@ lsStepLeft
line is drawn as steps where the step height is the value of the left data point
@ lsNone
data points are not connected with any lines (e.g.
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
void setType(PositionType type)
void setCoords(double key, double value)
@ ptViewportRatio
Static positioning given by a fraction of the viewport size.
void setText(const QString &text)
void setFont(const QFont &font)
void setColor(const QColor &color)
void setVisible(bool on)
@ foColumnsFirst
Columns are filled first, and a new element is wrapped to the next row if the column count would exce...
void setInsetAlignment(int index, Qt::Alignment alignment)
@ ssPlusCircle
\enumimage{ssPlusCircle.png} a circle with a plus inside
void setBackground(const QPixmap &pm)
QCPGraph * addGraph(QCPAxis *keyAxis=nullptr, QCPAxis *valueAxis=nullptr)
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
PlottableType * plottableAt(const QPointF &pos, bool onlySelectable=false, int *dataIndex=nullptr) const
void setInteractions(const QCP::Interactions &interactions)
QCPLegend * legend
QCPAxis * xAxis
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint)
QCPGraph * graph() const
QCPAxisRect * axisRect(int index=0) const
QCPAxis * yAxis2
QCPAxis * yAxis
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QString path(const QString &relativePath)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
KGuiItem overwrite()
const QList< QKeySequence > & begin()
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
bool exists() const const
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool event(QEvent *e)
QObject * parent() const const
T qobject_cast(QObject *object)
QPoint toPoint() const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString number(double n, char format, int precision)
AlignRight
Dense4Pattern
RightButton
QTextStream & endl(QTextStream &stream)
QTime addSecs(int s) const const
QTime currentTime()
QString toString(QStringView format) const const
void hideText()
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
QString fileName(ComponentFormattingOptions options) const const
bool isEmpty() const const
bool isValid() const const
void setPath(const QString &path, ParsingMode mode)
QString toLocalFile() const const
QString url(FormattingOptions options) const const
void lower()
QPoint mapToGlobal(const QPoint &pos) const const
bool isVisible() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 28 2025 11:57:24 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.