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

KDE's Doxygen guidelines are available online.