9#include <knotification.h>
15#include "auxiliary/kspaths.h"
17#include "ekos/manager.h"
18#include "ekos/focus/curvefit.h"
19#include "fitsviewer/fitsdata.h"
20#include "fitsviewer/fitsviewer.h"
21#include "ksmessagebox.h"
23#include "kstarsdata.h"
25#include "qcustomplot.h"
27#include <ekos_analyze_debug.h>
29#include <QDesktopServices>
37 void setOffset(
double offset)
41 QString getTickLabel(
double tick,
const QLocale &locale, QChar formatChar,
int precision)
override
49 double timeOffset = 0;
56QString timeFormat =
"yyyy-MM-dd hh:mm:ss.zzz";
59constexpr int MAX_SCROLL_VALUE = 10000;
64constexpr double halfTimelineHeight = 0.35;
69int TEMPERATURE_GRAPH = -1;
70int FOCUS_POSITION_GRAPH = -1;
71int NUM_CAPTURE_STARS_GRAPH = -1;
73int ECCENTRICITY_GRAPH = -1;
74int NUMSTARS_GRAPH = -1;
79int RA_PULSE_GRAPH = -1;
80int DEC_PULSE_GRAPH = -1;
83int CAPTURE_RMS_GRAPH = -1;
84int MOUNT_RA_GRAPH = -1;
85int MOUNT_DEC_GRAPH = -1;
86int MOUNT_HA_GRAPH = -1;
89int PIER_SIDE_GRAPH = -1;
90int TARGET_DISTANCE_GRAPH = -1;
93int ADAPTIVE_FOCUS_GRAPH = -1;
96int FOCUS_GRAPHICS = -1;
97int FOCUS_GRAPHICS_FINAL = -1;
98int FOCUS_GRAPHICS_CURVE = -1;
99int GUIDER_GRAPHICS = -1;
118 return info.exists() && info.isFile();
123const QString mountStatusString(ISD::Mount::Status status)
127 case ISD::Mount::MOUNT_IDLE:
129 case ISD::Mount::MOUNT_PARKED:
130 return i18n(
"Parked");
131 case ISD::Mount::MOUNT_PARKING:
132 return i18n(
"Parking");
133 case ISD::Mount::MOUNT_SLEWING:
134 return i18n(
"Slewing");
135 case ISD::Mount::MOUNT_MOVING:
136 return i18n(
"Moving");
137 case ISD::Mount::MOUNT_TRACKING:
138 return i18n(
"Tracking");
139 case ISD::Mount::MOUNT_ERROR:
140 return i18n(
"Error");
142 return i18n(
"Error");
145ISD::Mount::Status toMountStatus(
const QString &str)
147 if (str ==
i18n(
"Idle"))
148 return ISD::Mount::MOUNT_IDLE;
149 else if (str ==
i18n(
"Parked"))
150 return ISD::Mount::MOUNT_PARKED;
151 else if (str ==
i18n(
"Parking"))
152 return ISD::Mount::MOUNT_PARKING;
153 else if (str ==
i18n(
"Slewing"))
154 return ISD::Mount::MOUNT_SLEWING;
155 else if (str ==
i18n(
"Moving"))
156 return ISD::Mount::MOUNT_MOVING;
157 else if (str ==
i18n(
"Tracking"))
158 return ISD::Mount::MOUNT_TRACKING;
160 return ISD::Mount::MOUNT_ERROR;
169 const QString rPattern(
"^(red|r)$");
175 const QString gPattern(
"^(green|g)$");
181 const QString bPattern(
"^(blue|b)$");
187 const QString hPattern(
"^(ha|h|h-a|h_a|h-alpha|hydrogen|hydrogen_alpha|hydrogen-alpha|h_alpha|halpha)$");
193 const QString oPattern(
"^(oiii|oxygen|oxygen_3|oxygen-3|oxygen_iii|oxygen-iii|o_iii|o-iii|o_3|o-3|o3)$");
200 sPattern(
"^(sii|sulphur|sulphur_2|sulphur-2|sulphur_ii|sulphur-ii|sulfur|sulfur_2|sulfur-2|sulfur_ii|sulfur-ii|s_ii|s-ii|s_2|s-2|s2)$");
207 const QString lPattern(
"^(lpr|L|UV-IR cut|UV-IR|white|monochrome|broadband|clear|focus|luminance|lum|lps|cls)$");
225 const QString &alternateDirectory = Options::analyzeAlternativeDirectoryName();
229 if (info.exists() && info.isFile())
239 int size = filename.
size();
240 int searchBackFrom = size -
name.
size();
242 while (searchBackFrom >= 0)
244 int index = filename.
lastIndexOf(
'/', searchBackFrom);
252 searchBackFrom = index - 1;
271 intervals.append(value);
277 QList<T>
find(
double t)
280 for (
const auto &interval : intervals)
282 if (t >= interval.start && t <= interval.end)
290 double bestStart = 1e7;
292 for (
auto &interval : intervals)
294 if (interval.start > t && interval.start < bestStart)
296 bestStart = interval.start;
303 T *findPrevious(
double t)
305 double bestStart = -1e7;
307 for (
auto &interval : intervals)
309 if (interval.start < t && interval.start > bestStart)
311 bestStart = interval.start;
321IntervalFinder<Ekos::Analyze::CaptureSession> captureSessions;
322IntervalFinder<Ekos::Analyze::FocusSession> focusSessions;
323IntervalFinder<Ekos::Analyze::GuideSession> guideSessions;
324IntervalFinder<Ekos::Analyze::MountSession> mountSessions;
325IntervalFinder<Ekos::Analyze::AlignSession> alignSessions;
326IntervalFinder<Ekos::Analyze::MountFlipSession> mountFlipSessions;
327IntervalFinder<Ekos::Analyze::SchedulerJobSession> schedulerJobSessions;
341 RmsFilter(
int size = 40) : m_WindowSize(size) {}
343 double newSample(
double x,
double y)
345 m_XData.push_back(x);
346 m_YData.push_back(y);
349 m_XSumOfSquares += x * x;
350 m_YSumOfSquares += y * y;
352 if (m_XData.size() > m_WindowSize)
354 double oldValue = m_XData.front();
357 m_XSumOfSquares -= oldValue * oldValue;
359 oldValue = m_YData.front();
362 m_YSumOfSquares -= oldValue * oldValue;
364 const int size = m_XData.size();
368 const double xVariance = (m_XSumOfSquares - (m_XSum * m_XSum) / size) / (size - 1);
369 const double yVariance = (m_YSumOfSquares - (m_YSum * m_YSum) / size) / (size - 1);
370 return std::sqrt(xVariance + yVariance);
384 std::deque<double> m_XData;
385 std::deque<double> m_YData;
387 unsigned long m_WindowSize;
388 double m_XSum = 0, m_YSum = 0;
389 double m_XSumOfSquares = 0, m_YSumOfSquares = 0;
392bool Analyze::eventFilter(QObject *obj, QEvent *ev)
402 auto axisEntry = yAxisMap.find(obj);
403 if (axisEntry == yAxisMap.end())
409 (
static_cast<QMouseEvent*
>(ev)->modifiers() &
410 Qt::KeyboardModifier::ControlModifier);
412 (
static_cast<QMouseEvent*
>(ev)->modifiers() &
413 Qt::KeyboardModifier::ShiftModifier);
417 startYAxisTool(axisEntry->first, axisEntry->second);
423 clickTimer.setSingleShot(
true);
424 clickTimer.setInterval(250);
426 m_ClickTimerInfo = axisEntry->second;
430 m_YAxisTool.reject();
431 if (m_ClickTimerInfo.checkBox && !m_ClickTimerInfo.checkBox->isChecked())
434 m_ClickTimerInfo.checkBox->setChecked(
true);
435 statsPlot->graph(m_ClickTimerInfo.graphIndex)->setVisible(
true);
436 statsPlot->graph(m_ClickTimerInfo.graphIndex)->addToLegend();
438 userSetLeftAxis(m_ClickTimerInfo.axis);
445Analyze::Analyze() : m_YAxisTool(this)
449 captureRms.reset(
new RmsFilter);
450 guiderRms.reset(
new RmsFilter);
452 initInputSelection();
456 connect(&m_YAxisTool, &YAxisTool::axisChanged,
this, &Analyze::userChangedYAxis);
457 connect(&m_YAxisTool, &YAxisTool::leftAxisChanged,
this, &Analyze::userSetLeftAxis);
458 connect(&m_YAxisTool, &YAxisTool::axisColorChanged,
this, &Analyze::userSetAxisColor);
459 qApp->installEventFilter(
this);
462 fullWidthCB->setChecked(
true);
463 keepCurrentCB->setChecked(
true);
464 runtimeDisplay =
true;
465 fullWidthCB->setVisible(
true);
466 fullWidthCB->setDisabled(
false);
470 detailsCB->setChecked(
true);
471 statsCB->setChecked(
true);
472 graphsCB->setChecked(
true);
473 timelineCB->setChecked(
true);
486 initStatsCheckboxes();
499 analyzeSB->setRange(0, MAX_SCROLL_VALUE);
502 setupKeyboardShortcuts(
this);
508void Analyze::setVisibility()
510 detailsWidget->setVisible(detailsCB->isChecked());
511 statsGridWidget->setVisible(statsCB->isChecked());
512 timelinePlot->setVisible(timelineCB->isChecked());
513 statsPlot->setVisible(graphsCB->isChecked());
518void Analyze::timelineMouseWheel(
QWheelEvent *event)
520 if (
event->angleDelta().y() > 0)
522 else if (
event->angleDelta().y() < 0)
528void Analyze::keepCurrent(
int state)
531 if (keepCurrentCB->isChecked())
539QString Analyze::getNextFile(
bool after)
554 dirString = dirPath.toLocalFile();
559 dir.setPath(dirString);
561 filters <<
"*.analyze";
562 dir.setNameFilters(filters);
566 if (fileList.
size() == 0)
570 if (filename.
isEmpty() && fileList.
size() > 0 && !after)
575 for (
int i = fileList.
size() - 1; i >= 0; --i)
577 if (fileList[i] == filename)
587 else if (!after && index <= 0)
589 else if (after && index >= fileList.
size() - 1)
595void Analyze::nextFile()
597 QString filename = getNextFile(
true);
599 displayFile(
QUrl(),
true);
605void Analyze::prevFile()
607 QString filename = getNextFile(
false);
614void Analyze::displayFile(
const QUrl &url,
bool forceCurrentSession)
616 if (forceCurrentSession || (logFilename.size() > 0 && url.
toLocalFile() == logFilename))
619 inputCombo->setCurrentIndex(0);
620 inputValue->setText(
"");
624 maxXValue = readDataFromFile(logFilename);
626 runtimeDisplay =
true;
627 fullWidthCB->setChecked(
true);
628 fullWidthCB->setVisible(
true);
629 fullWidthCB->setDisabled(
false);
630 displayedSession =
QUrl();
635 inputCombo->setCurrentIndex(1);
636 displayedSession = url;
640 inputValue->setText(url.
fileName());
643 runtimeDisplay =
false;
646 checkForMissingSchedulerJobEnd(maxXValue);
648 plotWidth = maxXValue + 5;
654void Analyze::initInputSelection()
659 inputCombo->addItem(
i18n(
"Current Session"));
660 inputCombo->addItem(
i18n(
"Read from File"));
661 inputValue->setText(
"");
662 inputCombo->setCurrentIndex(0);
675 QString(
"Analyze %1 (*.analyze);;%2").arg(
i18n(
"Log")).arg(
i18n(
"All Files (*)")));
678 displayFile(inputURL);
685void Analyze::setupKeyboardShortcuts(
QWidget *plot)
721void Analyze::setSelectedSession(
const Session &s)
723 m_selectedSession = s;
726void Analyze::clearSelectedSession()
733void Analyze::unhighlightTimelineItem()
735 clearSelectedSession();
736 if (selectionHighlight !=
nullptr)
738 timelinePlot->removeItem(selectionHighlight);
739 selectionHighlight =
nullptr;
741 detailsTable->clear();
742 prevSessionB->setDisabled(
true);
743 nextSessionB->setDisabled(
true);
748void Analyze::highlightTimelineItem(
const Session &session)
750 constexpr double halfHeight = 0.5;
751 unhighlightTimelineItem();
753 setSelectedSession(session);
755 rect->topLeft->
setCoords(session.start, session.offset + halfHeight);
756 rect->bottomRight->
setCoords(session.end, session.offset - halfHeight);
757 rect->
setBrush(timelineSelectionBrush);
758 selectionHighlight = rect;
759 prevSessionB->setDisabled(
false);
760 nextSessionB->setDisabled(
false);
765QCPItemRect * Analyze::addSession(
double start,
double end,
double y,
770 rect->topLeft->
setCoords(start, y + halfTimelineHeight);
771 rect->bottomRight->
setCoords(end, y - halfTimelineHeight);
777 if (stripeBrush !=
nullptr)
780 stripe->topLeft->
setCoords(start, y + halfTimelineHeight / 2.0);
781 stripe->bottomRight->
setCoords(end, y - halfTimelineHeight / 2.0);
793void Analyze::addGuideStats(
double raDrift,
double decDrift,
int raPulse,
int decPulse,
double snr,
794 int numStars,
double skyBackground,
double time)
796 double MAX_GUIDE_STATS_GAP = 30;
798 if (time - lastGuideStatsTime > MAX_GUIDE_STATS_GAP &&
799 lastGuideStatsTime >= 0)
801 addGuideStatsInternal(qQNaN(), qQNaN(), 0, 0, qQNaN(), qQNaN(), qQNaN(), qQNaN(), qQNaN(),
802 lastGuideStatsTime + .0001);
803 addGuideStatsInternal(qQNaN(), qQNaN(), 0, 0, qQNaN(), qQNaN(), qQNaN(), qQNaN(), qQNaN(), time - .0001);
804 guiderRms->resetFilter();
807 const double drift = std::hypot(raDrift, decDrift);
812 const double rms = guiderRms->newSample(raDrift, decDrift);
813 addGuideStatsInternal(raDrift, decDrift,
double(raPulse),
double(decPulse), snr, numStars, skyBackground, drift, rms, time);
816 if (captureStartedTime >= 0)
821 if ((lastCaptureRmsTime >= 0) &&
822 (time - lastCaptureRmsTime > MAX_GUIDE_STATS_GAP))
825 statsPlot->graph(CAPTURE_RMS_GRAPH)->addData(lastCaptureRmsTime + .0001, qQNaN());
826 statsPlot->graph(CAPTURE_RMS_GRAPH)->addData(time - .0001, qQNaN());
827 captureRms->resetFilter();
829 const double rmsC = captureRms->newSample(raDrift, decDrift);
830 statsPlot->graph(CAPTURE_RMS_GRAPH)->addData(time, rmsC);
831 lastCaptureRmsTime = time;
834 lastGuideStatsTime = time;
837void Analyze::addGuideStatsInternal(
double raDrift,
double decDrift,
double raPulse,
838 double decPulse,
double snr,
839 double numStars,
double skyBackground,
840 double drift,
double rms,
double time)
842 statsPlot->graph(RA_GRAPH)->addData(time, raDrift);
843 statsPlot->graph(DEC_GRAPH)->addData(time, decDrift);
844 statsPlot->graph(RA_PULSE_GRAPH)->addData(time, raPulse);
845 statsPlot->graph(DEC_PULSE_GRAPH)->addData(time, decPulse);
846 statsPlot->graph(DRIFT_GRAPH)->addData(time, drift);
847 statsPlot->graph(RMS_GRAPH)->addData(time, rms);
851 snrMax = std::max(snr, snrMax);
852 if (!qIsNaN(skyBackground))
853 skyBgMax = std::max(skyBackground, skyBgMax);
854 if (!qIsNaN(numStars))
855 numStarsMax = std::max(numStars,
static_cast<double>(numStarsMax));
857 statsPlot->graph(SNR_GRAPH)->addData(time, snr);
858 statsPlot->graph(NUMSTARS_GRAPH)->addData(time, numStars);
859 statsPlot->graph(SKYBG_GRAPH)->addData(time, skyBackground);
862void Analyze::addTemperature(
double temperature,
double time)
866 if (temperature > -200)
867 statsPlot->graph(TEMPERATURE_GRAPH)->addData(time, temperature);
870void Analyze::addFocusPosition(
double focusPosition,
double time)
872 statsPlot->graph(FOCUS_POSITION_GRAPH)->addData(time, focusPosition);
875void Analyze::addTargetDistance(
double targetDistance,
double time)
878 if (previousCaptureStartedTime >= 0 && previousCaptureCompletedTime >= 0 &&
879 previousCaptureStartedTime < previousCaptureCompletedTime &&
880 previousCaptureCompletedTime <= time)
882 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureStartedTime - .0001, qQNaN());
883 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureStartedTime, targetDistance);
884 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureCompletedTime, targetDistance);
885 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureCompletedTime + .0001, qQNaN());
890void Analyze::addHFR(
double hfr,
int numCaptureStars,
int median,
double eccentricity,
891 double time,
double startTime)
894 statsPlot->graph(HFR_GRAPH)->addData(startTime - .0001, qQNaN());
895 statsPlot->graph(HFR_GRAPH)->addData(startTime, hfr);
896 statsPlot->graph(HFR_GRAPH)->addData(time, hfr);
897 statsPlot->graph(HFR_GRAPH)->addData(time + .0001, qQNaN());
899 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(startTime - .0001, qQNaN());
900 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(startTime, numCaptureStars);
901 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(time, numCaptureStars);
902 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(time + .0001, qQNaN());
904 statsPlot->graph(MEDIAN_GRAPH)->addData(startTime - .0001, qQNaN());
905 statsPlot->graph(MEDIAN_GRAPH)->addData(startTime, median);
906 statsPlot->graph(MEDIAN_GRAPH)->addData(time, median);
907 statsPlot->graph(MEDIAN_GRAPH)->addData(time + .0001, qQNaN());
909 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(startTime - .0001, qQNaN());
910 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(startTime, eccentricity);
911 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(time, eccentricity);
912 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(time + .0001, qQNaN());
914 medianMax = std::max(median, medianMax);
915 numCaptureStarsMax = std::max(numCaptureStars, numCaptureStarsMax);
920void Analyze::addMountCoords(
double ra,
double dec,
double az,
921 double alt,
int pierSide,
double ha,
double time)
923 statsPlot->graph(MOUNT_RA_GRAPH)->addData(time, ra);
924 statsPlot->graph(MOUNT_DEC_GRAPH)->addData(time, dec);
925 statsPlot->graph(MOUNT_HA_GRAPH)->addData(time, ha);
926 statsPlot->graph(AZ_GRAPH)->addData(time, az);
927 statsPlot->graph(ALT_GRAPH)->addData(time, alt);
928 statsPlot->graph(PIER_SIDE_GRAPH)->addData(time,
double(pierSide));
932double Analyze::readDataFromFile(
const QString &filename)
934 double lastTime = 10;
935 QFile inputFile(filename);
942 double time = processInputLine(line);
952double Analyze::processInputLine(
const QString &line)
960 if (list[0].at(0).toLatin1() ==
'#')
966 if ((list[0] ==
"AnalyzeStartTime") &&
list.
size() == 3)
969 startTimeInitialized =
true;
970 analyzeTimeZone =
list[2];
979 if (time < 0 || time > 3600 * 24 * 10)
982 if ((list[0] ==
"CaptureStarting") && (
list.
size() == 4))
988 processCaptureStarting(time, exposureSeconds, filter);
990 else if ((list[0] ==
"CaptureComplete") && (
list.
size() >= 6) && (
list.
size() <= 9))
1009 processCaptureComplete(time, filename, exposureSeconds, filter, hfr, numStars, median, eccentricity,
true);
1011 else if ((list[0] ==
"CaptureAborted") && (
list.
size() == 3))
1016 processCaptureAborted(time, exposureSeconds,
true);
1018 else if ((list[0] ==
"AutofocusStarting") && (
list.
size() >= 4))
1024 AutofocusReason reason;
1028 reason = AutofocusReason::FOCUS_NONE;
1033 reason =
static_cast<AutofocusReason
>(
QString(list[4]).
toInt(&ok));
1036 reasonInfo =
list[5];
1038 processAutofocusStarting(time, temperature, filter, reason, reasonInfo);
1040 else if ((list[0] ==
"AutofocusComplete") && (
list.
size() >= 8))
1047 int reasonInt = reasonV.
toInt();
1048 if (reasonInt < 0 || reasonInt >= AutofocusReason::FOCUS_MAX_REASONS)
1050 AutofocusReason reason =
static_cast<AutofocusReason
>(reasonInt);
1059 processAutofocusCompleteV2(time, temperature, filter, reason, reasonInfo, samples, useWeights, curve, title,
true);
1061 else if ((list[0] ==
"AutofocusComplete") && (
list.
size() >= 4))
1068 processAutofocusComplete(time, filter, samples, curve, title,
true);
1070 else if ((list[0] ==
"AutofocusAborted") && (
list.
size() >= 9))
1076 int reasonInt = reasonV.
toInt();
1077 if (reasonInt < 0 || reasonInt >= AutofocusReason::FOCUS_MAX_REASONS)
1079 AutofocusReason reason =
static_cast<AutofocusReason
>(reasonInt);
1086 AutofocusFailReason failCode;
1088 int failCodeInt = failCodeV.
toInt();
1089 if (failCodeInt < 0 || failCodeInt >= AutofocusFailReason::FOCUS_FAIL_MAX_REASONS)
1091 failCode =
static_cast<AutofocusFailReason
>(failCodeInt);
1096 failCodeInfo =
QString(list[9]);
1097 processAutofocusAbortedV2(time, temperature, filter, reason, reasonInfo, samples, useWeights, failCode, failCodeInfo,
true);
1099 else if ((list[0] ==
"AutofocusAborted") && (
list.
size() >= 4))
1103 processAutofocusAborted(time, filter, samples,
true);
1105 else if ((list[0] ==
"AdaptiveFocusComplete") && (
list.
size() == 12))
1117 const bool focuserMoved =
QString(list[11]).
toInt(&ok) != 0;
1118 processAdaptiveFocusComplete(time, filter, temperature, tempTicks, altitude, altTicks, prevPosError,
1119 thisPosError, totalTicks, position, focuserMoved,
true);
1121 else if ((list[0] ==
"AdaptiveFocusComplete") && (
list.
size() >= 9))
1133 processAdaptiveFocusComplete(time, filter, temperature, tempTicks,
1134 altitude, altTicks, 0, 0, totalTicks, position, focuserMoved,
true);
1136 else if ((list[0] ==
"GuideState") &&
list.
size() == 3)
1138 processGuideState(time, list[2],
true);
1140 else if ((list[0] ==
"GuideStats") &&
list.
size() == 9)
1163 processGuideStats(time, ra, dec, raPulse, decPulse, snr, skyBg, numStars,
true);
1165 else if ((list[0] ==
"Temperature") &&
list.
size() == 3)
1170 processTemperature(time, temperature,
true);
1172 else if ((list[0] ==
"TargetDistance") &&
list.
size() == 3)
1177 processTargetDistance(time, targetDistance,
true);
1179 else if ((list[0] ==
"MountState") &&
list.
size() == 3)
1181 processMountState(time, list[2],
true);
1183 else if ((list[0] ==
"MountCoords") && (
list.
size() == 7 ||
list.
size() == 8))
1203 processMountCoords(time, ra, dec, az, alt, side, ha,
true);
1205 else if ((list[0] ==
"AlignState") &&
list.
size() == 3)
1207 processAlignState(time, list[2],
true);
1209 else if ((list[0] ==
"MeridianFlipState") &&
list.
size() == 3)
1211 processMountFlipState(time, list[2],
true);
1213 else if ((list[0] ==
"SchedulerJobStart") &&
list.
size() == 3)
1216 processSchedulerJobStarted(time, jobName);
1218 else if ((list[0] ==
"SchedulerJobEnd") &&
list.
size() == 4)
1222 processSchedulerJobEnded(time, jobName, reason,
true);
1241 if (col1 ==
"Filename")
1264 if (col1 ==
"Filename")
1273 if (col3.size() > 0)
1291void Analyze::Session::setupTable(
const QString &name,
const QString &status,
1296 details->setRowCount(0);
1298 details->setColumnCount(3);
1299 details->verticalHeader()->setDefaultSectionSize(20);
1300 details->horizontalHeader()->setStretchLastSection(
true);
1301 details->setColumnWidth(0, 100);
1302 details->setColumnWidth(1, 100);
1303 details->setShowGrid(
false);
1304 details->setWordWrap(
true);
1305 details->horizontalHeader()->hide();
1306 details->verticalHeader()->hide();
1310 QString endTimeStr = isTemporary() ?
"Ongoing"
1322void Analyze::Session::addRow(
const QString &key,
const QString &value)
1327bool Analyze::Session::isTemporary()
const
1329 return rect !=
nullptr;
1335Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
bool ok,
double temperature_,
1336 const QString &filter_,
const AutofocusReason reason_,
const QString &reasonInfo_,
const QString &points_,
1337 const bool useWeights_,
const QString &curve_,
const QString &title_,
const AutofocusFailReason failCode_,
1339 :
Session(start_, end_, FOCUS_Y, rect), success(
ok), temperature(temperature_),
filter(filter_), reason(reason_),
1340 reasonInfo(reasonInfo_), points(points_), useWeights(useWeights_), curve(curve_), title(title_), failCode(failCode_),
1341 failCodeInfo(failCodeInfo_)
1349 for (
int i = 0; i < size; )
1351 bool parsed1, parsed2, parsed3, parsed4;
1358 if (!parsed1 || !parsed2 || !parsed3 || !parsed4)
1366 positions.push_back(position);
1367 hfrs.push_back(hfr);
1368 weights.push_back(weight);
1369 outliers.push_back(outlier);
1376Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
bool ok,
double temperature_,
1378 :
Session(start_, end_, FOCUS_Y, rect), success(
ok),
1379 temperature(temperature_),
filter(filter_), points(points_), curve(curve_), title(title_)
1382 reason = AutofocusReason::FOCUS_NONE;
1385 failCode = AutofocusFailReason::FOCUS_FAIL_NONE;
1394 for (
int i = 0; i < size; )
1396 bool parsed1, parsed2;
1401 if (!parsed1 || !parsed2)
1409 positions.push_back(position);
1410 hfrs.push_back(hfr);
1411 weights.push_back(1.0);
1412 outliers.push_back(
false);
1416Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
1417 const QString &filter_,
double temperature_,
double tempTicks_,
double altitude_,
1418 double altTicks_,
int prevPosError_,
int thisPosError_,
int totalTicks_,
int position_)
1419 :
Session(start_, end_, FOCUS_Y, rect), temperature(temperature_),
filter(filter_), tempTicks(tempTicks_),
1420 altitude(altitude_), altTicks(altTicks_), prevPosError(prevPosError_), thisPosError(thisPosError_),
1421 totalTicks(totalTicks_), adaptedPosition(position_)
1423 standardSession =
false;
1426double Analyze::FocusSession::focusPosition()
1428 if (!standardSession)
1429 return adaptedPosition;
1431 if (positions.size() > 0)
1432 return positions.last();
1438bool isTemporaryFile(
const QString &filename)
1441 return filename.
startsWith(tempFileLocation);
1448void Analyze::captureSessionClicked(CaptureSession &c,
bool doubleClick)
1450 highlightTimelineItem(c);
1452 if (c.isTemporary())
1453 c.setupTable(
"Capture",
"in progress", clockTime(c.start), clockTime(c.start), detailsTable);
1455 c.setupTable(
"Capture",
"ABORTED", clockTime(c.start), clockTime(c.end), detailsTable);
1457 c.setupTable(
"Capture",
"successful", clockTime(c.start), clockTime(c.end), detailsTable);
1459 c.addRow(
"Filter", c.filter);
1461 double raRMS, decRMS, totalRMS;
1463 displayGuideGraphics(c.start, c.end, &raRMS, &decRMS, &totalRMS, &numSamples);
1468 if (!c.isTemporary())
1469 c.addRow(
"Filename", c.filename);
1473 if (doubleClick && !c.isTemporary())
1475 QString filename = findFilename(c.filename);
1477 bool tempImage = isTemporaryFile(c.filename);
1478 if (!tempImage && filename.
size() == 0)
1479 appendLogText(
i18n(
"Could not find image file: %1", c.filename));
1480 else if (!tempImage)
1481 displayFITS(filename);
1482 else appendLogText(
i18n(
"Cannot display temporary image file: %1", c.filename));
1490 if (val == 0)
return "";
1491 else if (val > 0)
return "+";
1494QString signedIntString(
int val)
1505void Analyze::focusSessionClicked(FocusSession &c,
bool doubleClick)
1507 Q_UNUSED(doubleClick);
1508 highlightTimelineItem(c);
1510 if (!c.standardSession)
1513 c.setupTable(
"Focus",
"Adaptive", clockTime(c.end), clockTime(c.end), detailsTable);
1514 c.addRow(
"Filter", c.filter);
1515 addDetailsRow(detailsTable,
"Temperature",
Qt::yellow,
QString(
"%1°").arg(c.temperature, 0,
'f', 1),
1517 addDetailsRow(detailsTable,
"Altitude",
Qt::yellow,
QString(
"%1°").arg(c.altitude, 0,
'f', 1),
1520 QString(
"%1 / %2").arg(c.prevPosError).
arg(c.thisPosError));
1522 Qt::white, signedIntString(c.totalTicks));
1527 c.setupTable(
"Focus",
"successful", clockTime(c.start), clockTime(c.end), detailsTable);
1528 else if (c.isTemporary())
1529 c.setupTable(
"Focus",
"in progress", clockTime(c.start), clockTime(c.start), detailsTable);
1531 c.setupTable(
"Focus",
"FAILED", clockTime(c.start), clockTime(c.end), detailsTable);
1533 if (!c.isTemporary())
1537 if (c.hfrs.size() > 0)
1539 if (c.positions.size() > 0)
1545 if (!c.success && !c.isTemporary())
1546 addDetailsRow(detailsTable,
"Fail Reason",
Qt::yellow, AutofocusFailReasonStr[c.failCode],
Qt::white, c.failCodeInfo,
1549 c.addRow(
"Filter", c.filter);
1550 c.addRow(
"Temperature", (c.temperature == INVALID_VALUE) ?
"N/A" :
QString::number(c.temperature,
'f', 1));
1552 if (c.isTemporary())
1553 resetGraphicsPlot();
1555 displayFocusGraphics(c.positions, c.hfrs, c.useWeights, c.weights, c.outliers, c.curve, c.title, c.success);
1562void Analyze::guideSessionClicked(GuideSession &c,
bool doubleClick)
1564 Q_UNUSED(doubleClick);
1565 highlightTimelineItem(c);
1568 if (c.simpleState == G_IDLE)
1570 else if (c.simpleState == G_GUIDING)
1572 else if (c.simpleState == G_CALIBRATING)
1574 else if (c.simpleState == G_SUSPENDED)
1576 else if (c.simpleState == G_DITHERING)
1579 c.setupTable(
"Guide", st, clockTime(c.start), clockTime(c.end), detailsTable);
1580 resetGraphicsPlot();
1581 if (c.simpleState == G_GUIDING)
1583 double raRMS, decRMS, totalRMS;
1585 displayGuideGraphics(c.start, c.end, &raRMS, &decRMS, &totalRMS, &numSamples);
1596void Analyze::displayGuideGraphics(
double start,
double end,
double *raRMS,
1597 double *decRMS,
double *totalRMS,
int *numSamples)
1599 resetGraphicsPlot();
1600 auto ra = statsPlot->graph(RA_GRAPH)->data()->findBegin(start);
1601 auto dec = statsPlot->graph(DEC_GRAPH)->data()->findBegin(start);
1602 auto raEnd = statsPlot->graph(RA_GRAPH)->data()->findEnd(end);
1603 auto decEnd = statsPlot->graph(DEC_GRAPH)->data()->findEnd(end);
1605 double raSquareErrorSum = 0, decSquareErrorSum = 0;
1606 while (ra != raEnd && dec != decEnd &&
1607 ra->mainKey() < end &&
dec->mainKey() < end &&
1608 ra != statsPlot->graph(RA_GRAPH)->data()->constEnd() &&
1609 dec != statsPlot->graph(DEC_GRAPH)->data()->constEnd() &&
1610 ra->mainKey() < end &&
dec->mainKey() < end)
1612 const double raVal = ra->mainValue();
1613 const double decVal =
dec->mainValue();
1614 graphicsPlot->graph(GUIDER_GRAPHICS)->addData(raVal, decVal);
1615 if (!qIsNaN(raVal) && !qIsNaN(decVal))
1617 raSquareErrorSum += raVal * raVal;
1618 decSquareErrorSum += decVal * decVal;
1624 if (numSamples !=
nullptr)
1628 if (raRMS !=
nullptr)
1629 *raRMS = sqrt(raSquareErrorSum / num);
1630 if (decRMS !=
nullptr)
1631 *decRMS = sqrt(decSquareErrorSum / num);
1632 if (totalRMS !=
nullptr)
1633 *totalRMS = sqrt((raSquareErrorSum + decSquareErrorSum) / num);
1634 if (numSamples !=
nullptr)
1651 graphicsPlot->xAxis->setRange(-2.5, 2.5);
1652 graphicsPlot->yAxis->setRange(-2.5, 2.5);
1653 graphicsPlot->xAxis->setScaleRatio(graphicsPlot->yAxis);
1658void Analyze::mountSessionClicked(MountSession &c,
bool doubleClick)
1660 Q_UNUSED(doubleClick);
1661 highlightTimelineItem(c);
1663 c.setupTable(
"Mount", mountStatusString(c.state), clockTime(c.start),
1664 clockTime(c.isTemporary() ? c.start : c.end), detailsTable);
1669void Analyze::alignSessionClicked(AlignSession &c,
bool doubleClick)
1671 Q_UNUSED(doubleClick);
1672 highlightTimelineItem(c);
1673 c.setupTable(
"Align", getAlignStatusString(c.state), clockTime(c.start),
1674 clockTime(c.isTemporary() ? c.start : c.end), detailsTable);
1679void Analyze::mountFlipSessionClicked(MountFlipSession &c,
bool doubleClick)
1681 Q_UNUSED(doubleClick);
1682 highlightTimelineItem(c);
1683 c.setupTable(
"Meridian Flip", MeridianFlipState::meridianFlipStatusString(c.state),
1684 clockTime(c.start), clockTime(c.isTemporary() ? c.start : c.end), detailsTable);
1689void Analyze::schedulerSessionClicked(SchedulerJobSession &c,
bool doubleClick)
1691 Q_UNUSED(doubleClick);
1692 highlightTimelineItem(c);
1693 c.setupTable(
"Scheduler Job", c.jobName,
1694 clockTime(c.start), clockTime(c.isTemporary() ? c.start : c.end), detailsTable);
1695 c.addRow(
"End reason", c.reason);
1701void Analyze::processTimelineClick(
QMouseEvent *event,
bool doubleClick)
1703 unhighlightTimelineItem();
1704 double xval = timelinePlot->xAxis->pixelToCoord(
event->x());
1705 double yval = timelinePlot->yAxis->pixelToCoord(
event->y());
1706 if (yval >= CAPTURE_Y - 0.5 && yval <= CAPTURE_Y + 0.5)
1709 if (candidates.
size() > 0)
1710 captureSessionClicked(candidates[0], doubleClick);
1711 else if ((temporaryCaptureSession.rect !=
nullptr) &&
1712 (xval > temporaryCaptureSession.start))
1713 captureSessionClicked(temporaryCaptureSession, doubleClick);
1715 else if (yval >= FOCUS_Y - 0.5 && yval <= FOCUS_Y + 0.5)
1718 if (candidates.
size() > 0)
1719 focusSessionClicked(candidates[0], doubleClick);
1720 else if ((temporaryFocusSession.rect !=
nullptr) &&
1721 (xval > temporaryFocusSession.start))
1722 focusSessionClicked(temporaryFocusSession, doubleClick);
1724 else if (yval >= GUIDE_Y - 0.5 && yval <= GUIDE_Y + 0.5)
1727 if (candidates.
size() > 0)
1728 guideSessionClicked(candidates[0], doubleClick);
1729 else if ((temporaryGuideSession.rect !=
nullptr) &&
1730 (xval > temporaryGuideSession.start))
1731 guideSessionClicked(temporaryGuideSession, doubleClick);
1733 else if (yval >= MOUNT_Y - 0.5 && yval <= MOUNT_Y + 0.5)
1736 if (candidates.
size() > 0)
1737 mountSessionClicked(candidates[0], doubleClick);
1738 else if ((temporaryMountSession.rect !=
nullptr) &&
1739 (xval > temporaryMountSession.start))
1740 mountSessionClicked(temporaryMountSession, doubleClick);
1742 else if (yval >= ALIGN_Y - 0.5 && yval <= ALIGN_Y + 0.5)
1745 if (candidates.
size() > 0)
1746 alignSessionClicked(candidates[0], doubleClick);
1747 else if ((temporaryAlignSession.rect !=
nullptr) &&
1748 (xval > temporaryAlignSession.start))
1749 alignSessionClicked(temporaryAlignSession, doubleClick);
1751 else if (yval >= MERIDIAN_MOUNT_FLIP_Y - 0.5 && yval <= MERIDIAN_MOUNT_FLIP_Y + 0.5)
1754 if (candidates.
size() > 0)
1755 mountFlipSessionClicked(candidates[0], doubleClick);
1756 else if ((temporaryMountFlipSession.rect !=
nullptr) &&
1757 (xval > temporaryMountFlipSession.start))
1758 mountFlipSessionClicked(temporaryMountFlipSession, doubleClick);
1760 else if (yval >= SCHEDULER_Y - 0.5 && yval <= SCHEDULER_Y + 0.5)
1763 if (candidates.
size() > 0)
1764 schedulerSessionClicked(candidates[0], doubleClick);
1765 else if ((temporarySchedulerJobSession.rect !=
nullptr) &&
1766 (xval > temporarySchedulerJobSession.start))
1767 schedulerSessionClicked(temporarySchedulerJobSession, doubleClick);
1769 setStatsCursor(xval);
1773void Analyze::nextTimelineItem()
1775 changeTimelineItem(
true);
1778void Analyze::previousTimelineItem()
1780 changeTimelineItem(
false);
1783void Analyze::changeTimelineItem(
bool next)
1785 if (m_selectedSession.start == 0 && m_selectedSession.end == 0)
return;
1786 switch(m_selectedSession.offset)
1790 auto nextSession =
next ? captureSessions.findNext(m_selectedSession.start)
1791 : captureSessions.findPrevious(m_selectedSession.start);
1795 while (nextSession && nextSession->aborted)
1796 nextSession =
next ? captureSessions.findNext(nextSession->start)
1797 : captureSessions.findPrevious(nextSession->start);
1802 captureSessionClicked(*nextSession,
true);
1803 setStatsCursor((nextSession->end + nextSession->start) / 2);
1809 auto nextSession =
next ? focusSessions.findNext(m_selectedSession.start)
1810 : focusSessions.findPrevious(m_selectedSession.start);
1813 focusSessionClicked(*nextSession,
true);
1814 setStatsCursor((nextSession->end + nextSession->start) / 2);
1820 auto nextSession =
next ? alignSessions.findNext(m_selectedSession.start)
1821 : alignSessions.findPrevious(m_selectedSession.start);
1824 alignSessionClicked(*nextSession,
true);
1825 setStatsCursor((nextSession->end + nextSession->start) / 2);
1831 auto nextSession =
next ? guideSessions.findNext(m_selectedSession.start)
1832 : guideSessions.findPrevious(m_selectedSession.start);
1835 guideSessionClicked(*nextSession,
true);
1836 setStatsCursor((nextSession->end + nextSession->start) / 2);
1842 auto nextSession =
next ? mountSessions.findNext(m_selectedSession.start)
1843 : mountSessions.findPrevious(m_selectedSession.start);
1846 mountSessionClicked(*nextSession,
true);
1847 setStatsCursor((nextSession->end + nextSession->start) / 2);
1853 auto nextSession =
next ? schedulerJobSessions.findNext(m_selectedSession.start)
1854 : schedulerJobSessions.findPrevious(m_selectedSession.start);
1857 schedulerSessionClicked(*nextSession,
true);
1858 setStatsCursor((nextSession->end + nextSession->start) / 2);
1864 if (!isVisible(m_selectedSession) && !isVisible(m_selectedSession))
1865 adjustView((m_selectedSession.start + m_selectedSession.end) / 2.0);
1869bool Analyze::isVisible(
const Session &s)
const
1871 if (fullWidthCB->isChecked())
1873 return !((s.start < plotStart && s.end < plotStart) ||
1874 (s.start > (plotStart + plotWidth) && s.end > (plotStart + plotWidth)));
1877void Analyze::adjustView(
double time)
1879 if (!fullWidthCB->isChecked())
1881 plotStart = time - plotWidth / 2;
1885void Analyze::setStatsCursor(
double time)
1887 removeStatsCursor();
1892 const double top = statsPlot->yAxis->range().upper;
1893 const double bottom = statsPlot->yAxis->range().lower;
1901 const double top2 = timelinePlot->yAxis->range().upper;
1902 const double bottom2 = timelinePlot->yAxis->range().lower;
1905 timelineCursor = line2;
1907 cursorTimeOut->setText(
QString(
"%1s").arg(time));
1908 cursorClockTimeOut->setText(
QString(
"%1")
1909 .arg(clockTime(time).
toString(
"hh:mm:ss")));
1910 statsCursorTime = time;
1914void Analyze::removeStatsCursor()
1916 if (statsCursor !=
nullptr)
1917 statsPlot->removeItem(statsCursor);
1918 statsCursor =
nullptr;
1920 if (timelineCursor !=
nullptr)
1921 timelinePlot->removeItem(timelineCursor);
1922 timelineCursor =
nullptr;
1924 cursorTimeOut->setText(
"");
1925 cursorClockTimeOut->setText(
"");
1926 statsCursorTime = -1;
1930void Analyze::processStatsClick(
QMouseEvent *event,
bool doubleClick)
1932 Q_UNUSED(doubleClick);
1933 double xval = statsPlot->xAxis->pixelToCoord(
event->x());
1934 setStatsCursor(xval);
1938void Analyze::timelineMousePress(
QMouseEvent *event)
1940 processTimelineClick(event,
false);
1943void Analyze::timelineMouseDoubleClick(
QMouseEvent *event)
1945 processTimelineClick(event,
true);
1954 if (statsPlot->xAxis->pixelToCoord(
event->x()) < plotStart)
1959 processStatsClick(event,
false);
1962void Analyze::statsMouseDoubleClick(
QMouseEvent *event)
1964 processStatsClick(event,
true);
1974 if (statsPlot->xAxis->pixelToCoord(
event->x()) < plotStart)
1976 auto range = yAxis->range();
1978 yAxis->
setRange(range.lower + yDiff, range.upper + yDiff);
1980 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == yAxis)
1981 m_YAxisTool.replot(
true);
1984 processStatsClick(event,
false);
1988void Analyze::scroll(
int value)
1990 double pct =
static_cast<double>(value) / MAX_SCROLL_VALUE;
1991 plotStart = std::max(0.0, maxXValue * pct - plotWidth / 2.0);
1997void Analyze::scrollRight()
1999 plotStart = std::min(maxXValue - plotWidth / 5, plotStart + plotWidth / 5);
2000 fullWidthCB->setChecked(
false);
2004void Analyze::scrollLeft()
2006 plotStart = std::max(0.0, plotStart - plotWidth / 5);
2007 fullWidthCB->setChecked(
false);
2011void Analyze::replot(
bool adjustSlider)
2013 adjustTemporarySessions();
2014 if (fullWidthCB->isChecked())
2017 plotWidth = std::max(10.0, maxXValue);
2019 else if (keepCurrentCB->isChecked())
2021 plotStart = std::max(0.0, maxXValue - plotWidth);
2025 if (keepCurrentCB->isChecked() && statsCursor ==
nullptr)
2027 cursorTimeOut->setText(
QString(
"%1s").arg(maxXValue));
2028 cursorClockTimeOut->setText(
QString(
"%1")
2029 .arg(clockTime(maxXValue).
toString(
"hh:mm:ss")));
2031 analyzeSB->setPageStep(
2032 std::min(MAX_SCROLL_VALUE,
2033 static_cast<int>(MAX_SCROLL_VALUE * plotWidth / maxXValue)));
2036 double sliderCenter = plotStart + plotWidth / 2.0;
2037 analyzeSB->setSliderPosition(MAX_SCROLL_VALUE * (sliderCenter / maxXValue));
2040 timelinePlot->xAxis->setRange(plotStart, plotStart + plotWidth);
2041 timelinePlot->yAxis->setRange(0, LAST_Y);
2043 statsPlot->xAxis->setRange(plotStart, plotStart + plotWidth);
2046 if (statsPlot->isVisible())
2048 for (
auto &pairs : yAxisMap)
2051 if (statsPlot->graph(info.graphIndex)->visible() && info.rescale)
2060 dateTicker->setOffset(displayStartTime.toMSecsSinceEpoch() / 1000.0);
2062 timelinePlot->replot();
2063 statsPlot->replot();
2064 graphicsPlot->replot();
2066 if (activeYAxis !=
nullptr)
2069 const int widthDiff = statsPlot->axisRect()->width() - timelinePlot->axisRect()->width();
2070 const int paddingSize = activeYAxis->padding();
2071 constexpr int maxPadding = 100;
2073 const int newPad = std::min(maxPadding, std::max(0, paddingSize + widthDiff));
2074 if (newPad != paddingSize)
2076 activeYAxis->setPadding(newPad);
2077 statsPlot->replot();
2080 updateStatsValues();
2083void Analyze::statsYZoom(
double zoomAmount)
2085 auto axis = activeYAxis;
2087 auto range = axis->range();
2088 const double halfDiff = (range.upper - range.lower) / 2.0;
2089 const double middle = (range.upper + range.lower) / 2.0;
2090 axis->setRange(
QCPRange(middle - halfDiff * zoomAmount, middle + halfDiff * zoomAmount));
2091 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == axis)
2092 m_YAxisTool.replot(
true);
2094void Analyze::statsYZoomIn()
2097 statsPlot->replot();
2099void Analyze::statsYZoomOut()
2102 statsPlot->replot();
2109template<
typename Func>
2110void updateStat(
double time,
QLineEdit *valueBox,
QCPGraph *graph, Func func,
bool useLastRealVal =
false)
2112 auto begin = graph->
data()->findBegin(time);
2113 double timeDiffThreshold = 10000000.0;
2114 if ((begin != graph->
data()->constEnd()) &&
2115 (fabs(
begin->mainKey() - time) < timeDiffThreshold))
2117 double foundVal =
begin->mainValue();
2119 if (qIsNaN(foundVal))
2122 const double MAX_TIME_DIFF = 600;
2123 while (useLastRealVal && index >= 0)
2125 const double val = graph->
data()->at(index)->mainValue();
2126 const double t = graph->
data()->at(index)->mainKey();
2127 if (time - t > MAX_TIME_DIFF)
2139 valueBox->
setText(func(foundVal));
2147void Analyze::updateStatsValues()
2149 const double time = statsCursorTime < 0 ? maxXValue : statsCursorTime;
2155 updateStat(time, hfrOut, statsPlot->graph(HFR_GRAPH), d2Fcn,
true);
2156 updateStat(time, eccentricityOut, statsPlot->graph(ECCENTRICITY_GRAPH), d2Fcn,
true);
2157 updateStat(time, skyBgOut, statsPlot->graph(SKYBG_GRAPH), d1Fcn);
2158 updateStat(time, snrOut, statsPlot->graph(SNR_GRAPH), d1Fcn);
2159 updateStat(time, raOut, statsPlot->graph(RA_GRAPH), d2Fcn);
2160 updateStat(time, decOut, statsPlot->graph(DEC_GRAPH), d2Fcn);
2161 updateStat(time, driftOut, statsPlot->graph(DRIFT_GRAPH), d2Fcn);
2162 updateStat(time, rmsOut, statsPlot->graph(RMS_GRAPH), d2Fcn);
2163 updateStat(time, rmsCOut, statsPlot->graph(CAPTURE_RMS_GRAPH), d2Fcn);
2164 updateStat(time, azOut, statsPlot->graph(AZ_GRAPH), d1Fcn);
2165 updateStat(time, altOut, statsPlot->graph(ALT_GRAPH), d2Fcn);
2166 updateStat(time, temperatureOut, statsPlot->graph(TEMPERATURE_GRAPH), d2Fcn);
2168 auto asFcn = [](
double d) ->
QString {
return QString(
"%1\"").
arg(d, 0,
'f', 0); };
2169 updateStat(time, targetDistanceOut, statsPlot->graph(TARGET_DISTANCE_GRAPH), asFcn,
true);
2171 auto hmsFcn = [](
double d) ->
QString
2178 updateStat(time, mountRaOut, statsPlot->graph(MOUNT_RA_GRAPH), hmsFcn);
2179 auto dmsFcn = [](
double d) ->
QString {
dms dec;
dec.setD(d);
return dec.toDMSString(); };
2180 updateStat(time, mountDecOut, statsPlot->graph(MOUNT_DEC_GRAPH), dmsFcn);
2181 auto haFcn = [](
double d) ->
QString
2187 if (ha.
Hours() > 12.0)
2195 updateStat(time, mountHaOut, statsPlot->graph(MOUNT_HA_GRAPH), haFcn);
2198 updateStat(time, numStarsOut, statsPlot->graph(NUMSTARS_GRAPH), intFcn);
2199 updateStat(time, raPulseOut, statsPlot->graph(RA_PULSE_GRAPH), intFcn);
2200 updateStat(time, decPulseOut, statsPlot->graph(DEC_PULSE_GRAPH), intFcn);
2201 updateStat(time, numCaptureStarsOut, statsPlot->graph(NUM_CAPTURE_STARS_GRAPH), intFcn,
true);
2202 updateStat(time, medianOut, statsPlot->graph(MEDIAN_GRAPH), intFcn,
true);
2203 updateStat(time, focusPositionOut, statsPlot->graph(FOCUS_POSITION_GRAPH), intFcn);
2205 auto pierFcn = [](
double d) ->
QString
2207 return d == 0.0 ?
"W->E" : d == 1.0 ?
"E->W" :
"?";
2209 updateStat(time, pierSideOut, statsPlot->graph(PIER_SIDE_GRAPH), pierFcn);
2212void Analyze::initStatsCheckboxes()
2214 hfrCB->setChecked(Options::analyzeHFR());
2215 numCaptureStarsCB->setChecked(Options::analyzeNumCaptureStars());
2216 medianCB->setChecked(Options::analyzeMedian());
2217 eccentricityCB->setChecked(Options::analyzeEccentricity());
2218 numStarsCB->setChecked(Options::analyzeNumStars());
2219 skyBgCB->setChecked(Options::analyzeSkyBg());
2220 snrCB->setChecked(Options::analyzeSNR());
2221 temperatureCB->setChecked(Options::analyzeTemperature());
2222 focusPositionCB->setChecked(Options::focusPosition());
2223 targetDistanceCB->setChecked(Options::analyzeTargetDistance());
2224 raCB->setChecked(Options::analyzeRA());
2225 decCB->setChecked(Options::analyzeDEC());
2226 raPulseCB->setChecked(Options::analyzeRAp());
2227 decPulseCB->setChecked(Options::analyzeDECp());
2228 driftCB->setChecked(Options::analyzeDrift());
2229 rmsCB->setChecked(Options::analyzeRMS());
2230 rmsCCB->setChecked(Options::analyzeRMSC());
2231 mountRaCB->setChecked(Options::analyzeMountRA());
2232 mountDecCB->setChecked(Options::analyzeMountDEC());
2233 mountHaCB->setChecked(Options::analyzeMountHA());
2234 azCB->setChecked(Options::analyzeAz());
2235 altCB->setChecked(Options::analyzeAlt());
2236 pierSideCB->setChecked(Options::analyzePierSide());
2239void Analyze::zoomIn()
2241 if (plotWidth > 0.5)
2243 if (keepCurrentCB->isChecked())
2245 plotStart = std::max(0.0, maxXValue - plotWidth / 4.0);
2246 else if (statsCursorTime >= 0)
2248 plotStart = std::max(0.0, statsCursorTime - plotWidth / 4.0);
2251 plotStart += plotWidth / 4.0;
2252 plotWidth = plotWidth / 2.0;
2254 fullWidthCB->setChecked(
false);
2258void Analyze::zoomOut()
2260 if (plotWidth < maxXValue)
2262 plotStart = std::max(0.0, plotStart - plotWidth / 2.0);
2263 plotWidth = plotWidth * 2;
2265 fullWidthCB->setChecked(
false);
2272void setupAxisDefaults(
QCPAxis *axis)
2290 setupAxisDefaults(plot->
yAxis);
2291 setupAxisDefaults(plot->
xAxis);
2296void Analyze::initTimelinePlot()
2298 initQCP(timelinePlot);
2302 textTicker->addTick(CAPTURE_Y,
i18n(
"Capture"));
2303 textTicker->addTick(FOCUS_Y,
i18n(
"Focus"));
2304 textTicker->addTick(ALIGN_Y,
i18n(
"Align"));
2305 textTicker->addTick(GUIDE_Y,
i18n(
"Guide"));
2306 textTicker->addTick(MERIDIAN_MOUNT_FLIP_Y,
i18n(
"Flip"));
2307 textTicker->addTick(MOUNT_Y,
i18n(
"Mount"));
2308 textTicker->addTick(SCHEDULER_Y,
i18n(
"Job"));
2309 timelinePlot->yAxis->setTicker(textTicker);
2311 ADAPTIVE_FOCUS_GRAPH = initGraph(timelinePlot, timelinePlot->yAxis,
QCPGraph::lsNone,
Qt::red,
"adaptiveFocus");
2312 timelinePlot->graph(ADAPTIVE_FOCUS_GRAPH)->setPen(
QPen(
Qt::red, 2));
2317void Analyze::toggleGraph(
int graph_id,
bool show)
2319 statsPlot->graph(graph_id)->setVisible(show);
2321 statsPlot->graph(graph_id)->addToLegend();
2323 statsPlot->graph(graph_id)->removeFromLegend();
2340 if (key ==
nullptr)
return;
2341 auto axisEntry = yAxisMap.find(key);
2342 if (axisEntry == yAxisMap.end())
2343 yAxisMap.insert(std::make_pair(key, axisInfo));
2345 axisEntry->second = axisInfo;
2348template <
typename Func>
2353 const int num = initGraph(plot, yAxis, lineStyle, color, shortName);
2356 const bool autoAxis = YAxisInfo::isRescale(yAxis->range());
2357 updateYAxisMap(out,
YAxisInfo(yAxis, yAxis->range(), autoAxis, num, plot, cb, name, shortName, color));
2372 this->toggleGraph(num, show);
2382 updateYAxisMap(key, axisInfo);
2383 statsPlot->graph(axisInfo.graphIndex)->setPen(
QPen(color));
2384 Options::setAnalyzeStatsYAxis(serializeYAxes());
2388void Analyze::userSetLeftAxis(
QCPAxis *axis)
2391 Options::setAnalyzeStatsYAxis(serializeYAxes());
2397 updateYAxisMap(key, axisInfo);
2398 Options::setAnalyzeStatsYAxis(serializeYAxes());
2403void Analyze::yAxisRangeChanged(
const QCPRange &newRange)
2406 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == activeYAxis)
2407 m_YAxisTool.replot(
true);
2410void Analyze::setLeftAxis(
QCPAxis *axis)
2412 if (axis !=
nullptr && axis != activeYAxis)
2414 for (
const auto &pair : yAxisMap)
2417 QOverload<const QCPRange &>::of(&Analyze::yAxisRangeChanged));
2418 pair.second.axis->setVisible(
false);
2424 QOverload<const QCPRange &>::of(&Analyze::yAxisRangeChanged));
2430 if (info.checkBox && !info.checkBox->isChecked())
2433 info.checkBox->setChecked(
true);
2434 statsPlot->graph(info.graphIndex)->setVisible(
true);
2435 statsPlot->graph(info.graphIndex)->addToLegend();
2438 m_YAxisTool.reset(key, info, info.axis == activeYAxis);
2442QCPAxis *Analyze::newStatsYAxis(
const QString &label,
double lower,
double upper)
2448 setupAxisDefaults(axis);
2452bool Analyze::restoreYAxes(
const QString &encoding)
2454 constexpr int headerSize = 2;
2455 constexpr int itemSize = 5;
2457 if (items.
size() <= headerSize)
return false;
2458 if ((items.
size() - headerSize) % itemSize != 0)
return false;
2459 if (items[0] !=
"AnalyzeStatsYAxis1.0")
return false;
2462 const QString leftID =
"left=";
2463 if (!items[1].startsWith(leftID))
return false;
2465 if (
left.size() <= 0)
return false;
2466 for (
const auto &pair : yAxisMap)
2468 if (pair.second.axis->label() == left)
2470 setLeftAxis(pair.second.axis);
2476 for (
int i = headerSize; i < items.
size(); i += itemSize)
2478 const QString shortName = items[i];
2479 const double lower = items[i + 1].toDouble();
2480 const double upper = items[i + 2].toDouble();
2481 const bool rescale = items[i + 3] ==
"T";
2482 const QColor color(items[i + 4]);
2483 for (
auto &pair : yAxisMap)
2485 auto &info = pair.second;
2486 if (info.axis->label() == shortName)
2489 statsPlot->graph(info.graphIndex)->setPen(
QPen(color));
2490 info.rescale = rescale;
2492 info.axis->setRange(
2494 YAxisInfo::UPPER_RESCALE));
2496 info.axis->setRange(
QCPRange(lower, upper));
2505QString Analyze::serializeYAxes()
2507 QString encoding =
QString(
"AnalyzeStatsYAxis1.0,left=%1").
arg(activeYAxis->label());
2509 for (
const auto &pair : yAxisMap)
2512 const bool rescale = info.rescale;
2515 bool somethingChanged = (info.initialColor != info.color) ||
2516 (rescale != YAxisInfo::isRescale(info.initialRange)) ||
2517 (!rescale && info.axis->range() != info.initialRange);
2519 if (!somethingChanged)
continue;
2522 if (savedAxes.
contains(info.axis->label()))
continue;
2524 double lower = rescale ? YAxisInfo::LOWER_RESCALE : info.axis->range().lower;
2525 double upper = rescale ? YAxisInfo::UPPER_RESCALE : info.axis->range().upper;
2527 .arg(info.axis->label()).
arg(lower).
arg(upper)
2528 .
arg(info.rescale ?
"T" :
"F").
arg(info.color.name()));
2529 savedAxes.
append(info.axis->label());
2534void Analyze::initStatsPlot()
2539 statsPlot->yAxis->setVisible(
true);
2540 statsPlot->yAxis->setLabel(
"RA/DEC");
2541 statsPlot->yAxis->setRange(-2, 5);
2542 setLeftAxis(statsPlot->yAxis);
2545 statsPlot->legend->setVisible(
true);
2546 statsPlot->legend->setFont(
QFont(
"Helvetica", 6));
2547 statsPlot->legend->setTextColor(
Qt::white);
2549 statsPlot->legend->setBrush(
QBrush(
QColor(0, 0, 0, 50)));
2553 statsPlot->legend->setRowSpacing(-10);
2559 statsPlot->legend->setIconSize(10, 18);
2560 statsPlot->legend->setIconTextPadding(3);
2569 if (statsPlot->legend->font().pointSize() < 6)
2572 statsPlot->legend->setRowSpacing(-10);
2573 statsPlot->legend->setIconSize(10, 18);
2574 statsPlot->legend->setFont(
QFont(
"Helvetica", 6));
2575 statsPlot->legend->setBrush(
QBrush(
QColor(0, 0, 0, 50)));
2580 statsPlot->legend->setRowSpacing(-10);
2581 statsPlot->legend->setIconSize(5, 5);
2582 statsPlot->legend->setFont(
QFont(
"Helvetica", 1));
2583 statsPlot->legend->setBrush(
QBrush(
QColor(0, 0, 0, 0)));
2585 statsPlot->replot();
2591 QCPAxis *hfrAxis = newStatsYAxis(shortName, -2, 6);
2593 Options::setAnalyzeHFR, hfrOut);
2597 if (show && !Options::autoHFR())
2598 KSNotification::info(
2599 i18n(
"The \"Auto Compute HFR\" option in the KStars "
2600 "FITS options menu is not set. You won't get HFR values "
2601 "without it. Once you set it, newly captured images "
2602 "will have their HFRs computed."));
2605 shortName =
"#SubStars";
2606 QCPAxis *numCaptureStarsAxis = newStatsYAxis(shortName);
2608 "#Stars in Capture", shortName,
2609 numCaptureStarsCB, Options::setAnalyzeNumCaptureStars, numCaptureStarsOut);
2613 if (show && !Options::autoHFR())
2614 KSNotification::info(
2615 i18n(
"The \"Auto Compute HFR\" option in the KStars "
2616 "FITS options menu is not set. You won't get # stars in capture image values "
2617 "without it. Once you set it, newly captured images "
2618 "will have their stars detected."));
2621 shortName =
"median";
2622 QCPAxis *medianAxis = newStatsYAxis(shortName);
2624 medianCB, Options::setAnalyzeMedian, medianOut);
2627 QCPAxis *eccAxis = newStatsYAxis(shortName, 0, 1.0);
2629 shortName, eccentricityCB, Options::setAnalyzeEccentricity, eccentricityOut);
2630 shortName =
"#Stars";
2631 QCPAxis *numStarsAxis = newStatsYAxis(shortName);
2633 shortName, numStarsCB, Options::setAnalyzeNumStars, numStarsOut);
2634 shortName =
"SkyBG";
2635 QCPAxis *skyBgAxis = newStatsYAxis(shortName);
2637 shortName, skyBgCB, Options::setAnalyzeSkyBg, skyBgOut);
2640 QCPAxis *temperatureAxis = newStatsYAxis(shortName, -40, 40);
2642 temperatureCB, Options::setAnalyzeTemperature, temperatureOut);
2643 shortName =
"focus";
2644 QCPAxis *focusPositionAxis = newStatsYAxis(shortName);
2646 focusPositionCB, Options::setFocusPosition, focusPositionOut);
2647 shortName =
"tDist";
2648 QCPAxis *targetDistanceAxis = newStatsYAxis(shortName, 0, 60);
2649 TARGET_DISTANCE_GRAPH = initGraphAndCB(statsPlot, targetDistanceAxis,
QCPGraph::lsLine,
2651 "Distance to Target (arcsec)", shortName, targetDistanceCB, Options::setAnalyzeTargetDistance, targetDistanceOut);
2653 QCPAxis *snrAxis = newStatsYAxis(shortName, -100, 100);
2655 Options::setAnalyzeSNR, snrOut);
2657 auto raColor = KStarsData::Instance()->colorScheme()->colorNamed(
"RAGuideError");
2658 RA_GRAPH = initGraphAndCB(statsPlot, statsPlot->yAxis,
QCPGraph::lsLine, raColor,
"Guider RA Drift", shortName, raCB,
2659 Options::setAnalyzeRA, raOut);
2661 auto decColor = KStarsData::Instance()->colorScheme()->colorNamed(
"DEGuideError");
2662 DEC_GRAPH = initGraphAndCB(statsPlot, statsPlot->yAxis,
QCPGraph::lsLine, decColor,
"Guider DEC Drift", shortName, decCB,
2663 Options::setAnalyzeDEC, decOut);
2665 auto raPulseColor = KStarsData::Instance()->colorScheme()->colorNamed(
"RAGuideError");
2666 raPulseColor.setAlpha(75);
2667 QCPAxis *pulseAxis = newStatsYAxis(shortName, -2 * 150, 5 * 150);
2668 RA_PULSE_GRAPH = initGraphAndCB(statsPlot, pulseAxis,
QCPGraph::lsLine, raPulseColor,
"RA Correction Pulse (ms)", shortName,
2669 raPulseCB, Options::setAnalyzeRAp, raPulseOut);
2673 auto decPulseColor = KStarsData::Instance()->colorScheme()->colorNamed(
"DEGuideError");
2674 decPulseColor.setAlpha(75);
2675 DEC_PULSE_GRAPH = initGraphAndCB(statsPlot, pulseAxis,
QCPGraph::lsLine, decPulseColor,
"DEC Correction Pulse (ms)",
2676 shortName, decPulseCB, Options::setAnalyzeDECp, decPulseOut);
2679 shortName =
"Drift";
2681 shortName, driftCB, Options::setAnalyzeDrift, driftOut);
2683 RMS_GRAPH = initGraphAndCB(statsPlot, statsPlot->yAxis,
QCPGraph::lsLine,
Qt::red,
"Guider RMS Drift", shortName, rmsCB,
2684 Options::setAnalyzeRMS, rmsOut);
2687 "Guider RMS Drift (during capture)", shortName, rmsCCB,
2688 Options::setAnalyzeRMSC, rmsCOut);
2689 shortName =
"MOUNT_RA";
2690 QCPAxis *mountRaDecAxis = newStatsYAxis(shortName, -10, 370);
2692 MOUNT_RA_GRAPH = initGraphAndCB(statsPlot, mountRaDecAxis,
QCPGraph::lsLine,
Qt::red,
"Mount RA Degrees", shortName,
2693 mountRaCB, Options::setAnalyzeMountRA, mountRaOut);
2694 shortName =
"MOUNT_DEC";
2695 MOUNT_DEC_GRAPH = initGraphAndCB(statsPlot, mountRaDecAxis,
QCPGraph::lsLine,
Qt::red,
"Mount DEC Degrees", shortName,
2696 mountDecCB, Options::setAnalyzeMountDEC, mountDecOut);
2697 shortName =
"MOUNT_HA";
2698 MOUNT_HA_GRAPH = initGraphAndCB(statsPlot, mountRaDecAxis,
QCPGraph::lsLine,
Qt::red,
"Mount Hour Angle", shortName,
2699 mountHaCB, Options::setAnalyzeMountHA, mountHaOut);
2701 QCPAxis *azAxis = newStatsYAxis(shortName, -10, 370);
2703 Options::setAnalyzeAz, azOut);
2705 QCPAxis *altAxis = newStatsYAxis(shortName, 0, 90);
2707 Options::setAnalyzeAlt, altOut);
2708 shortName =
"PierSide";
2709 QCPAxis *pierSideAxis = newStatsYAxis(shortName, -2, 2);
2711 pierSideCB, Options::setAnalyzePierSide, pierSideOut);
2714 statsPlot->setMouseTracking(
false);
2717 dateTicker.reset(
new OffsetDateTimeTicker);
2718 dateTicker->setDateTimeFormat(
"hh:mm:ss");
2719 statsPlot->xAxis->setTicker(dateTicker);
2724 restoreYAxes(Options::analyzeStatsYAxis());
2728void Analyze::reset()
2734 guiderRms->resetFilter();
2735 captureRms->resetFilter();
2737 unhighlightTimelineItem();
2739 for (
int i = 0; i < statsPlot->graphCount(); ++i)
2740 statsPlot->graph(i)->data()->clear();
2741 statsPlot->clearItems();
2743 for (
int i = 0; i < timelinePlot->graphCount(); ++i)
2744 timelinePlot->graph(i)->data()->clear();
2745 timelinePlot->clearItems();
2747 resetGraphicsPlot();
2749 detailsTable->clear();
2750 QPalette p = detailsTable->palette();
2753 detailsTable->setPalette(p);
2755 inputValue->clear();
2757 captureSessions.clear();
2758 focusSessions.clear();
2759 guideSessions.clear();
2760 mountSessions.clear();
2761 alignSessions.clear();
2762 mountFlipSessions.clear();
2763 schedulerJobSessions.clear();
2765 numStarsOut->setText(
"");
2766 skyBgOut->setText(
"");
2767 snrOut->setText(
"");
2768 temperatureOut->setText(
"");
2769 focusPositionOut->setText(
"");
2770 targetDistanceOut->setText(
"");
2771 eccentricityOut->setText(
"");
2772 medianOut->setText(
"");
2773 numCaptureStarsOut->setText(
"");
2776 decOut->setText(
"");
2777 driftOut->setText(
"");
2778 rmsOut->setText(
"");
2779 rmsCOut->setText(
"");
2781 removeStatsCursor();
2782 removeTemporarySessions();
2784 resetCaptureState();
2785 resetAutofocusState();
2791 resetMountFlipState();
2792 resetSchedulerJob();
2797void Analyze::initGraphicsPlot()
2799 initQCP(graphicsPlot);
2800 FOCUS_GRAPHICS = initGraph(graphicsPlot, graphicsPlot->yAxis,
2802 graphicsPlot->graph(FOCUS_GRAPHICS)->setScatterStyle(
2804 errorBars =
new QCPErrorBars(graphicsPlot->xAxis, graphicsPlot->yAxis);
2805 errorBars->setAntialiased(
false);
2806 errorBars->setDataPlottable(graphicsPlot->graph(FOCUS_GRAPHICS));
2807 errorBars->setPen(
QPen(
QColor(180, 180, 180)));
2809 FOCUS_GRAPHICS_FINAL = initGraph(graphicsPlot, graphicsPlot->yAxis,
2811 graphicsPlot->graph(FOCUS_GRAPHICS_FINAL)->setScatterStyle(
2813 finalErrorBars =
new QCPErrorBars(graphicsPlot->xAxis, graphicsPlot->yAxis);
2814 finalErrorBars->setAntialiased(
false);
2815 finalErrorBars->setDataPlottable(graphicsPlot->graph(FOCUS_GRAPHICS_FINAL));
2816 finalErrorBars->setPen(
QPen(
QColor(180, 180, 180)));
2818 FOCUS_GRAPHICS_CURVE = initGraph(graphicsPlot, graphicsPlot->yAxis,
2823 GUIDER_GRAPHICS = initGraph(graphicsPlot, graphicsPlot->yAxis,
2825 graphicsPlot->graph(GUIDER_GRAPHICS)->setScatterStyle(
2832 resetGraphicsPlot();
2833 auto graph = graphicsPlot->graph(FOCUS_GRAPHICS);
2834 auto finalGraph = graphicsPlot->graph(FOCUS_GRAPHICS_FINAL);
2835 double maxHfr = -1e8, maxPosition = -1e8, minHfr = 1e8, minPosition = 1e8;
2837 for (
int i = 0; i < positions.
size(); ++i)
2840 if (success && i == positions.
size() - 1)
2842 finalGraph->addData(positions[i], hfrs[i]);
2846 double sd = (weights[i] <= 0.0) ? 0.0 : std::pow(weights[i], -0.5);
2852 graph->
addData(positions[i], hfrs[i]);
2855 double sd = (weights[i] <= 0.0) ? 0.0 : std::pow(weights[i], -0.5);
2859 maxHfr = std::max(maxHfr, hfrs[i]);
2860 minHfr = std::min(minHfr, hfrs[i]);
2861 maxPosition = std::max(maxPosition, positions[i]);
2862 minPosition = std::min(minPosition, positions[i]);
2865 for (
int i = 0; i < positions.
size(); ++i)
2870 textLabel->position->
setCoords(positions[i], hfrs[i]);
2887 errorBars->setVisible(useWeights);
2888 finalErrorBars->setVisible(useWeights);
2891 errorBars->setData(errorData);
2892 finalErrorBars->setData(finalErrorData);
2895 const double xRange = maxPosition - minPosition;
2896 const double xPadding = hfrs.
size() > 1 ? xRange / (hfrs.
size() - 1.0) : 10;
2899 if (curve.
size() > 0)
2901 CurveFitting curveFitting(curve);
2902 const double interval = xRange / 20.0;
2903 auto curveGraph = graphicsPlot->graph(FOCUS_GRAPHICS_CURVE);
2904 for (
double x = minPosition - xPadding ; x <= maxPosition + xPadding; x += interval)
2905 curveGraph->addData(x, curveFitting.f(x));
2909 plotTitle->setColor(
QColor(255, 255, 255));
2912 plotTitle->position->setCoords(0.5, 0);
2913 plotTitle->setFont(
QFont(font().family(), 10));
2914 plotTitle->setVisible(
true);
2915 plotTitle->setText(title);
2918 const double upper = 1.5 * maxHfr;
2919 const double lower = minHfr - (0.25 * (upper - minHfr));
2920 graphicsPlot->xAxis->setRange(minPosition - xPadding, maxPosition + xPadding);
2921 graphicsPlot->yAxis->setRange(lower, upper);
2922 graphicsPlot->replot();
2925void Analyze::resetGraphicsPlot()
2927 for (
int i = 0; i < graphicsPlot->graphCount(); ++i)
2928 graphicsPlot->graph(i)->data()->clear();
2929 graphicsPlot->clearItems();
2930 errorBars->data().clear();
2931 finalErrorBars->data().clear();
2934void Analyze::displayFITS(
const QString &filename)
2938 if (fitsViewer.isNull())
2941 fitsViewerTabID = fitsViewer->loadFile(url);
2942 connect(fitsViewer.get(), &FITSViewer::terminated,
this, [
this]()
2949 if (fitsViewer->tabExists(fitsViewerTabID))
2950 fitsViewer->updateFile(url, fitsViewerTabID);
2952 fitsViewerTabID = fitsViewer->loadFile(url);
2961double Analyze::logTime(
const QDateTime &time)
2963 if (!logInitialized)
2965 return (time.
toMSecsSinceEpoch() - analyzeStartTime.toMSecsSinceEpoch()) / 1000.0;
2971double Analyze::logTime()
2978QDateTime Analyze::clockTime(
double logSeconds)
2980 return displayStartTime.
addMSecs(logSeconds * 1000.0);
2985void Analyze::saveMessage(
const QString &type,
const QString &message)
2990 .arg(message.
size() > 0 ?
"," :
"", message));
2995void Analyze::restart()
2997 qCDebug(KSTARS_EKOS_ANALYZE) <<
"(Re)starting Analyze";
3004 inputCombo->setCurrentIndex(0);
3005 inputValue->setText(
"");
3006 maxXValue = readDataFromFile(logFilename);
3007 runtimeDisplay =
true;
3008 fullWidthCB->setChecked(
true);
3009 fullWidthCB->setVisible(
true);
3010 fullWidthCB->setDisabled(
false);
3015void Analyze::startLog()
3018 startTimeInitialized =
true;
3020 displayStartTime = analyzeStartTime;
3025 logFile.reset(
new QFile);
3027 logFile->setFileName(logFilename);
3031 logInitialized =
true;
3033 appendToLog(
QString(
"#KStars version %1. Analyze log version 1.0.\n\n")
3034 .arg(KSTARS_VERSION));
3035 appendToLog(
QString(
"%1,%2,%3\n")
3036 .arg(
"AnalyzeStartTime", analyzeStartTime.toString(timeFormat), analyzeStartTime.timeZoneAbbreviation()));
3039void Analyze::appendToLog(
const QString &lines)
3041 if (!logInitialized)
3049void Analyze::updateMaxX(
double time)
3051 maxXValue = std::max(time, maxXValue);
3059void Analyze::removeTemporarySession(
Session * session)
3061 if (session->rect !=
nullptr)
3062 timelinePlot->removeItem(session->rect);
3063 session->rect =
nullptr;
3069void Analyze::removeTemporarySessions()
3071 removeTemporarySession(&temporaryCaptureSession);
3072 removeTemporarySession(&temporaryMountFlipSession);
3073 removeTemporarySession(&temporaryFocusSession);
3074 removeTemporarySession(&temporaryGuideSession);
3075 removeTemporarySession(&temporaryMountSession);
3076 removeTemporarySession(&temporaryAlignSession);
3077 removeTemporarySession(&temporarySchedulerJobSession);
3081void Analyze::addTemporarySession(
Session * session,
double time,
double duration,
3082 int y_offset,
const QBrush &brush)
3084 if (time < 0)
return;
3085 removeTemporarySession(session);
3086 session->rect = addSession(time, time + duration, y_offset, brush);
3087 session->start = time;
3088 session->end = time + duration;
3089 session->offset = y_offset;
3090 session->temporaryBrush = brush;
3091 updateMaxX(time + duration);
3097void Analyze::adjustTemporarySession(
Session * session)
3099 if (session->rect !=
nullptr && session->end < maxXValue)
3101 QBrush brush = session->temporaryBrush;
3102 double start = session->start;
3103 int offset = session->offset;
3104 addTemporarySession(session, start, maxXValue - start, offset, brush);
3109void Analyze::adjustTemporarySessions()
3111 adjustTemporarySession(&temporaryCaptureSession);
3112 adjustTemporarySession(&temporaryMountFlipSession);
3113 adjustTemporarySession(&temporaryFocusSession);
3114 adjustTemporarySession(&temporaryGuideSession);
3115 adjustTemporarySession(&temporaryMountSession);
3116 adjustTemporarySession(&temporaryAlignSession);
3117 adjustTemporarySession(&temporarySchedulerJobSession);
3122void Analyze::captureStarting(
double exposureSeconds,
const QString &filter)
3124 saveMessage(
"CaptureStarting",
3126 processCaptureStarting(logTime(), exposureSeconds, filter);
3131void Analyze::processCaptureStarting(
double time,
double exposureSeconds,
const QString &filter)
3133 captureStartedTime = time;
3134 captureStartedFilter =
filter;
3137 addTemporarySession(&temporaryCaptureSession, time, 1, CAPTURE_Y, temporaryBrush);
3138 temporaryCaptureSession.duration = exposureSeconds;
3139 temporaryCaptureSession.filter =
filter;
3143void Analyze::captureComplete(
const QVariantMap &metadata)
3145 auto filename = metadata[
"filename"].toString();
3146 auto exposure = metadata[
"exposure"].toDouble();
3147 auto filter = metadata[
"filter"].toString();
3148 auto hfr = metadata[
"hfr"].toDouble();
3149 auto starCount = metadata[
"starCount"].toInt();
3150 auto median = metadata[
"median"].toDouble();
3151 auto eccentricity = metadata[
"eccentricity"].toDouble();
3153 saveMessage(
"CaptureComplete",
3154 QString(
"%1,%2,%3,%4,%5,%6,%7")
3159 if (runtimeDisplay && captureStartedTime >= 0)
3160 processCaptureComplete(logTime(), filename, exposure, filter, hfr, starCount, median, eccentricity);
3163void Analyze::processCaptureComplete(
double time,
const QString &filename,
3164 double exposureSeconds,
const QString &filter,
double hfr,
3165 int numStars,
int median,
double eccentricity,
bool batchMode)
3167 removeTemporarySession(&temporaryCaptureSession);
3169 if (captureStartedTime < 0)
3172 if (filterStripeBrush(filter, &stripe))
3173 addSession(captureStartedTime, time, CAPTURE_Y, successBrush, &stripe);
3175 addSession(captureStartedTime, time, CAPTURE_Y, successBrush,
nullptr);
3176 auto session = CaptureSession(captureStartedTime, time,
nullptr,
false,
3177 filename, exposureSeconds, filter);
3178 captureSessions.add(session);
3179 addHFR(hfr, numStars, median, eccentricity, time, captureStartedTime);
3183 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3184 captureSessionClicked(session,
false);
3187 previousCaptureStartedTime = captureStartedTime;
3188 previousCaptureCompletedTime = time;
3189 captureStartedTime = -1;
3192void Analyze::captureAborted(
double exposureSeconds)
3194 saveMessage(
"CaptureAborted",
3196 if (runtimeDisplay && captureStartedTime >= 0)
3197 processCaptureAborted(logTime(), exposureSeconds);
3200void Analyze::processCaptureAborted(
double time,
double exposureSeconds,
bool batchMode)
3202 removeTemporarySession(&temporaryCaptureSession);
3203 double duration = time - captureStartedTime;
3204 if (captureStartedTime >= 0 &&
3205 duration < (exposureSeconds + 30) &&
3210 addSession(captureStartedTime, time, CAPTURE_Y, failureBrush);
3211 auto session = CaptureSession(captureStartedTime, time,
nullptr,
true,
"",
3212 exposureSeconds, captureStartedFilter);
3213 captureSessions.add(session);
3217 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3218 captureSessionClicked(session,
false);
3221 captureStartedTime = -1;
3223 previousCaptureStartedTime = -1;
3224 previousCaptureCompletedTime = -1;
3227void Analyze::resetCaptureState()
3229 captureStartedTime = -1;
3230 captureStartedFilter =
"";
3232 numCaptureStarsMax = 1;
3233 previousCaptureStartedTime = -1;
3234 previousCaptureCompletedTime = -1;
3237void Analyze::autofocusStarting(
double temperature,
const QString &filter,
const AutofocusReason reason,
3240 saveMessage(
"AutofocusStarting",
3246 processAutofocusStarting(logTime(), temperature, filter, reason, reasonInfo);
3249void Analyze::processAutofocusStarting(
double time,
double temperature,
const QString &filter,
const AutofocusReason reason,
3252 autofocusStartedTime = time;
3253 autofocusStartedFilter =
filter;
3254 autofocusStartedTemperature = temperature;
3255 autofocusStartedReason = reason;
3256 autofocusStartedReasonInfo = reasonInfo;
3258 addTemperature(temperature, time);
3261 addTemporarySession(&temporaryFocusSession, time, 1, FOCUS_Y, temporaryBrush);
3262 temporaryFocusSession.temperature = temperature;
3263 temporaryFocusSession.filter =
filter;
3264 temporaryFocusSession.reason = reason;
3267void Analyze::adaptiveFocusComplete(
const QString &filter,
double temperature,
double tempTicks,
3268 double altitude,
double altTicks,
int prevPosError,
int thisPosError,
3269 int totalTicks,
int position,
bool focuserMoved)
3271 saveMessage(
"AdaptiveFocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8,%9,%10").arg(filter).arg(temperature, 0,
'f', 2)
3272 .arg(tempTicks, 0,
'f', 2).arg(altitude, 0,
'f', 2).arg(altTicks, 0,
'f', 2).arg(prevPosError)
3273 .arg(thisPosError).arg(totalTicks).arg(position).arg(focuserMoved ? 1 : 0));
3276 processAdaptiveFocusComplete(logTime(), filter, temperature, tempTicks, altitude, altTicks, prevPosError, thisPosError,
3277 totalTicks, position, focuserMoved);
3280void Analyze::processAdaptiveFocusComplete(
double time,
const QString &filter,
double temperature,
double tempTicks,
3281 double altitude,
double altTicks,
int prevPosError,
int thisPosError,
int totalTicks,
int position,
3282 bool focuserMoved,
bool batchMode)
3284 removeTemporarySession(&temporaryFocusSession);
3286 addFocusPosition(position, time);
3291 if (!focuserMoved || (abs(tempTicks) < 1.00 && abs(altTicks) < 1.0 && prevPosError == 0 && thisPosError == 0))
3295 timelinePlot->graph(ADAPTIVE_FOCUS_GRAPH)->addData(time, FOCUS_Y);
3298 constexpr int artificialInterval = 10;
3299 auto session = FocusSession(time - artificialInterval, time + artificialInterval,
nullptr,
3300 filter, temperature, tempTicks, altitude, altTicks, prevPosError, thisPosError, totalTicks,
3302 focusSessions.add(session);
3307 autofocusStartedTime = -1;
3310void Analyze::autofocusComplete(
const double temperature,
const QString &filter,
const QString &points,
3311 const bool useWeights,
const QString &curve,
const QString &rawTitle)
3326 QVariant reasonV = autofocusStartedReason;
3328 QString reasonInfo = autofocusStartedReasonInfo;
3330 if (curve.
size() == 0)
3331 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6").arg(temp, reason, reasonInfo, filter, points, weights));
3332 else if (title.
size() == 0)
3333 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7").arg(temp, reason, reasonInfo, filter, points, weights,
3336 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8").arg(temp, reason, reasonInfo, filter, points, weights,
3339 if (runtimeDisplay && autofocusStartedTime >= 0)
3340 processAutofocusCompleteV2(logTime(), temperature, filter, autofocusStartedReason, reasonInfo, points, useWeights, curve,
3345void Analyze::processAutofocusCompleteV2(
double time,
const double temperature,
const QString &filter,
3346 const AutofocusReason reason,
const QString &reasonInfo,
3347 const QString &points,
const bool useWeights,
const QString &curve,
const QString &title,
bool batchMode)
3349 removeTemporarySession(&temporaryFocusSession);
3351 if (autofocusStartedTime >= 0)
3354 if (filterStripeBrush(filter, &stripe))
3355 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush, &stripe);
3357 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush,
nullptr);
3359 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
true, temperature, filter, reason, reasonInfo, points,
3360 useWeights, curve, title, AutofocusFailReason::FOCUS_FAIL_NONE,
"");
3361 focusSessions.add(session);
3362 addFocusPosition(session.focusPosition(), autofocusStartedTime);
3365 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3366 focusSessionClicked(session,
false);
3370 autofocusStartedTime = -1;
3374void Analyze::processAutofocusComplete(
double time,
const QString &filter,
const QString &points,
3377 removeTemporarySession(&temporaryFocusSession);
3378 if (autofocusStartedTime < 0)
3382 if (filterStripeBrush(filter, &stripe))
3383 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush, &stripe);
3385 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush,
nullptr);
3386 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
true,
3387 autofocusStartedTemperature, filter, points, curve, title);
3388 focusSessions.add(session);
3389 addFocusPosition(session.focusPosition(), autofocusStartedTime);
3393 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3394 focusSessionClicked(session,
false);
3397 autofocusStartedTime = -1;
3400void Analyze::autofocusAborted(
const QString &filter,
const QString &points,
const bool useWeights,
3401 const AutofocusFailReason failCode,
const QString failCodeInfo)
3404 QVariant reasonV = autofocusStartedReason;
3406 QString reasonInfo = autofocusStartedReasonInfo;
3408 QVariant failReasonV =
static_cast<int>(failCode);
3410 saveMessage(
"AutofocusAborted",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8").arg(temperature, reason, reasonInfo, filter, points,
3411 weights, failReason, failCodeInfo));
3412 if (runtimeDisplay && autofocusStartedTime >= 0)
3413 processAutofocusAbortedV2(logTime(), autofocusStartedTemperature, filter, autofocusStartedReason, reasonInfo, points,
3414 useWeights, failCode, failCodeInfo);
3418void Analyze::processAutofocusAbortedV2(
double time,
double temperature,
const QString &filter,
3419 const AutofocusReason reason,
const QString &reasonInfo,
const QString &points,
const bool useWeights,
3420 const AutofocusFailReason failCode,
const QString failCodeInfo,
bool batchMode)
3422 Q_UNUSED(temperature);
3423 removeTemporarySession(&temporaryFocusSession);
3424 double duration = time - autofocusStartedTime;
3425 if (autofocusStartedTime >= 0 && duration < 1000)
3428 addSession(autofocusStartedTime, time, FOCUS_Y, failureBrush);
3429 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
false, autofocusStartedTemperature, filter, reason,
3430 reasonInfo, points, useWeights,
"",
"", failCode, failCodeInfo);
3431 focusSessions.add(session);
3435 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3436 focusSessionClicked(session,
false);
3439 autofocusStartedTime = -1;
3444void Analyze::processAutofocusAborted(
double time,
const QString &filter,
const QString &points,
bool batchMode)
3446 removeTemporarySession(&temporaryFocusSession);
3447 double duration = time - autofocusStartedTime;
3448 if (autofocusStartedTime >= 0 && duration < 1000)
3451 addSession(autofocusStartedTime, time, FOCUS_Y, failureBrush);
3452 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
false,
3453 autofocusStartedTemperature, filter, points,
"",
"");
3454 focusSessions.add(session);
3458 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3459 focusSessionClicked(session,
false);
3462 autofocusStartedTime = -1;
3466void Analyze::resetAutofocusState()
3468 autofocusStartedTime = -1;
3469 autofocusStartedFilter =
"";
3470 autofocusStartedTemperature = 0;
3471 autofocusStartedReason = AutofocusReason::FOCUS_NONE;
3472 autofocusStartedReasonInfo =
"";
3479Ekos::GuideState stringToGuideState(
const QString &str)
3481 if (str ==
i18n(
"Idle"))
3483 else if (str ==
i18n(
"Aborted"))
3484 return GUIDE_ABORTED;
3485 else if (str ==
i18n(
"Connected"))
3486 return GUIDE_CONNECTED;
3487 else if (str ==
i18n(
"Disconnected"))
3488 return GUIDE_DISCONNECTED;
3489 else if (str ==
i18n(
"Capturing"))
3490 return GUIDE_CAPTURE;
3491 else if (str ==
i18n(
"Looping"))
3492 return GUIDE_LOOPING;
3493 else if (str ==
i18n(
"Subtracting"))
3495 else if (str ==
i18n(
"Subframing"))
3496 return GUIDE_SUBFRAME;
3497 else if (str ==
i18n(
"Selecting star"))
3498 return GUIDE_STAR_SELECT;
3499 else if (str ==
i18n(
"Calibrating"))
3500 return GUIDE_CALIBRATING;
3501 else if (str ==
i18n(
"Calibration error"))
3502 return GUIDE_CALIBRATION_ERROR;
3503 else if (str ==
i18n(
"Calibrated"))
3504 return GUIDE_CALIBRATION_SUCCESS;
3505 else if (str ==
i18n(
"Guiding"))
3506 return GUIDE_GUIDING;
3507 else if (str ==
i18n(
"Suspended"))
3508 return GUIDE_SUSPENDED;
3509 else if (str ==
i18n(
"Reacquiring"))
3510 return GUIDE_REACQUIRE;
3511 else if (str ==
i18n(
"Dithering"))
3512 return GUIDE_DITHERING;
3513 else if (str ==
i18n(
"Manual Dithering"))
3514 return GUIDE_MANUAL_DITHERING;
3515 else if (str ==
i18n(
"Dithering error"))
3516 return GUIDE_DITHERING_ERROR;
3517 else if (str ==
i18n(
"Dithering successful"))
3518 return GUIDE_DITHERING_SUCCESS;
3519 else if (str ==
i18n(
"Settling"))
3520 return GUIDE_DITHERING_SETTLE;
3525Analyze::SimpleGuideState convertGuideState(Ekos::GuideState state)
3531 case GUIDE_CONNECTED:
3532 case GUIDE_DISCONNECTED:
3534 return Analyze::G_IDLE;
3536 return Analyze::G_GUIDING;
3539 case GUIDE_SUBFRAME:
3540 case GUIDE_STAR_SELECT:
3541 return Analyze::G_IGNORE;
3542 case GUIDE_CALIBRATING:
3543 case GUIDE_CALIBRATION_ERROR:
3544 case GUIDE_CALIBRATION_SUCCESS:
3545 return Analyze::G_CALIBRATING;
3546 case GUIDE_SUSPENDED:
3547 case GUIDE_REACQUIRE:
3548 return Analyze::G_SUSPENDED;
3549 case GUIDE_DITHERING:
3550 case GUIDE_MANUAL_DITHERING:
3551 case GUIDE_DITHERING_ERROR:
3552 case GUIDE_DITHERING_SUCCESS:
3553 case GUIDE_DITHERING_SETTLE:
3554 return Analyze::G_DITHERING;
3557 return Analyze::G_IDLE;
3560const QBrush guideBrush(Analyze::SimpleGuideState simpleState)
3562 switch (simpleState)
3564 case Analyze::G_IDLE:
3565 case Analyze::G_IGNORE:
3568 case Analyze::G_GUIDING:
3569 return successBrush;
3570 case Analyze::G_CALIBRATING:
3571 return progressBrush;
3572 case Analyze::G_SUSPENDED:
3573 return stoppedBrush;
3574 case Analyze::G_DITHERING:
3575 return progress2Brush;
3583void Analyze::guideState(Ekos::GuideState state)
3585 QString str = getGuideStatusString(state);
3586 saveMessage(
"GuideState", str);
3588 processGuideState(logTime(), str);
3591void Analyze::processGuideState(
double time,
const QString &stateStr,
bool batchMode)
3593 Ekos::GuideState gstate = stringToGuideState(stateStr);
3594 SimpleGuideState state = convertGuideState(gstate);
3595 if (state == G_IGNORE)
3597 if (state == lastGuideStateStarted)
3600 if (guideStateStartedTime >= 0)
3602 if (lastGuideStateStarted != G_IDLE)
3605 addSession(guideStateStartedTime, time, GUIDE_Y, guideBrush(lastGuideStateStarted));
3606 guideSessions.add(GuideSession(guideStateStartedTime, time,
nullptr, lastGuideStateStarted));
3609 if (state == G_GUIDING)
3611 addTemporarySession(&temporaryGuideSession, time, 1, GUIDE_Y, successBrush);
3612 temporaryGuideSession.simpleState = state;
3615 removeTemporarySession(&temporaryGuideSession);
3617 guideStateStartedTime = time;
3618 lastGuideStateStarted = state;
3624void Analyze::resetGuideState()
3626 lastGuideStateStarted = G_IDLE;
3627 guideStateStartedTime = -1;
3630void Analyze::newTemperature(
double temperatureDelta,
double temperature)
3632 Q_UNUSED(temperatureDelta);
3633 if (temperature > -200 && temperature != lastTemperature)
3636 lastTemperature = temperature;
3638 processTemperature(logTime(), temperature);
3642void Analyze::processTemperature(
double time,
double temperature,
bool batchMode)
3644 addTemperature(temperature, time);
3650void Analyze::resetTemperature()
3652 lastTemperature = -1000;
3655void Analyze::newTargetDistance(
double targetDistance)
3659 processTargetDistance(logTime(), targetDistance);
3662void Analyze::processTargetDistance(
double time,
double targetDistance,
bool batchMode)
3664 addTargetDistance(targetDistance, time);
3670void Analyze::guideStats(
double raError,
double decError,
int raPulse,
int decPulse,
3671 double snr,
double skyBg,
int numStars)
3673 saveMessage(
"GuideStats",
QString(
"%1,%2,%3,%4,%5,%6,%7")
3681 processGuideStats(logTime(), raError, decError, raPulse, decPulse, snr, skyBg, numStars);
3684void Analyze::processGuideStats(
double time,
double raError,
double decError,
3685 int raPulse,
int decPulse,
double snr,
double skyBg,
int numStars,
bool batchMode)
3687 addGuideStats(raError, decError, raPulse, decPulse, snr, numStars, skyBg, time);
3693void Analyze::resetGuideStats()
3695 lastGuideStatsTime = -1;
3696 lastCaptureRmsTime = -1;
3706AlignState convertAlignState(
const QString &str)
3708 for (
int i = 0; i < alignStates.size(); ++i)
3710 if (str == alignStates[i].
toString())
3711 return static_cast<AlignState
>(i);
3716const QBrush alignBrush(AlignState state)
3724 return successBrush;
3726 return failureBrush;
3728 return progress3Brush;
3730 return progress2Brush;
3732 return progressBrush;
3734 return progress4Brush;
3736 return failureBrush;
3745void Analyze::alignState(AlignState state)
3747 if (state == lastAlignStateReceived)
3749 lastAlignStateReceived = state;
3751 QString stateStr = getAlignStatusString(state);
3752 saveMessage(
"AlignState", stateStr);
3754 processAlignState(logTime(), stateStr);
3758void Analyze::processAlignState(
double time,
const QString &statusString,
bool batchMode)
3760 AlignState state = convertAlignState(statusString);
3762 if (state == lastAlignStateStarted)
3765 bool lastStateInteresting = (lastAlignStateStarted ==
ALIGN_PROGRESS ||
3768 if (lastAlignStateStartedTime >= 0 && lastStateInteresting)
3770 if (state == ALIGN_COMPLETE || state == ALIGN_FAILED || state == ALIGN_ABORTED)
3773 addSession(lastAlignStateStartedTime, time, ALIGN_Y, alignBrush(state));
3774 alignSessions.add(AlignSession(lastAlignStateStartedTime, time,
nullptr, state));
3778 addSession(lastAlignStateStartedTime, time, ALIGN_Y, alignBrush(lastAlignStateStarted));
3779 alignSessions.add(AlignSession(lastAlignStateStartedTime, time,
nullptr, lastAlignStateStarted));
3784 if (stateInteresting)
3786 addTemporarySession(&temporaryAlignSession, time, 1, ALIGN_Y, temporaryBrush);
3787 temporaryAlignSession.state = state;
3790 removeTemporarySession(&temporaryAlignSession);
3792 lastAlignStateStartedTime = time;
3793 lastAlignStateStarted = state;
3800void Analyze::resetAlignState()
3804 lastAlignStateStartedTime = -1;
3810const QBrush mountBrush(ISD::Mount::Status state)
3814 case ISD::Mount::MOUNT_IDLE:
3816 case ISD::Mount::MOUNT_ERROR:
3817 return failureBrush;
3818 case ISD::Mount::MOUNT_MOVING:
3819 case ISD::Mount::MOUNT_SLEWING:
3820 return progressBrush;
3821 case ISD::Mount::MOUNT_TRACKING:
3822 return successBrush;
3823 case ISD::Mount::MOUNT_PARKING:
3824 return stoppedBrush;
3825 case ISD::Mount::MOUNT_PARKED:
3826 return stopped2Brush;
3836void Analyze::mountState(ISD::Mount::Status state)
3838 QString statusString = mountStatusString(state);
3839 saveMessage(
"MountState", statusString);
3841 processMountState(logTime(), statusString);
3844void Analyze::processMountState(
double time,
const QString &statusString,
bool batchMode)
3846 ISD::Mount::Status state = toMountStatus(statusString);
3847 if (mountStateStartedTime >= 0 && lastMountState != ISD::Mount::MOUNT_IDLE)
3849 addSession(mountStateStartedTime, time, MOUNT_Y, mountBrush(lastMountState));
3850 mountSessions.add(MountSession(mountStateStartedTime, time,
nullptr, lastMountState));
3853 if (state != ISD::Mount::MOUNT_IDLE)
3855 addTemporarySession(&temporaryMountSession, time, 1, MOUNT_Y,
3856 (state == ISD::Mount::MOUNT_TRACKING) ? successBrush : temporaryBrush);
3857 temporaryMountSession.state = state;
3860 removeTemporarySession(&temporaryMountSession);
3862 mountStateStartedTime = time;
3863 lastMountState = state;
3869void Analyze::resetMountState()
3871 mountStateStartedTime = -1;
3872 lastMountState = ISD::Mount::Status::MOUNT_IDLE;
3876void Analyze::mountCoords(
const SkyPoint &position, ISD::Mount::PierSide pierSide,
const dms &haValue)
3880 double ha = haValue.
Degrees();
3885 constexpr double MIN_DEGREES_CHANGE = 0.25;
3886 if ((fabs(ra - lastMountRa) > MIN_DEGREES_CHANGE) ||
3887 (fabs(dec - lastMountDec) > MIN_DEGREES_CHANGE) ||
3888 (fabs(ha - lastMountHa) > MIN_DEGREES_CHANGE) ||
3889 (fabs(az - lastMountAz) > MIN_DEGREES_CHANGE) ||
3890 (fabs(alt - lastMountAlt) > MIN_DEGREES_CHANGE) ||
3891 (pierSide != lastMountPierSide))
3893 saveMessage(
"MountCoords",
QString(
"%1,%2,%3,%4,%5,%6")
3900 processMountCoords(logTime(), ra, dec, az, alt, pierSide, ha);
3907 lastMountPierSide = pierSide;
3911void Analyze::processMountCoords(
double time,
double ra,
double dec,
double az,
3912 double alt,
int pierSide,
double ha,
bool batchMode)
3914 addMountCoords(ra, dec, az, alt, pierSide, ha, time);
3920void Analyze::resetMountCoords()
3927 lastMountPierSide = -1;
3934MeridianFlipState::MeridianFlipMountState convertMountFlipState(
const QString &statusStr)
3936 if (statusStr ==
"MOUNT_FLIP_NONE")
3937 return MeridianFlipState::MOUNT_FLIP_NONE;
3938 else if (statusStr ==
"MOUNT_FLIP_PLANNED")
3939 return MeridianFlipState::MOUNT_FLIP_PLANNED;
3940 else if (statusStr ==
"MOUNT_FLIP_WAITING")
3941 return MeridianFlipState::MOUNT_FLIP_WAITING;
3942 else if (statusStr ==
"MOUNT_FLIP_ACCEPTED")
3943 return MeridianFlipState::MOUNT_FLIP_ACCEPTED;
3944 else if (statusStr ==
"MOUNT_FLIP_RUNNING")
3945 return MeridianFlipState::MOUNT_FLIP_RUNNING;
3946 else if (statusStr ==
"MOUNT_FLIP_COMPLETED")
3947 return MeridianFlipState::MOUNT_FLIP_COMPLETED;
3948 else if (statusStr ==
"MOUNT_FLIP_ERROR")
3949 return MeridianFlipState::MOUNT_FLIP_ERROR;
3950 return MeridianFlipState::MOUNT_FLIP_ERROR;
3953QBrush mountFlipStateBrush(MeridianFlipState::MeridianFlipMountState state)
3957 case MeridianFlipState::MOUNT_FLIP_NONE:
3959 case MeridianFlipState::MOUNT_FLIP_PLANNED:
3960 return stoppedBrush;
3961 case MeridianFlipState::MOUNT_FLIP_WAITING:
3962 return stopped2Brush;
3963 case MeridianFlipState::MOUNT_FLIP_ACCEPTED:
3964 return progressBrush;
3965 case MeridianFlipState::MOUNT_FLIP_RUNNING:
3966 return progress2Brush;
3967 case MeridianFlipState::MOUNT_FLIP_COMPLETED:
3968 return successBrush;
3969 case MeridianFlipState::MOUNT_FLIP_ERROR:
3970 return failureBrush;
3977void Analyze::mountFlipStatus(MeridianFlipState::MeridianFlipMountState state)
3979 if (state == lastMountFlipStateReceived)
3981 lastMountFlipStateReceived = state;
3983 QString stateStr = MeridianFlipState::meridianFlipStatusString(state);
3984 saveMessage(
"MeridianFlipState", stateStr);
3986 processMountFlipState(logTime(), stateStr);
3991void Analyze::processMountFlipState(
double time,
const QString &statusString,
bool batchMode)
3993 MeridianFlipState::MeridianFlipMountState state = convertMountFlipState(statusString);
3994 if (state == lastMountFlipStateStarted)
3997 bool lastStateInteresting =
3998 (lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_PLANNED ||
3999 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_WAITING ||
4000 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_ACCEPTED ||
4001 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_RUNNING);
4002 if (mountFlipStateStartedTime >= 0 && lastStateInteresting)
4004 if (state == MeridianFlipState::MOUNT_FLIP_COMPLETED || state == MeridianFlipState::MOUNT_FLIP_ERROR)
4007 addSession(mountFlipStateStartedTime, time, MERIDIAN_MOUNT_FLIP_Y, mountFlipStateBrush(state));
4008 mountFlipSessions.add(MountFlipSession(mountFlipStateStartedTime, time,
nullptr, state));
4012 addSession(mountFlipStateStartedTime, time, MERIDIAN_MOUNT_FLIP_Y, mountFlipStateBrush(lastMountFlipStateStarted));
4013 mountFlipSessions.add(MountFlipSession(mountFlipStateStartedTime, time,
nullptr, lastMountFlipStateStarted));
4016 bool stateInteresting =
4017 (state == MeridianFlipState::MOUNT_FLIP_PLANNED ||
4018 state == MeridianFlipState::MOUNT_FLIP_WAITING ||
4019 state == MeridianFlipState::MOUNT_FLIP_ACCEPTED ||
4020 state == MeridianFlipState::MOUNT_FLIP_RUNNING);
4021 if (stateInteresting)
4023 addTemporarySession(&temporaryMountFlipSession, time, 1, MERIDIAN_MOUNT_FLIP_Y, temporaryBrush);
4024 temporaryMountFlipSession.state = state;
4027 removeTemporarySession(&temporaryMountFlipSession);
4029 mountFlipStateStartedTime = time;
4030 lastMountFlipStateStarted = state;
4036void Analyze::resetMountFlipState()
4038 lastMountFlipStateReceived = MeridianFlipState::MOUNT_FLIP_NONE;
4039 lastMountFlipStateStarted = MeridianFlipState::MOUNT_FLIP_NONE;
4040 mountFlipStateStartedTime = -1;
4043QBrush Analyze::schedulerJobBrush(
const QString &jobName,
bool temporary)
4047 {110, 120, 150}, {150, 180, 180}, {180, 165, 130}, {180, 200, 140}, {250, 180, 130},
4048 {190, 170, 160}, {140, 110, 160}, {250, 240, 190}, {250, 200, 220}, {150, 125, 175}
4052 auto it = schedulerJobColors.constFind(jobName);
4053 if (it == schedulerJobColors.constEnd())
4055 const int numSoFar = schedulerJobColors.size();
4056 auto color = colors[numSoFar % colors.
size()];
4057 schedulerJobColors[jobName] = color;
4058 return QBrush(color, pattern);
4062 return QBrush(*it, pattern);
4066void Analyze::schedulerJobStarted(
const QString &jobName)
4068 saveMessage(
"SchedulerJobStart", jobName);
4070 processSchedulerJobStarted(logTime(), jobName);
4074void Analyze::schedulerJobEnded(
const QString &jobName,
const QString &reason)
4076 saveMessage(
"SchedulerJobEnd",
QString(
"%1,%2").arg(jobName, reason));
4078 processSchedulerJobEnded(logTime(), jobName, reason);
4084void Analyze::processSchedulerJobStarted(
double time,
const QString &jobName)
4086 checkForMissingSchedulerJobEnd(time - 1);
4087 schedulerJobStartedTime = time;
4088 schedulerJobStartedJobName = jobName;
4091 addTemporarySession(&temporarySchedulerJobSession, time, 1, SCHEDULER_Y, schedulerJobBrush(jobName,
true));
4092 temporarySchedulerJobSession.jobName = jobName;
4096void Analyze::processSchedulerJobEnded(
double time,
const QString &jobName,
const QString &reason,
bool batchMode)
4098 removeTemporarySession(&temporarySchedulerJobSession);
4100 if (schedulerJobStartedTime < 0)
4106 addSession(schedulerJobStartedTime, time, SCHEDULER_Y, schedulerJobBrush(jobName,
false));
4107 auto session = SchedulerJobSession(schedulerJobStartedTime, time,
nullptr, jobName, reason);
4108 schedulerJobSessions.add(session);
4110 resetSchedulerJob();
4116void Analyze::checkForMissingSchedulerJobEnd(
double time)
4118 if (schedulerJobStartedTime < 0)
4120 removeTemporarySession(&temporarySchedulerJobSession);
4121 addSession(schedulerJobStartedTime, time, SCHEDULER_Y, schedulerJobBrush(schedulerJobStartedJobName,
false));
4122 auto session = SchedulerJobSession(schedulerJobStartedTime, time,
nullptr, schedulerJobStartedJobName,
"missing job end");
4123 schedulerJobSessions.add(session);
4125 resetSchedulerJob();
4128void Analyze::resetSchedulerJob()
4130 schedulerJobStartedTime = -1;
4131 schedulerJobStartedJobName =
"";
4134void Analyze::appendLogText(
const QString &text)
4136 m_LogText.insert(0,
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
4137 KStarsData::Instance()->lt().
toString(
"yyyy-MM-ddThh:mm:ss"), text));
4139 qCInfo(KSTARS_EKOS_ANALYZE) << text;
4144void Analyze::clearLog()
static KStars * Instance()
The abstract base class for all entries in a QCPLegend.
virtual int findBegin(double sortKey, bool expandedRange=true) const override
bool removeFromLegend(QCPLegend *legend) const
bool addToLegend(QCPLegend *legend)
void setPen(const QPen &pen)
void setName(const QString &name)
QCPAxis * addAxis(QCPAxis::AxisType type, QCPAxis *axis=nullptr)
void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
Specialized axis ticker for calendar dates and times as axis ticks.
static QDateTime keyToDateTime(double key)
Specialized axis ticker which allows arbitrary labels at specified coordinates.
Manages a single axis inside a QCustomPlot.
void rangeChanged(const QCPRange &newRange)
void scaleRange(double factor)
void setLabel(const QString &str)
void setTickLabelColor(const QColor &color)
void rescale(bool onlyVisiblePlottables=false)
double pixelToCoord(double value) const
void setLabelColor(const QColor &color)
void setBasePen(const QPen &pen)
void setTickPen(const QPen &pen)
@ atLeft
0x01 Axis is vertical and on the left side of the axis rect
Q_SLOT void setRange(const QCPRange &range)
void setSubTickPen(const QPen &pen)
A plottable that adds a set of error bars to other plottables.
A plottable representing a graph in a plot.
QSharedPointer< QCPGraphDataContainer > data() const
void setLineStyle(LineStyle ls)
@ lsLine
data points are connected by a straight line
@ lsStepRight
line is drawn as steps where the step height is the value of the right data point
@ 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 setZeroLinePen(const QPen &pen)
void setSubGridPen(const QPen &pen)
void setPen(const QPen &pen)
void setPen(const QPen &pen)
A line from one point to another.
void setPen(const QPen &pen)
void setType(PositionType type)
void setCoords(double key, double value)
@ ptAxisRectRatio
Static positioning given by a fraction of the axis rect size (see setAxisRect).
@ ptPlotCoords
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
void setPen(const QPen &pen)
void setSelectedPen(const QPen &pen)
void setBrush(const QBrush &brush)
void setSelectedBrush(const QBrush &brush)
void setText(const QString &text)
void setPositionAlignment(Qt::Alignment alignment)
void setFont(const QFont &font)
void setPen(const QPen &pen)
void setColor(const QColor &color)
@ foRowsFirst
Rows are filled first, and a new element is wrapped to the next column if the row count would exceed ...
Manages a legend inside a QCustomPlot.
@ spLegendBox
0x001 The legend box (frame)
Represents the range an axis is encompassing.
Represents the visual appearance of scatter points.
@ ssDisc
\enumimage{ssDisc.png} a circle which is filled with the pen's color (not the brush as with ssCircle)
@ ssStar
\enumimage{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
@ ssCircle
\enumimage{ssCircle.png} a circle
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
void setBackground(const QPixmap &pm)
QCPGraph * addGraph(QCPAxis *keyAxis=nullptr, QCPAxis *valueAxis=nullptr)
QCPGraph * graph(int index) const
void mouseMove(QMouseEvent *event)
void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
void mouseDoubleClick(QMouseEvent *event)
void mouseWheel(QWheelEvent *event)
void mousePress(QMouseEvent *event)
The sky coordinates of a point in the sky.
const CachingDms & dec() const
const CachingDms & ra() const
An angle, stored as degrees, but expressible in many ways.
virtual void setH(const double &x)
Sets floating-point value of angle, in hours.
virtual void setD(const double &x)
Sets floating-point value of angle, in degrees.
const double & Degrees() const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
@ ALIGN_FAILED
Alignment failed.
@ ALIGN_PROGRESS
Alignment operation in progress.
@ ALIGN_SUCCESSFUL
Alignment Astrometry solver successfully solved the image.
@ ALIGN_SLEWING
Slewing mount to target coordinates.
@ ALIGN_ABORTED
Alignment aborted by user or agent.
@ ALIGN_SYNCING
Syncing mount to solution coordinates.
@ ALIGN_IDLE
No ongoing operations.
@ ALIGN_COMPLETE
Alignment successfully completed.
@ ALIGN_SUSPENDED
Alignment operations suspended.
@ ALIGN_ROTATING
Rotating (Automatic or Manual) to target position angle.
bool fileExists(const QUrl &path)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KIOCORE_EXPORT void add(const QString &fileClass, const QString &directory)
QString name(StandardAction id)
const QList< QKeySequence > & begin()
const QList< QKeySequence > & zoomIn()
const QList< QKeySequence > & zoomOut()
const QList< QKeySequence > & next()
const QList< QKeySequence > & find()
const QList< QKeySequence > & findNext()
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
void valueChanged(int value)
void stateChanged(int state)
void activated(int index)
QDateTime addMSecs(qint64 msecs) const const
QDateTime currentDateTime()
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
qint64 toMSecsSinceEpoch() const const
QString toString(QStringView format, QCalendar cal) const const
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QString absoluteFilePath() const const
QString fileName() const const
void setPointSizeF(qreal pointSize)
void setText(const QString &)
void append(QList< T > &&value)
bool contains(const AT &value) const const
QList< T > mid(qsizetype pos, qsizetype length) const const
void push_back(parameter_type value)
void push_front(parameter_type value)
qsizetype size() const const
QString toString(QDate date, FormatType format) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QString writableLocation(StandardLocation type)
QString & append(QChar ch)
QString arg(Args &&... args) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString right(qsizetype n) const const
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
double toDouble(bool *ok) const const
int toInt(bool *ok, int base) const const
QTextStream & dec(QTextStream &stream)
QTextStream & left(QTextStream &stream)
void setSpan(int row, int column, int rowSpanCount, int columnSpanCount)
void setText(const QString &text)
void setTextAlignment(Qt::Alignment alignment)
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
QString toLocalFile() const const
QString url(FormattingOptions options) const const
int toInt(bool *ok) const const
QString toString() const const
Used to keep track of the various Y-axes and connect them to the QLineEdits.