Kstars

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

KDE's Doxygen guidelines are available online.