File indexing completed on 2024-04-28 15:09:53

0001 /*
0002     SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003     SPDX-FileCopyrightText: 2021 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "guidedriftgraph.h"
0009 #include "klocalizedstring.h"
0010 #include "ksnotification.h"
0011 #include "kstarsdata.h"
0012 #include "guideinterface.h"
0013 #include "Options.h"
0014 
0015 // Qt version calming
0016 #include <qtendl.h>
0017 
0018 GuideDriftGraph::GuideDriftGraph(QWidget *parent)
0019 {
0020     Q_UNUSED(parent);
0021     // Drift Graph Color Settings
0022     setBackground(QBrush(Qt::black));
0023     xAxis->setBasePen(QPen(Qt::white, 1));
0024     yAxis->setBasePen(QPen(Qt::white, 1));
0025     xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
0026     yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
0027     xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
0028     yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
0029     xAxis->grid()->setZeroLinePen(Qt::NoPen);
0030     yAxis->grid()->setZeroLinePen(QPen(Qt::white, 1));
0031     xAxis->setBasePen(QPen(Qt::white, 1));
0032     yAxis->setBasePen(QPen(Qt::white, 1));
0033     yAxis2->setBasePen(QPen(Qt::white, 1));
0034     xAxis->setTickPen(QPen(Qt::white, 1));
0035     yAxis->setTickPen(QPen(Qt::white, 1));
0036     yAxis2->setTickPen(QPen(Qt::white, 1));
0037     xAxis->setSubTickPen(QPen(Qt::white, 1));
0038     yAxis->setSubTickPen(QPen(Qt::white, 1));
0039     yAxis2->setSubTickPen(QPen(Qt::white, 1));
0040     xAxis->setTickLabelColor(Qt::white);
0041     yAxis->setTickLabelColor(Qt::white);
0042     yAxis2->setTickLabelColor(Qt::white);
0043     xAxis->setLabelColor(Qt::white);
0044     yAxis->setLabelColor(Qt::white);
0045     yAxis2->setLabelColor(Qt::white);
0046 
0047     snrAxis = axisRect()->addAxis(QCPAxis::atLeft, 0);
0048     snrAxis->setVisible(false);
0049     // This will be reset to the actual data values.
0050     snrAxis->setRange(-100, 100);
0051 
0052     //Horizontal Axis Time Ticker Settings
0053     QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime);
0054     timeTicker->setTimeFormat("%m:%s");
0055     xAxis->setTicker(timeTicker);
0056 
0057     // Axis Labels Settings
0058     yAxis2->setVisible(true);
0059     yAxis2->setTickLabels(true);
0060     yAxis->setLabelFont(QFont(font().family(), 10));
0061     yAxis2->setLabelFont(QFont(font().family(), 10));
0062     xAxis->setTickLabelFont(QFont(font().family(), 9));
0063     yAxis->setTickLabelFont(QFont(font().family(), 9));
0064     yAxis2->setTickLabelFont(QFont(font().family(), 9));
0065     yAxis->setLabelPadding(1);
0066     yAxis2->setLabelPadding(1);
0067     yAxis->setLabel(i18n("drift (arcsec)"));
0068     yAxis2->setLabel(i18n("pulse (ms)"));
0069 
0070     setupNSEWLabels();
0071 
0072     int scale =
0073         50;  //This is a scaling value between the left and the right axes of the driftGraph, it could be stored in kstars kcfg
0074 
0075     //Sets the default ranges
0076     xAxis->setRange(0, 120, Qt::AlignRight);
0077     yAxis->setRange(-3, 3);
0078     yAxis2->setRange(-3 * scale, 3 * scale);
0079 
0080     //This sets up the legend
0081     legend->setVisible(true);
0082     legend->setFont(QFont(font().family(), 7));
0083     legend->setTextColor(Qt::white);
0084     legend->setBrush(QBrush(Qt::black));
0085     legend->setIconSize(4, 12);
0086     legend->setFillOrder(QCPLegend::foColumnsFirst);
0087     axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignLeft | Qt::AlignBottom);
0088 
0089     // RA Curve
0090     addGraph(xAxis, yAxis);
0091     graph(GuideGraph::G_RA)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError")));
0092     graph(GuideGraph::G_RA)->setName("RA");
0093     graph(GuideGraph::G_RA)->setLineStyle(QCPGraph::lsLine);
0094 
0095     // DE Curve
0096     addGraph(xAxis, yAxis);
0097     graph(GuideGraph::G_DEC)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError")));
0098     graph(GuideGraph::G_DEC)->setName("DE");
0099     graph(GuideGraph::G_DEC)->setLineStyle(QCPGraph::lsLine);
0100 
0101     // RA highlighted Point
0102     addGraph(xAxis, yAxis);
0103     graph(GuideGraph::G_RA_HIGHLIGHT)->setLineStyle(QCPGraph::lsNone);
0104     graph(GuideGraph::G_RA_HIGHLIGHT)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError")));
0105     graph(GuideGraph::G_RA_HIGHLIGHT)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssPlusCircle,
0106             QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"), 2), QBrush(), 10));
0107 
0108     // DE highlighted Point
0109     addGraph(xAxis, yAxis);
0110     graph(GuideGraph::G_DEC_HIGHLIGHT)->setLineStyle(QCPGraph::lsNone);
0111     graph(GuideGraph::G_DEC_HIGHLIGHT)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError")));
0112     graph(GuideGraph::G_DEC_HIGHLIGHT)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssPlusCircle,
0113             QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"), 2), QBrush(), 10));
0114 
0115     // RA Pulse
0116     addGraph(xAxis, yAxis2);
0117     QColor raPulseColor(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"));
0118     raPulseColor.setAlpha(75);
0119     graph(GuideGraph::G_RA_PULSE)->setPen(QPen(raPulseColor));
0120     graph(GuideGraph::G_RA_PULSE)->setBrush(QBrush(raPulseColor, Qt::Dense4Pattern));
0121     graph(GuideGraph::G_RA_PULSE)->setName("RA Pulse");
0122     graph(GuideGraph::G_RA_PULSE)->setLineStyle(QCPGraph::lsStepLeft);
0123 
0124     // DEC Pulse
0125     addGraph(xAxis, yAxis2);
0126     QColor dePulseColor(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"));
0127     dePulseColor.setAlpha(75);
0128     graph(GuideGraph::G_DEC_PULSE)->setPen(QPen(dePulseColor));
0129     graph(GuideGraph::G_DEC_PULSE)->setBrush(QBrush(dePulseColor, Qt::Dense4Pattern));
0130     graph(GuideGraph::G_DEC_PULSE)->setName("DEC Pulse");
0131     graph(GuideGraph::G_DEC_PULSE)->setLineStyle(QCPGraph::lsStepLeft);
0132 
0133     // SNR
0134     addGraph(xAxis, snrAxis);
0135     graph(GuideGraph::G_SNR)->setPen(QPen(Qt::yellow));
0136     graph(GuideGraph::G_SNR)->setName("SNR");
0137     graph(GuideGraph::G_SNR)->setLineStyle(QCPGraph::lsLine);
0138 
0139     // RA RMS
0140     addGraph(xAxis, yAxis);
0141     graph(GuideGraph::G_RA_RMS)->setPen(QPen(Qt::red));
0142     graph(GuideGraph::G_RA_RMS)->setName("RA RMS");
0143     graph(GuideGraph::G_RA_RMS)->setLineStyle(QCPGraph::lsLine);
0144 
0145     // DEC RMS
0146     addGraph(xAxis, yAxis);
0147     graph(GuideGraph::G_DEC_RMS)->setPen(QPen(Qt::red));
0148     graph(GuideGraph::G_DEC_RMS)->setName("DEC RMS");
0149     graph(GuideGraph::G_DEC_RMS)->setLineStyle(QCPGraph::lsLine);
0150 
0151     // Total RMS
0152     addGraph(xAxis, yAxis);
0153     graph(GuideGraph::G_RMS)->setPen(QPen(Qt::red));
0154     graph(GuideGraph::G_RMS)->setName("RMS");
0155     graph(GuideGraph::G_RMS)->setLineStyle(QCPGraph::lsLine);
0156 
0157     //This will prevent the highlighted points and Pulses from showing up in the legend.
0158     legend->removeItem(GuideGraph::G_DEC_RMS);
0159     legend->removeItem(GuideGraph::G_RA_RMS);
0160     legend->removeItem(GuideGraph::G_DEC_PULSE);
0161     legend->removeItem(GuideGraph::G_RA_PULSE);
0162     legend->removeItem(GuideGraph::G_DEC_HIGHLIGHT);
0163     legend->removeItem(GuideGraph::G_RA_HIGHLIGHT);
0164 
0165     setInteractions(QCP::iRangeZoom);
0166     axisRect()->setRangeZoom(Qt::Orientation::Vertical);
0167     setInteraction(QCP::iRangeDrag, true);
0168     //This sets the visibility of graph components to the stored values.
0169     graph(GuideGraph::G_RA)->setVisible(Options::rADisplayedOnGuideGraph()); //RA data
0170     graph(GuideGraph::G_DEC)->setVisible(Options::dEDisplayedOnGuideGraph()); //DEC data
0171     graph(GuideGraph::G_RA_HIGHLIGHT)->setVisible(Options::rADisplayedOnGuideGraph()); //RA highlighted point
0172     graph(GuideGraph::G_DEC_HIGHLIGHT)->setVisible(Options::dEDisplayedOnGuideGraph()); //DEC highlighted point
0173     graph(GuideGraph::G_RA_PULSE)->setVisible(Options::rACorrDisplayedOnGuideGraph()); //RA Pulses
0174     graph(GuideGraph::G_DEC_PULSE)->setVisible(Options::dECorrDisplayedOnGuideGraph()); //DEC Pulses
0175     graph(GuideGraph::G_SNR)->setVisible(Options::sNRDisplayedOnGuideGraph()); //SNR
0176     setRMSVisibility();
0177 
0178     updateCorrectionsScaleVisibility();
0179 }
0180 
0181 void GuideDriftGraph::guideHistory(int sliderValue, bool graphOnLatestPt)
0182 {
0183     graph(GuideGraph::G_RA_HIGHLIGHT)->data()->clear(); //Clear RA highlighted point
0184     graph(GuideGraph::G_DEC_HIGHLIGHT)->data()->clear(); //Clear DEC highlighted point
0185     double t = graph(GuideGraph::G_RA)->dataMainKey(sliderValue); //Get time from RA data
0186     double ra = graph(GuideGraph::G_RA)->dataMainValue(sliderValue); //Get RA from RA data
0187     double de = graph(GuideGraph::G_DEC)->dataMainValue(sliderValue); //Get DEC from DEC data
0188     double raPulse = graph(GuideGraph::G_RA_PULSE)->dataMainValue(sliderValue); //Get RA Pulse from RA pulse data
0189     double dePulse = graph(GuideGraph::G_DEC_PULSE)->dataMainValue(sliderValue); //Get DEC Pulse from DEC pulse data
0190     graph(GuideGraph::G_RA_HIGHLIGHT)->addData(t, ra); //Set RA highlighted point
0191     graph(GuideGraph::G_DEC_HIGHLIGHT)->addData(t, de); //Set DEC highlighted point
0192 
0193     //This will allow the graph to scroll left and right along with the guide slider
0194     if (xAxis->range().contains(t) == false)
0195     {
0196         if(t < xAxis->range().lower)
0197         {
0198             xAxis->setRange(t, t + xAxis->range().size());
0199         }
0200         if(t > xAxis->range().upper)
0201         {
0202             xAxis->setRange(t - xAxis->range().size(), t);
0203         }
0204     }
0205     replot();
0206     double snr = 0;
0207     if (graph(GuideGraph::G_SNR)->data()->size() > 0)
0208         snr = graph(GuideGraph::G_SNR)->dataMainValue(sliderValue);
0209     double rms = graph(GuideGraph::G_RMS)->dataMainValue(sliderValue);
0210 
0211     if(!graphOnLatestPt)
0212     {
0213         QTime localTime = guideTimer;
0214         localTime = localTime.addSecs(t);
0215 
0216         QPoint localTooltipCoordinates = graph(GuideGraph::G_RA)->dataPixelPosition(sliderValue).toPoint();
0217         QPoint globalTooltipCoordinates = mapToGlobal(localTooltipCoordinates);
0218 
0219         if(raPulse == 0 && dePulse == 0)
0220         {
0221             QToolTip::showText(
0222                 globalTooltipCoordinates,
0223                 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",
0224                       "<table>"
0225                       "<tr><td>LT:   </td><td>%1</td></tr>"
0226                       "<tr><td>RA:   </td><td>%2 \"</td></tr>"
0227                       "<tr><td>DE:   </td><td>%3 \"</td></tr>"
0228                       "<tr><td>RMS:   </td><td>%4 \"</td></tr>"
0229                       "<tr><td>SNR:   </td><td>%5 \"</td></tr>"
0230                       "</table>",
0231                       localTime.toString("hh:mm:ss AP"),
0232                       QString::number(ra, 'f', 2),
0233                       QString::number(de, 'f', 2),
0234                       QString::number(rms, 'f', 2),
0235                       QString::number(snr, 'f', 1)
0236                      ));
0237         }
0238         else
0239         {
0240             QToolTip::showText(
0241                 globalTooltipCoordinates,
0242                 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",
0243                       "<table>"
0244                       "<tr><td>LT:   </td><td>%1</td></tr>"
0245                       "<tr><td>RA:   </td><td>%2 \"</td></tr>"
0246                       "<tr><td>DE:   </td><td>%3 \"</td></tr>"
0247                       "<tr><td>RMS:   </td><td>%4 \"</td></tr>"
0248                       "<tr><td>SNR:   </td><td>%5 \"</td></tr>"
0249                       "<tr><td>RA Pulse:   </td><td>%6 ms</td></tr>"
0250                       "<tr><td>DE Pulse:   </td><td>%7 ms</td></tr>"
0251                       "</table>",
0252                       localTime.toString("hh:mm:ss AP"),
0253                       QString::number(ra, 'f', 2),
0254                       QString::number(de, 'f', 2),
0255                       QString::number(rms, 'f', 2),
0256                       QString::number(snr, 'f', 1),
0257                       QString::number(raPulse, 'f', 2),
0258                       QString::number(dePulse, 'f', 2)
0259                      )); //The pulses were divided by 100 before they were put on the graph.
0260         }
0261 
0262     }
0263 }
0264 
0265 void GuideDriftGraph::handleVerticalPlotSizeChange()
0266 {
0267 }
0268 
0269 void GuideDriftGraph::handleHorizontalPlotSizeChange()
0270 {
0271 }
0272 
0273 void GuideDriftGraph::setupNSEWLabels()
0274 {
0275     //Labels for N/S/E/W
0276     QColor raLabelColor(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"));
0277     QColor deLabelColor(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"));
0278 
0279     QCPItemText *northLabel = new QCPItemText(this);
0280     northLabel->setColor(deLabelColor);
0281     northLabel->setFont(QFont(font().family(), 9));
0282     northLabel->setText(i18nc("North", "N"));
0283     northLabel->position->setType(QCPItemPosition::ptViewportRatio);
0284     northLabel->position->setCoords(0.7, 0.12);
0285     northLabel->setVisible(true);
0286 
0287     QCPItemText *southLabel = new QCPItemText(this);
0288     southLabel->setColor(deLabelColor);
0289     southLabel->setFont(QFont(font().family(), 9));
0290     southLabel->setText(i18nc("South", "S"));
0291     southLabel->position->setType(QCPItemPosition::ptViewportRatio);
0292     southLabel->position->setCoords(0.7, 0.8);
0293     southLabel->setVisible(true);
0294 
0295     QCPItemText *westLabel = new QCPItemText(this);
0296     westLabel->setColor(raLabelColor);
0297     westLabel->setFont(QFont(font().family(), 9));
0298     westLabel->setText(i18nc("West", "W"));
0299     westLabel->position->setType(QCPItemPosition::ptViewportRatio);
0300     westLabel->position->setCoords(0.78, 0.12);
0301     westLabel->setVisible(true);
0302 
0303     QCPItemText *eastLabel = new QCPItemText(this);
0304     eastLabel->setColor(raLabelColor);
0305     eastLabel->setFont(QFont(font().family(), 9));
0306     eastLabel->setText(i18nc("East", "E"));
0307     eastLabel->position->setType(QCPItemPosition::ptViewportRatio);
0308     eastLabel->position->setCoords(0.8, 0.8);
0309     eastLabel->setVisible(true);
0310 
0311 }
0312 
0313 void GuideDriftGraph::autoScaleGraphs()
0314 {
0315     yAxis->setRange(-3, 3);
0316     // First bool below is only_enlarge, 2nd is only look at values that are visible in X.
0317     // Net result is all RA & DEC points within the times being plotted should be visible.
0318     // This is only called when the autoScale button is pressed.
0319     graph(GuideGraph::G_RA)->rescaleValueAxis(false, true);
0320     graph(GuideGraph::G_DEC)->rescaleValueAxis(true, true);
0321     replot();
0322 }
0323 
0324 void GuideDriftGraph::zoomX(int zoomLevel)
0325 {
0326     double key = (guideElapsedTimer.isValid() || guideTimer.isValid()
0327                   || guideTimer.isNull()) ? 0 : guideElapsedTimer.elapsed() / 1000.0;
0328 
0329     // The # of seconds displayd on the x-axis of the drift-graph for the various zoom levels.
0330     static std::vector<int> zoomLevels = {15, 30, 60, 120, 300, 900, 1800, 3600, 7200, 14400};
0331 
0332     zoomLevel = std::max(0, zoomLevel);
0333     driftGraphZoomLevel = std::min(static_cast<int>(zoomLevels.size() - 1), zoomLevel);
0334 
0335     xAxis->setRange(key - zoomLevels[driftGraphZoomLevel], key);
0336 }
0337 
0338 void GuideDriftGraph::zoomInX()
0339 {
0340     zoomX(driftGraphZoomLevel - 1);
0341     replot();
0342 }
0343 
0344 void GuideDriftGraph::zoomOutX()
0345 {
0346     zoomX(driftGraphZoomLevel + 1);
0347     replot();
0348 }
0349 
0350 void GuideDriftGraph::setCorrectionGraphScale(int value)
0351 {
0352     yAxis2->setRange(yAxis->range().lower * value,
0353                      yAxis->range().upper * value);
0354     replot();
0355 }
0356 
0357 void GuideDriftGraph::clear()
0358 {
0359     graph(GuideGraph::G_RA)->data()->clear(); //RA data
0360     graph(GuideGraph::G_DEC)->data()->clear(); //DEC data
0361     graph(GuideGraph::G_RA_HIGHLIGHT)->data()->clear(); //RA highlighted point
0362     graph(GuideGraph::G_DEC_HIGHLIGHT)->data()->clear(); //DEC highlighted point
0363     graph(GuideGraph::G_RA_PULSE)->data()->clear(); //RA Pulses
0364     graph(GuideGraph::G_DEC_PULSE)->data()->clear(); //DEC Pulses
0365     graph(GuideGraph::G_SNR)->data()->clear(); //SNR
0366     graph(GuideGraph::G_RA_RMS)->data()->clear(); //RA RMS
0367     graph(GuideGraph::G_DEC_RMS)->data()->clear(); //DEC RMS
0368     graph(GuideGraph::G_RMS)->data()->clear(); //RMS
0369     clearItems();  //Clears dither text items from the graph
0370     setupNSEWLabels();
0371     replot();
0372 }
0373 
0374 void GuideDriftGraph::toggleShowPlot(GuideGraph::DRIFT_GRAPH_INDICES plot, bool isChecked)
0375 {
0376     switch (plot)
0377     {
0378         case GuideGraph::G_RA:
0379             Options::setRADisplayedOnGuideGraph(isChecked);
0380             graph(GuideGraph::G_RA)->setVisible(isChecked);
0381             graph(GuideGraph::G_RA_HIGHLIGHT)->setVisible(isChecked);
0382             setRMSVisibility();
0383             replot();
0384             break;
0385         case GuideGraph::G_DEC:
0386             Options::setDEDisplayedOnGuideGraph(isChecked);
0387             graph(GuideGraph::G_DEC)->setVisible(isChecked);
0388             graph(GuideGraph::G_DEC_HIGHLIGHT)->setVisible(isChecked);
0389             setRMSVisibility();
0390             replot();
0391             break;
0392         case GuideGraph::G_RA_PULSE:
0393             Options::setRACorrDisplayedOnGuideGraph(isChecked);
0394             graph(GuideGraph::G_RA_PULSE)->setVisible(isChecked);
0395             updateCorrectionsScaleVisibility();
0396             break;
0397         case GuideGraph::G_DEC_PULSE:
0398             Options::setDECorrDisplayedOnGuideGraph(isChecked);
0399             graph(GuideGraph::G_DEC_PULSE)->setVisible(isChecked);
0400             updateCorrectionsScaleVisibility();
0401             break;
0402         case GuideGraph::G_SNR:
0403             Options::setSNRDisplayedOnGuideGraph(isChecked);
0404             graph(GuideGraph::G_SNR)->setVisible(isChecked);
0405             replot();
0406             break;
0407         case GuideGraph::G_RMS:
0408             Options::setRMSDisplayedOnGuideGraph(isChecked);
0409             setRMSVisibility();
0410             replot();
0411             break;
0412         default:
0413             break;
0414     }
0415 }
0416 
0417 void GuideDriftGraph::setRMSVisibility()
0418 {
0419     if (!Options::rMSDisplayedOnGuideGraph())
0420     {
0421         graph(GuideGraph::G_RA_RMS)->setVisible(false);
0422         graph(GuideGraph::G_DEC_RMS)->setVisible(false);
0423         graph(GuideGraph::G_RMS)->setVisible(false);
0424         return;
0425     }
0426 
0427     if ((Options::dEDisplayedOnGuideGraph() && Options::rADisplayedOnGuideGraph()) ||
0428             (!Options::dEDisplayedOnGuideGraph() && !Options::rADisplayedOnGuideGraph()))
0429     {
0430         graph(GuideGraph::G_RA_RMS)->setVisible(false);
0431         graph(GuideGraph::G_DEC_RMS)->setVisible(false);
0432         graph(GuideGraph::G_RMS)->setVisible(true);
0433     }
0434     else if (!Options::dEDisplayedOnGuideGraph() && Options::rADisplayedOnGuideGraph())
0435     {
0436         graph(GuideGraph::G_RA_RMS)->setVisible(true);
0437         graph(GuideGraph::G_DEC_RMS)->setVisible(false);
0438         graph(GuideGraph::G_RMS)->setVisible(false);
0439     }
0440     else
0441     {
0442         graph(GuideGraph::G_RA_RMS)->setVisible(false);
0443         graph(GuideGraph::G_DEC_RMS)->setVisible(true);
0444         graph(GuideGraph::G_RMS)->setVisible(false);
0445     }
0446 }
0447 
0448 void GuideDriftGraph::exportGuideData()
0449 {
0450     int numPoints = graph(GuideGraph::G_RA)->dataCount();
0451     if (numPoints == 0)
0452         return;
0453 
0454     QUrl exportFile = QFileDialog::getSaveFileUrl(this, i18nc("@title:window", "Export Guide Data"), guideURLPath,
0455                       "CSV File (*.csv)");
0456     if (exportFile.isEmpty()) // if user presses cancel
0457         return;
0458     if (exportFile.toLocalFile().endsWith(QLatin1String(".csv")) == false)
0459         exportFile.setPath(exportFile.toLocalFile() + ".csv");
0460 
0461     QString path = exportFile.toLocalFile();
0462 
0463     if (QFile::exists(path))
0464     {
0465         int r = KMessageBox::warningContinueCancel(nullptr,
0466                 i18n("A file named \"%1\" already exists. "
0467                      "Overwrite it?",
0468                      exportFile.fileName()),
0469                 i18n("Overwrite File?"), KStandardGuiItem::overwrite());
0470         if (r == KMessageBox::Cancel)
0471             return;
0472     }
0473 
0474     if (!exportFile.isValid())
0475     {
0476         QString message = i18n("Invalid URL: %1", exportFile.url());
0477         KSNotification::sorry(message, i18n("Invalid URL"));
0478         return;
0479     }
0480 
0481     QFile file;
0482     file.setFileName(path);
0483     if (!file.open(QIODevice::WriteOnly))
0484     {
0485         QString message = i18n("Unable to write to file %1", path);
0486         KSNotification::sorry(message, i18n("Could Not Open File"));
0487         return;
0488     }
0489 
0490     QTextStream outstream(&file);
0491 
0492     outstream <<
0493               "Frame #, Time Elapsed (sec), Local Time (HMS), RA Error (arcsec), DE Error (arcsec), RA Pulse  (ms), DE Pulse (ms)" <<
0494               Qt::endl;
0495 
0496     for (int i = 0; i < numPoints; i++)
0497     {
0498         double t = graph(GuideGraph::G_RA)->dataMainKey(i);
0499         double ra = graph(GuideGraph::G_RA)->dataMainValue(i);
0500         double de = graph(GuideGraph::G_DEC)->dataMainValue(i);
0501         double raPulse = graph(GuideGraph::G_RA_PULSE)->dataMainValue(i);
0502         double dePulse = graph(GuideGraph::G_DEC_PULSE)->dataMainValue(i);
0503 
0504         QTime localTime = guideTimer;
0505         localTime = localTime.addSecs(t);
0506 
0507         outstream << i << ',' << t << ',' << localTime.toString("hh:mm:ss AP") << ',' << ra << ',' << de << ',' << raPulse << ',' <<
0508                   dePulse << ',' << Qt::endl;
0509     }
0510     file.close();
0511 }
0512 
0513 void GuideDriftGraph::resetTimer()
0514 {
0515     guideTimer = QTime::currentTime();
0516     guideElapsedTimer.start();
0517 }
0518 
0519 void GuideDriftGraph::connectGuider(Ekos::GuideInterface *guider)
0520 {
0521     connect(guider, &Ekos::GuideInterface::newAxisDelta, this, &GuideDriftGraph::setAxisDelta);
0522     connect(guider, &Ekos::GuideInterface::newAxisPulse, this, &GuideDriftGraph::setAxisPulse);
0523     connect(guider, &Ekos::GuideInterface::newAxisSigma, this, &GuideDriftGraph::setAxisSigma);
0524     connect(guider, &Ekos::GuideInterface::newSNR, this, &GuideDriftGraph::setSNR);
0525 
0526     resetTimer();
0527 }
0528 
0529 void GuideDriftGraph::setAxisDelta(double ra, double de)
0530 {
0531     // Time since timer started.
0532     double key = guideElapsedTimer.elapsed() / 1000.0;
0533 
0534     // similar to same operation in Guide::setAxisDelta
0535     ra = -ra;
0536 
0537     graph(GuideGraph::G_RA)->addData(key, ra);
0538     graph(GuideGraph::G_DEC)->addData(key, de);
0539 
0540     if(graphOnLatestPt)
0541     {
0542         xAxis->setRange(key, xAxis->range().size(), Qt::AlignRight);
0543         graph(GuideGraph::G_RA_HIGHLIGHT)->data()->clear(); //Clear highlighted RA point
0544         graph(GuideGraph::G_DEC_HIGHLIGHT)->data()->clear(); //Clear highlighted DEC point
0545         graph(GuideGraph::G_RA_HIGHLIGHT)->addData(key, ra); //Set highlighted RA point to latest point
0546         graph(GuideGraph::G_DEC_HIGHLIGHT)->addData(key, de); //Set highlighted DEC point to latest point
0547     }
0548     replot();
0549 }
0550 
0551 void GuideDriftGraph::setAxisSigma(double ra, double de)
0552 {
0553     const double key = guideElapsedTimer.elapsed() / 1000.0;
0554     const double total = std::hypot(ra, de);
0555     graph(GuideGraph::G_RA_RMS)->addData(key, ra);
0556     graph(GuideGraph::G_DEC_RMS)->addData(key, de);
0557     graph(GuideGraph::G_RMS)->addData(key, total);
0558 }
0559 
0560 void GuideDriftGraph::setAxisPulse(double ra, double de)
0561 {
0562     double key = guideElapsedTimer.elapsed() / 1000.0;
0563     graph(GuideGraph::G_RA_PULSE)->addData(key, ra);
0564     graph(GuideGraph::G_DEC_PULSE)->addData(key, de);
0565 }
0566 
0567 void GuideDriftGraph::setSNR(double snr)
0568 {
0569     double key = guideElapsedTimer.elapsed() / 1000.0;
0570     graph(GuideGraph::G_SNR)->addData(key, snr);
0571 
0572     // Sets the SNR axis to have the maximum be 95% of the way up from the middle to the top.
0573     QCPGraphData snrMax = *std::min_element(graph(GuideGraph::G_SNR)->data()->begin(),
0574                                             graph(GuideGraph::G_SNR)->data()->end(),
0575                                             [](QCPGraphData const & s1, QCPGraphData const & s2)
0576     {
0577         return s1.value > s2.value;
0578     });
0579     snrAxis->setRange(-1.05 * snrMax.value, 1.05 * snrMax.value);
0580 }
0581 
0582 void GuideDriftGraph::updateCorrectionsScaleVisibility()
0583 {
0584     bool isVisible = (Options::rACorrDisplayedOnGuideGraph() || Options::dECorrDisplayedOnGuideGraph());
0585     yAxis2->setVisible(isVisible);
0586     replot();
0587 }
0588 
0589 void GuideDriftGraph::mouseOverLine(QMouseEvent *event)
0590 {
0591     double key = xAxis->pixelToCoord(event->localPos().x());
0592 
0593     if (xAxis->range().contains(key))
0594     {
0595         if (plottableAt(event->pos(), false))
0596         {
0597             int raIndex = graph(GuideGraph::G_RA)->findBegin(key);
0598             int deIndex = graph(GuideGraph::G_DEC)->findBegin(key);
0599             int rmsIndex = graph(GuideGraph::G_RMS)->findBegin(key);
0600 
0601             double raDelta = graph(GuideGraph::G_RA)->dataMainValue(raIndex);
0602             double deDelta = graph(GuideGraph::G_DEC)->dataMainValue(deIndex);
0603 
0604             double raPulse = graph(GuideGraph::G_RA_PULSE)->dataMainValue(raIndex); //Get RA Pulse from RA pulse data
0605             double dePulse = graph(GuideGraph::G_DEC_PULSE)->dataMainValue(deIndex); //Get DEC Pulse from DEC pulse data
0606 
0607             double rms = graph(GuideGraph::G_RMS)->dataMainValue(rmsIndex);
0608             double snr = 0;
0609             if (graph(GuideGraph::G_SNR)->data()->size() > 0)
0610             {
0611                 int snrIndex = graph(GuideGraph::G_SNR)->findBegin(key);
0612                 snr = graph(GuideGraph::G_SNR)->dataMainValue(snrIndex);
0613             }
0614 
0615             // Compute time value:
0616             QTime localTime = guideTimer;
0617 
0618             localTime = localTime.addSecs(key);
0619 
0620             QToolTip::hideText();
0621             if(raPulse == 0 && dePulse == 0)
0622             {
0623                 QToolTip::showText(
0624                     event->globalPos(),
0625                     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",
0626                           "<table>"
0627                           "<tr><td>LT:   </td><td>%1</td></tr>"
0628                           "<tr><td>RA:   </td><td>%2 \"</td></tr>"
0629                           "<tr><td>DE:   </td><td>%3 \"</td></tr>"
0630                           "<tr><td>RMS:   </td><td>%4 \"</td></tr>"
0631                           "<tr><td>SNR:   </td><td>%5 \"</td></tr>"
0632                           "</table>",
0633                           localTime.toString("hh:mm:ss AP"),
0634                           QString::number(raDelta, 'f', 2), QString::number(deDelta, 'f', 2),
0635                           QString::number(rms, 'f', 2), QString::number(snr, 'f', 1)));
0636             }
0637             else
0638             {
0639                 QToolTip::showText(
0640                     event->globalPos(),
0641                     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",
0642                           "<table>"
0643                           "<tr><td>LT:   </td><td>%1</td></tr>"
0644                           "<tr><td>RA:   </td><td>%2 \"</td></tr>"
0645                           "<tr><td>DE:   </td><td>%3 \"</td></tr>"
0646                           "<tr><td>RMS:   </td><td>%4 \"</td></tr>"
0647                           "<tr><td>SNR:   </td><td>%5 \"</td></tr>"
0648                           "<tr><td>RA Pulse:   </td><td>%6 ms</td></tr>"
0649                           "<tr><td>DE Pulse:   </td><td>%7 ms</td></tr>"
0650                           "</table>",
0651                           localTime.toString("hh:mm:ss AP"),
0652                           QString::number(raDelta, 'f', 2),
0653                           QString::number(deDelta, 'f', 2),
0654                           QString::number(rms, 'f', 2),
0655                           QString::number(snr, 'f', 1),
0656                           QString::number(raPulse, 'f', 2),
0657                           QString::number(dePulse, 'f', 2))); //The pulses were divided by 100 before they were put on the graph.
0658             }
0659         }
0660         else
0661             QToolTip::hideText();
0662 
0663         replot();
0664     }
0665 
0666     if (xAxis->range().contains(key))
0667     {
0668         QCPGraph *qcpgraph = qobject_cast<QCPGraph *>(plottableAt(event->pos(), false));
0669 
0670         if (qcpgraph)
0671         {
0672             int raIndex = graph(GuideGraph::G_RA)->findBegin(key);
0673             int deIndex = graph(GuideGraph::G_DEC)->findBegin(key);
0674             int rmsIndex = graph(GuideGraph::G_RMS)->findBegin(key);
0675 
0676             double raDelta = graph(GuideGraph::G_RA)->dataMainValue(raIndex);
0677             double deDelta = graph(GuideGraph::G_DEC)->dataMainValue(deIndex);
0678 
0679             double raPulse = graph(GuideGraph::G_RA_PULSE)->dataMainValue(raIndex); //Get RA Pulse from RA pulse data
0680             double dePulse = graph(GuideGraph::G_DEC_PULSE)->dataMainValue(deIndex); //Get DEC Pulse from DEC pulse data
0681 
0682             double rms = graph(GuideGraph::G_RMS)->dataMainValue(rmsIndex);
0683             double snr = 0;
0684             if (graph(GuideGraph::G_SNR)->data()->size() > 0)
0685             {
0686                 int snrIndex = graph(GuideGraph::G_SNR)->findBegin(key);
0687                 snr = graph(GuideGraph::G_SNR)->dataMainValue(snrIndex);
0688             }
0689 
0690             // Compute time value:
0691             QTime localTime = guideTimer;
0692 
0693             localTime = localTime.addSecs(key);
0694 
0695             QToolTip::hideText();
0696             if(raPulse == 0 && dePulse == 0)
0697             {
0698                 QToolTip::showText(
0699                     event->globalPos(),
0700                     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",
0701                           "<table>"
0702                           "<tr><td>LT:   </td><td>%1</td></tr>"
0703                           "<tr><td>RA:   </td><td>%2 \"</td></tr>"
0704                           "<tr><td>DE:   </td><td>%3 \"</td></tr>"
0705                           "<tr><td>RMS:   </td><td>%4 \"</td></tr>"
0706                           "<tr><td>SNR:   </td><td>%5 \"</td></tr>"
0707                           "</table>",
0708                           localTime.toString("hh:mm:ss AP"),
0709                           QString::number(raDelta, 'f', 2), QString::number(deDelta, 'f', 2),
0710                           QString::number(rms, 'f', 2), QString::number(snr, 'f', 1)));
0711             }
0712             else
0713             {
0714                 QToolTip::showText(
0715                     event->globalPos(),
0716                     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",
0717                           "<table>"
0718                           "<tr><td>LT:   </td><td>%1</td></tr>"
0719                           "<tr><td>RA:   </td><td>%2 \"</td></tr>"
0720                           "<tr><td>DE:   </td><td>%3 \"</td></tr>"
0721                           "<tr><td>RMS:   </td><td>%4 \"</td></tr>"
0722                           "<tr><td>SNR:   </td><td>%5 \"</td></tr>"
0723                           "<tr><td>RA Pulse:   </td><td>%6 ms</td></tr>"
0724                           "<tr><td>DE Pulse:   </td><td>%7 ms</td></tr>"
0725                           "</table>",
0726                           localTime.toString("hh:mm:ss AP"),
0727                           QString::number(raDelta, 'f', 2),
0728                           QString::number(deDelta, 'f', 2),
0729                           QString::number(rms, 'f', 2),
0730                           QString::number(snr, 'f', 1),
0731                           QString::number(raPulse, 'f', 2),
0732                           QString::number(dePulse, 'f', 2))); //The pulses were divided by 100 before they were put on the graph.
0733             }
0734         }
0735         else
0736             QToolTip::hideText();
0737 
0738         replot();
0739     }
0740 }
0741 
0742 void GuideDriftGraph::mouseClicked(QMouseEvent *event)
0743 {
0744     if (event->buttons() & Qt::RightButton)
0745     {
0746         yAxis->setRange(-3, 3);
0747     }
0748 }
0749 
0750 void GuideDriftGraph::refreshColorScheme()
0751 {
0752     if (graph(GuideGraph::G_RA) && graph(GuideGraph::G_DEC) && graph(GuideGraph::G_RA_HIGHLIGHT)
0753             && graph(GuideGraph::G_DEC_HIGHLIGHT) && graph(GuideGraph::G_RA_PULSE)
0754             && graph(GuideGraph::G_DEC_PULSE))
0755     {
0756         graph(GuideGraph::G_RA)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError")));
0757         graph(GuideGraph::G_DEC)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError")));
0758         graph(GuideGraph::G_RA_HIGHLIGHT)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError")));
0759         graph(GuideGraph::G_RA_HIGHLIGHT)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssPlusCircle,
0760                 QPen(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"), 2), QBrush(), 10));
0761         graph(GuideGraph::G_DEC_HIGHLIGHT)->setPen(QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError")));
0762         graph(GuideGraph::G_DEC_HIGHLIGHT)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssPlusCircle,
0763                 QPen(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"), 2), QBrush(), 10));
0764 
0765         QColor raPulseColor(KStarsData::Instance()->colorScheme()->colorNamed("RAGuideError"));
0766         raPulseColor.setAlpha(75);
0767         graph(GuideGraph::G_RA_PULSE)->setPen(QPen(raPulseColor));
0768         graph(GuideGraph::G_RA_PULSE)->setBrush(QBrush(raPulseColor, Qt::Dense4Pattern));
0769 
0770         QColor dePulseColor(KStarsData::Instance()->colorScheme()->colorNamed("DEGuideError"));
0771         dePulseColor.setAlpha(75);
0772         graph(GuideGraph::G_DEC_PULSE)->setPen(QPen(dePulseColor));
0773         graph(GuideGraph::G_DEC_PULSE)->setBrush(QBrush(dePulseColor, Qt::Dense4Pattern));
0774     }
0775 }