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