9#include <KNotifications/KNotification>
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>
36 void setOffset(
double offset)
40 QString getTickLabel(
double tick,
const QLocale &locale,
QChar formatChar,
int precision)
override
48 double timeOffset = 0;
55QString timeFormat =
"yyyy-MM-dd hh:mm:ss.zzz";
58constexpr int MAX_SCROLL_VALUE = 10000;
63constexpr double halfTimelineHeight = 0.35;
68int TEMPERATURE_GRAPH = -1;
69int FOCUS_POSITION_GRAPH = -1;
70int NUM_CAPTURE_STARS_GRAPH = -1;
72int ECCENTRICITY_GRAPH = -1;
73int NUMSTARS_GRAPH = -1;
78int RA_PULSE_GRAPH = -1;
79int DEC_PULSE_GRAPH = -1;
82int CAPTURE_RMS_GRAPH = -1;
83int MOUNT_RA_GRAPH = -1;
84int MOUNT_DEC_GRAPH = -1;
85int MOUNT_HA_GRAPH = -1;
88int PIER_SIDE_GRAPH = -1;
89int TARGET_DISTANCE_GRAPH = -1;
92int ADAPTIVE_FOCUS_GRAPH = -1;
95int FOCUS_GRAPHICS = -1;
96int FOCUS_GRAPHICS_FINAL = -1;
97int FOCUS_GRAPHICS_CURVE = -1;
98int GUIDER_GRAPHICS = -1;
117 return info.exists() && info.isFile();
122const QString mountStatusString(ISD::Mount::Status status)
126 case ISD::Mount::MOUNT_IDLE:
128 case ISD::Mount::MOUNT_PARKED:
129 return i18n(
"Parked");
130 case ISD::Mount::MOUNT_PARKING:
131 return i18n(
"Parking");
132 case ISD::Mount::MOUNT_SLEWING:
133 return i18n(
"Slewing");
134 case ISD::Mount::MOUNT_MOVING:
135 return i18n(
"Moving");
136 case ISD::Mount::MOUNT_TRACKING:
137 return i18n(
"Tracking");
138 case ISD::Mount::MOUNT_ERROR:
139 return i18n(
"Error");
141 return i18n(
"Error");
144ISD::Mount::Status toMountStatus(
const QString &str)
146 if (str ==
i18n(
"Idle"))
147 return ISD::Mount::MOUNT_IDLE;
148 else if (str ==
i18n(
"Parked"))
149 return ISD::Mount::MOUNT_PARKED;
150 else if (str ==
i18n(
"Parking"))
151 return ISD::Mount::MOUNT_PARKING;
152 else if (str ==
i18n(
"Slewing"))
153 return ISD::Mount::MOUNT_SLEWING;
154 else if (str ==
i18n(
"Moving"))
155 return ISD::Mount::MOUNT_MOVING;
156 else if (str ==
i18n(
"Tracking"))
157 return ISD::Mount::MOUNT_TRACKING;
159 return ISD::Mount::MOUNT_ERROR;
168 const QString rPattern(
"^(red|r)$");
174 const QString gPattern(
"^(green|g)$");
180 const QString bPattern(
"^(blue|b)$");
186 const QString hPattern(
"^(ha|h|h-a|h_a|h-alpha|hydrogen|hydrogen_alpha|hydrogen-alpha|h_alpha|halpha)$");
192 const QString oPattern(
"^(oiii|oxygen|oxygen_3|oxygen-3|oxygen_iii|oxygen-iii|o_iii|o-iii|o_3|o-3|o3)$");
199 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)$");
206 const QString lPattern(
"^(lpr|L|UV-IR cut|UV-IR|white|monochrome|broadband|clear|focus|luminance|lum|lps|cls)$");
224 const QString &alternateDirectory = Options::analyzeAlternativeImageDirectory();
228 if (info.exists() && info.isFile())
238 int size = filename.
size();
239 int searchBackFrom = size -
name.
size();
241 while (searchBackFrom >= 0)
243 int index = filename.
lastIndexOf(
'/', searchBackFrom);
251 searchBackFrom = index - 1;
270 intervals.append(value);
279 for (
const auto &interval : intervals)
281 if (t >= interval.start && t <= interval.end)
289 double bestStart = 1e7;
291 for (
auto &interval : intervals)
293 if (interval.start > t && interval.start < bestStart)
295 bestStart = interval.start;
302 T *findPrevious(
double t)
304 double bestStart = -1e7;
306 for (
auto &interval : intervals)
308 if (interval.start < t && interval.start > bestStart)
310 bestStart = interval.start;
320IntervalFinder<Ekos::Analyze::CaptureSession> captureSessions;
321IntervalFinder<Ekos::Analyze::FocusSession> focusSessions;
322IntervalFinder<Ekos::Analyze::GuideSession> guideSessions;
323IntervalFinder<Ekos::Analyze::MountSession> mountSessions;
324IntervalFinder<Ekos::Analyze::AlignSession> alignSessions;
325IntervalFinder<Ekos::Analyze::MountFlipSession> mountFlipSessions;
326IntervalFinder<Ekos::Analyze::SchedulerJobSession> schedulerJobSessions;
342 constexpr double timeConstant = 40.0;
343 alpha = 1.0 / pow(timeConstant, 0.865);
349 double newSample(
double x,
double y)
351 const double valueSquared = x * x + y * y;
352 filteredRMS = alpha * valueSquared + (1.0 - alpha) * filteredRMS;
353 return sqrt(filteredRMS);
357 double filteredRMS { 0 };
370 auto axisEntry = yAxisMap.find(obj);
371 if (axisEntry == yAxisMap.end())
378 Qt::KeyboardModifier::ControlModifier);
381 Qt::KeyboardModifier::ShiftModifier);
385 startYAxisTool(axisEntry->first, axisEntry->second);
394 m_ClickTimerInfo = axisEntry->second;
399 if (m_ClickTimerInfo.checkBox && !m_ClickTimerInfo.checkBox->
isChecked())
402 m_ClickTimerInfo.checkBox->setChecked(true);
403 statsPlot->graph(m_ClickTimerInfo.graphIndex)->setVisible(true);
404 statsPlot->graph(m_ClickTimerInfo.graphIndex)->addToLegend();
406 userSetLeftAxis(m_ClickTimerInfo.axis);
413Analyze::Analyze() : m_YAxisTool(this)
417 captureRms.reset(
new RmsFilter);
418 guiderRms.reset(
new RmsFilter);
420 initInputSelection();
424 connect(&m_YAxisTool, &YAxisTool::axisChanged,
this, &Analyze::userChangedYAxis);
425 connect(&m_YAxisTool, &YAxisTool::leftAxisChanged,
this, &Analyze::userSetLeftAxis);
426 connect(&m_YAxisTool, &YAxisTool::axisColorChanged,
this, &Analyze::userSetAxisColor);
427 qApp->installEventFilter(
this);
430 fullWidthCB->setChecked(
true);
431 keepCurrentCB->setChecked(
true);
432 runtimeDisplay =
true;
433 fullWidthCB->setVisible(
true);
434 fullWidthCB->setDisabled(
false);
438 detailsCB->setChecked(
true);
439 statsCB->setChecked(
true);
440 graphsCB->setChecked(
true);
441 timelineCB->setChecked(
true);
454 initStatsCheckboxes();
467 analyzeSB->setRange(0, MAX_SCROLL_VALUE);
471 setupKeyboardShortcuts(
this);
477void Analyze::setVisibility()
479 detailsWidget->setVisible(detailsCB->isChecked());
480 statsGridWidget->setVisible(statsCB->isChecked());
481 timelinePlot->setVisible(timelineCB->isChecked());
482 statsPlot->setVisible(graphsCB->isChecked());
487void Analyze::timelineMouseWheel(
QWheelEvent *event)
489 if (
event->angleDelta().y() > 0)
491 else if (
event->angleDelta().y() < 0)
497void Analyze::keepCurrent(
int state)
500 if (keepCurrentCB->isChecked())
508QString Analyze::getNextFile(
bool after)
523 dirString = dirPath.toLocalFile();
528 dir.setPath(dirString);
530 filters <<
"*.analyze";
531 dir.setNameFilters(filters);
535 if (fileList.
size() == 0)
539 if (filename.
isEmpty() && fileList.
size() > 0 && !after)
544 for (
int i = fileList.
size() - 1; i >= 0; --i)
546 if (fileList[i] == filename)
556 else if (!after && index <= 0)
558 else if (after && index >= fileList.
size() - 1)
561 return QFileInfo(dirString, after ? fileList[index + 1] : fileList[index - 1]).absoluteFilePath();
564void Analyze::nextFile()
566 QString filename = getNextFile(
true);
568 displayFile(
QUrl(),
true);
574void Analyze::prevFile()
576 QString filename = getNextFile(
false);
583void Analyze::displayFile(
const QUrl &url,
bool forceCurrentSession)
585 if (forceCurrentSession || (logFilename.size() > 0 && url.
toLocalFile() == logFilename))
588 inputCombo->setCurrentIndex(0);
589 inputValue->setText(
"");
593 maxXValue = readDataFromFile(logFilename);
595 runtimeDisplay =
true;
596 fullWidthCB->setChecked(
true);
597 fullWidthCB->setVisible(
true);
598 fullWidthCB->setDisabled(
false);
599 displayedSession =
QUrl();
604 inputCombo->setCurrentIndex(1);
605 displayedSession = url;
609 inputValue->setText(url.
fileName());
612 runtimeDisplay =
false;
615 checkForMissingSchedulerJobEnd(maxXValue);
617 plotWidth = maxXValue + 5;
623void Analyze::initInputSelection()
628 inputCombo->addItem(
i18n(
"Current Session"));
629 inputCombo->addItem(
i18n(
"Read from File"));
630 inputValue->setText(
"");
631 inputCombo->setCurrentIndex(0);
644 QString(
"Analyze %1 (*.analyze);;%2").arg(
i18n(
"Log")).arg(
i18n(
"All Files (*)")));
647 displayFile(inputURL);
654void Analyze::setupKeyboardShortcuts(
QWidget *plot)
696void Analyze::setSelectedSession(
const Session &s)
698 m_selectedSession = s;
701void Analyze::clearSelectedSession()
708void Analyze::unhighlightTimelineItem()
710 clearSelectedSession();
711 if (selectionHighlight !=
nullptr)
713 timelinePlot->removeItem(selectionHighlight);
714 selectionHighlight =
nullptr;
716 detailsTable->clear();
717 prevSessionB->setDisabled(
true);
718 nextSessionB->setDisabled(
true);
723void Analyze::highlightTimelineItem(
const Session &session)
725 constexpr double halfHeight = 0.5;
726 unhighlightTimelineItem();
728 setSelectedSession(session);
730 rect->topLeft->
setCoords(session.start, session.offset + halfHeight);
731 rect->bottomRight->
setCoords(session.end, session.offset - halfHeight);
732 rect->
setBrush(timelineSelectionBrush);
733 selectionHighlight = rect;
734 prevSessionB->setDisabled(
false);
735 nextSessionB->setDisabled(
false);
740QCPItemRect * Analyze::addSession(
double start,
double end,
double y,
745 rect->topLeft->
setCoords(start, y + halfTimelineHeight);
746 rect->bottomRight->
setCoords(end, y - halfTimelineHeight);
752 if (stripeBrush !=
nullptr)
755 stripe->topLeft->
setCoords(start, y + halfTimelineHeight / 2.0);
756 stripe->bottomRight->
setCoords(end, y - halfTimelineHeight / 2.0);
768void Analyze::addGuideStats(
double raDrift,
double decDrift,
int raPulse,
int decPulse,
double snr,
769 int numStars,
double skyBackground,
double time)
771 double MAX_GUIDE_STATS_GAP = 30;
773 if (time - lastGuideStatsTime > MAX_GUIDE_STATS_GAP &&
774 lastGuideStatsTime >= 0)
776 addGuideStatsInternal(qQNaN(), qQNaN(), 0, 0, qQNaN(), qQNaN(), qQNaN(), qQNaN(), qQNaN(),
777 lastGuideStatsTime + .0001);
778 addGuideStatsInternal(qQNaN(), qQNaN(), 0, 0, qQNaN(), qQNaN(), qQNaN(), qQNaN(), qQNaN(), time - .0001);
779 guiderRms->resetFilter();
782 const double drift = std::hypot(raDrift, decDrift);
787 const double rms = guiderRms->newSample(raDrift, decDrift);
788 addGuideStatsInternal(raDrift, decDrift,
double(raPulse),
double(decPulse), snr, numStars, skyBackground, drift, rms, time);
791 if (captureStartedTime >= 0)
796 if ((lastCaptureRmsTime >= 0) &&
797 (time - lastCaptureRmsTime > MAX_GUIDE_STATS_GAP))
800 statsPlot->graph(CAPTURE_RMS_GRAPH)->addData(lastCaptureRmsTime + .0001, qQNaN());
801 statsPlot->graph(CAPTURE_RMS_GRAPH)->addData(time - .0001, qQNaN());
802 captureRms->resetFilter();
804 const double rmsC = captureRms->newSample(raDrift, decDrift);
805 statsPlot->graph(CAPTURE_RMS_GRAPH)->addData(time, rmsC);
806 lastCaptureRmsTime = time;
809 lastGuideStatsTime = time;
812void Analyze::addGuideStatsInternal(
double raDrift,
double decDrift,
double raPulse,
813 double decPulse,
double snr,
814 double numStars,
double skyBackground,
815 double drift,
double rms,
double time)
817 statsPlot->graph(RA_GRAPH)->addData(time, raDrift);
818 statsPlot->graph(DEC_GRAPH)->addData(time, decDrift);
819 statsPlot->graph(RA_PULSE_GRAPH)->addData(time, raPulse);
820 statsPlot->graph(DEC_PULSE_GRAPH)->addData(time, decPulse);
821 statsPlot->graph(DRIFT_GRAPH)->addData(time, drift);
822 statsPlot->graph(RMS_GRAPH)->addData(time, rms);
826 snrMax = std::max(snr, snrMax);
827 if (!qIsNaN(skyBackground))
828 skyBgMax = std::max(skyBackground, skyBgMax);
829 if (!qIsNaN(numStars))
830 numStarsMax = std::max(numStars,
static_cast<double>(numStarsMax));
832 statsPlot->graph(SNR_GRAPH)->addData(time, snr);
833 statsPlot->graph(NUMSTARS_GRAPH)->addData(time, numStars);
834 statsPlot->graph(SKYBG_GRAPH)->addData(time, skyBackground);
837void Analyze::addTemperature(
double temperature,
double time)
841 if (temperature > -200)
842 statsPlot->graph(TEMPERATURE_GRAPH)->addData(time, temperature);
845void Analyze::addFocusPosition(
double focusPosition,
double time)
847 statsPlot->graph(FOCUS_POSITION_GRAPH)->addData(time, focusPosition);
850void Analyze::addTargetDistance(
double targetDistance,
double time)
853 if (previousCaptureStartedTime >= 0 && previousCaptureCompletedTime >= 0 &&
854 previousCaptureStartedTime < previousCaptureCompletedTime &&
855 previousCaptureCompletedTime <= time)
857 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureStartedTime - .0001, qQNaN());
858 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureStartedTime, targetDistance);
859 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureCompletedTime, targetDistance);
860 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureCompletedTime + .0001, qQNaN());
865void Analyze::addHFR(
double hfr,
int numCaptureStars,
int median,
double eccentricity,
866 double time,
double startTime)
869 statsPlot->graph(HFR_GRAPH)->addData(startTime - .0001, qQNaN());
870 statsPlot->graph(HFR_GRAPH)->addData(startTime, hfr);
871 statsPlot->graph(HFR_GRAPH)->addData(time, hfr);
872 statsPlot->graph(HFR_GRAPH)->addData(time + .0001, qQNaN());
874 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(startTime - .0001, qQNaN());
875 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(startTime, numCaptureStars);
876 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(time, numCaptureStars);
877 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(time + .0001, qQNaN());
879 statsPlot->graph(MEDIAN_GRAPH)->addData(startTime - .0001, qQNaN());
880 statsPlot->graph(MEDIAN_GRAPH)->addData(startTime, median);
881 statsPlot->graph(MEDIAN_GRAPH)->addData(time, median);
882 statsPlot->graph(MEDIAN_GRAPH)->addData(time + .0001, qQNaN());
884 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(startTime - .0001, qQNaN());
885 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(startTime, eccentricity);
886 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(time, eccentricity);
887 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(time + .0001, qQNaN());
889 medianMax = std::max(median, medianMax);
890 numCaptureStarsMax = std::max(numCaptureStars, numCaptureStarsMax);
895void Analyze::addMountCoords(
double ra,
double dec,
double az,
896 double alt,
int pierSide,
double ha,
double time)
898 statsPlot->graph(MOUNT_RA_GRAPH)->addData(time, ra);
899 statsPlot->graph(MOUNT_DEC_GRAPH)->addData(time, dec);
900 statsPlot->graph(MOUNT_HA_GRAPH)->addData(time, ha);
901 statsPlot->graph(AZ_GRAPH)->addData(time, az);
902 statsPlot->graph(ALT_GRAPH)->addData(time, alt);
903 statsPlot->graph(PIER_SIDE_GRAPH)->addData(time,
double(pierSide));
907double Analyze::readDataFromFile(
const QString &filename)
909 double lastTime = 10;
910 QFile inputFile(filename);
917 double time = processInputLine(line);
927double Analyze::processInputLine(
const QString &line)
935 if (list[0].at(0).toLatin1() ==
'#')
941 if ((list[0] ==
"AnalyzeStartTime") &&
list.
size() == 3)
944 startTimeInitialized =
true;
945 analyzeTimeZone =
list[2];
954 if (time < 0 || time > 3600 * 24 * 10)
957 if ((list[0] ==
"CaptureStarting") && (
list.
size() == 4))
963 processCaptureStarting(time, exposureSeconds, filter);
965 else if ((list[0] ==
"CaptureComplete") && (
list.
size() >= 6) && (
list.
size() <= 9))
984 processCaptureComplete(time, filename, exposureSeconds, filter, hfr, numStars, median, eccentricity,
true);
986 else if ((list[0] ==
"CaptureAborted") && (
list.
size() == 3))
991 processCaptureAborted(time, exposureSeconds,
true);
993 else if ((list[0] ==
"AutofocusStarting") && (
list.
size() >= 4))
999 AutofocusReason reason;
1003 reason = AutofocusReason::FOCUS_NONE;
1008 reason =
static_cast<AutofocusReason
>(
QString(list[4]).
toInt(&ok));
1011 reasonInfo =
list[5];
1013 processAutofocusStarting(time, temperature, filter, reason, reasonInfo);
1015 else if ((list[0] ==
"AutofocusComplete") && (
list.
size() >= 8))
1022 int reasonInt = reasonV.
toInt();
1023 if (reasonInt < 0 || reasonInt >= AutofocusReason::FOCUS_MAX_REASONS)
1025 AutofocusReason reason =
static_cast<AutofocusReason
>(reasonInt);
1034 processAutofocusCompleteV2(time, temperature, filter, reason, reasonInfo, samples, useWeights, curve, title,
true);
1036 else if ((list[0] ==
"AutofocusComplete") && (
list.
size() >= 4))
1043 processAutofocusComplete(time, filter, samples, curve, title,
true);
1045 else if ((list[0] ==
"AutofocusAborted") && (
list.
size() >= 9))
1051 int reasonInt = reasonV.
toInt();
1052 if (reasonInt < 0 || reasonInt >= AutofocusReason::FOCUS_MAX_REASONS)
1054 AutofocusReason reason =
static_cast<AutofocusReason
>(reasonInt);
1061 AutofocusFailReason failCode;
1063 int failCodeInt = failCodeV.
toInt();
1064 if (failCodeInt < 0 || failCodeInt >= AutofocusFailReason::FOCUS_FAIL_MAX_REASONS)
1066 failCode =
static_cast<AutofocusFailReason
>(failCodeInt);
1071 failCodeInfo =
QString(list[9]);
1072 processAutofocusAbortedV2(time, temperature, filter, reason, reasonInfo, samples, useWeights, failCode, failCodeInfo,
true);
1074 else if ((list[0] ==
"AutofocusAborted") && (
list.
size() >= 4))
1078 processAutofocusAborted(time, filter, samples,
true);
1080 else if ((list[0] ==
"AdaptiveFocusComplete") && (
list.
size() == 12))
1092 const bool focuserMoved =
QString(list[11]).
toInt(&ok) != 0;
1093 processAdaptiveFocusComplete(time, filter, temperature, tempTicks, altitude, altTicks, prevPosError,
1094 thisPosError, totalTicks, position, focuserMoved,
true);
1096 else if ((list[0] ==
"AdaptiveFocusComplete") && (
list.
size() >= 9))
1108 processAdaptiveFocusComplete(time, filter, temperature, tempTicks,
1109 altitude, altTicks, 0, 0, totalTicks, position, focuserMoved,
true);
1111 else if ((list[0] ==
"GuideState") &&
list.
size() == 3)
1113 processGuideState(time, list[2],
true);
1115 else if ((list[0] ==
"GuideStats") &&
list.
size() == 9)
1138 processGuideStats(time, ra, dec, raPulse, decPulse, snr, skyBg, numStars,
true);
1140 else if ((list[0] ==
"Temperature") &&
list.
size() == 3)
1145 processTemperature(time, temperature,
true);
1147 else if ((list[0] ==
"TargetDistance") &&
list.
size() == 3)
1152 processTargetDistance(time, targetDistance,
true);
1154 else if ((list[0] ==
"MountState") &&
list.
size() == 3)
1156 processMountState(time, list[2],
true);
1158 else if ((list[0] ==
"MountCoords") && (
list.
size() == 7 ||
list.
size() == 8))
1178 processMountCoords(time, ra, dec, az, alt, side, ha,
true);
1180 else if ((list[0] ==
"AlignState") &&
list.
size() == 3)
1182 processAlignState(time, list[2],
true);
1184 else if ((list[0] ==
"MeridianFlipState") &&
list.
size() == 3)
1186 processMountFlipState(time, list[2],
true);
1188 else if ((list[0] ==
"SchedulerJobStart") &&
list.
size() == 3)
1191 processSchedulerJobStarted(time, jobName);
1193 else if ((list[0] ==
"SchedulerJobEnd") &&
list.
size() == 4)
1197 processSchedulerJobEnded(time, jobName, reason,
true);
1216 if (col1 ==
"Filename")
1239 if (col1 ==
"Filename")
1248 if (col3.size() > 0)
1266void Analyze::Session::setupTable(
const QString &name,
const QString &status,
1271 details->setRowCount(0);
1273 details->setColumnCount(3);
1274 details->verticalHeader()->setDefaultSectionSize(20);
1275 details->horizontalHeader()->setStretchLastSection(
true);
1276 details->setColumnWidth(0, 100);
1277 details->setColumnWidth(1, 100);
1278 details->setShowGrid(
false);
1279 details->setWordWrap(
true);
1280 details->horizontalHeader()->hide();
1281 details->verticalHeader()->hide();
1285 QString endTimeStr = isTemporary() ?
"Ongoing"
1297void Analyze::Session::addRow(
const QString &key,
const QString &value)
1302bool Analyze::Session::isTemporary()
const
1304 return rect !=
nullptr;
1310Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
bool ok,
double temperature_,
1311 const QString &filter_,
const AutofocusReason reason_,
const QString &reasonInfo_,
const QString &points_,
1312 const bool useWeights_,
const QString &curve_,
const QString &title_,
const AutofocusFailReason failCode_,
1314 :
Session(start_, end_, FOCUS_Y, rect), success(
ok), temperature(temperature_),
filter(filter_), reason(reason_),
1315 reasonInfo(reasonInfo_), points(points_), useWeights(useWeights_), curve(curve_), title(title_), failCode(failCode_),
1316 failCodeInfo(failCodeInfo_)
1324 for (
int i = 0; i < size; )
1326 bool parsed1, parsed2, parsed3, parsed4;
1333 if (!parsed1 || !parsed2 || !parsed3 || !parsed4)
1341 positions.push_back(position);
1342 hfrs.push_back(hfr);
1343 weights.push_back(weight);
1344 outliers.push_back(outlier);
1351Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
bool ok,
double temperature_,
1353 :
Session(start_, end_, FOCUS_Y, rect), success(
ok),
1354 temperature(temperature_),
filter(filter_), points(points_), curve(curve_), title(title_)
1357 reason = AutofocusReason::FOCUS_NONE;
1360 failCode = AutofocusFailReason::FOCUS_FAIL_NONE;
1369 for (
int i = 0; i < size; )
1371 bool parsed1, parsed2;
1376 if (!parsed1 || !parsed2)
1384 positions.push_back(position);
1385 hfrs.push_back(hfr);
1386 weights.push_back(1.0);
1387 outliers.push_back(
false);
1391Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
1392 const QString &filter_,
double temperature_,
double tempTicks_,
double altitude_,
1393 double altTicks_,
int prevPosError_,
int thisPosError_,
int totalTicks_,
int position_)
1394 :
Session(start_, end_, FOCUS_Y, rect), temperature(temperature_),
filter(filter_), tempTicks(tempTicks_),
1395 altitude(altitude_), altTicks(altTicks_), prevPosError(prevPosError_), thisPosError(thisPosError_),
1396 totalTicks(totalTicks_), adaptedPosition(position_)
1398 standardSession =
false;
1401double Analyze::FocusSession::focusPosition()
1403 if (!standardSession)
1404 return adaptedPosition;
1406 if (positions.size() > 0)
1407 return positions.last();
1413bool isTemporaryFile(
const QString &filename)
1416 return filename.
startsWith(tempFileLocation);
1423void Analyze::captureSessionClicked(CaptureSession &c,
bool doubleClick)
1425 highlightTimelineItem(c);
1427 if (c.isTemporary())
1428 c.setupTable(
"Capture",
"in progress", clockTime(c.start), clockTime(c.start), detailsTable);
1430 c.setupTable(
"Capture",
"ABORTED", clockTime(c.start), clockTime(c.end), detailsTable);
1432 c.setupTable(
"Capture",
"successful", clockTime(c.start), clockTime(c.end), detailsTable);
1434 c.addRow(
"Filter", c.filter);
1436 double raRMS, decRMS, totalRMS;
1438 displayGuideGraphics(c.start, c.end, &raRMS, &decRMS, &totalRMS, &numSamples);
1443 if (!c.isTemporary())
1444 c.addRow(
"Filename", c.filename);
1448 if (doubleClick && !c.isTemporary())
1450 QString filename = findFilename(c.filename);
1452 bool tempImage = isTemporaryFile(c.filename);
1453 if (!tempImage && filename.
size() == 0)
1454 appendLogText(
i18n(
"Could not find image file: %1", c.filename));
1455 else if (!tempImage)
1456 displayFITS(filename);
1457 else appendLogText(
i18n(
"Cannot display temporary image file: %1", c.filename));
1465 if (val == 0)
return "";
1466 else if (val > 0)
return "+";
1469QString signedIntString(
int val)
1480void Analyze::focusSessionClicked(FocusSession &c,
bool doubleClick)
1482 Q_UNUSED(doubleClick);
1483 highlightTimelineItem(c);
1485 if (!c.standardSession)
1488 c.setupTable(
"Focus",
"Adaptive", clockTime(c.end), clockTime(c.end), detailsTable);
1489 c.addRow(
"Filter", c.filter);
1490 addDetailsRow(detailsTable,
"Temperature",
Qt::yellow,
QString(
"%1°").arg(c.temperature, 0,
'f', 1),
1492 addDetailsRow(detailsTable,
"Altitude",
Qt::yellow,
QString(
"%1°").arg(c.altitude, 0,
'f', 1),
1495 QString(
"%1 / %2").arg(c.prevPosError).
arg(c.thisPosError));
1497 Qt::white, signedIntString(c.totalTicks));
1502 c.setupTable(
"Focus",
"successful", clockTime(c.start), clockTime(c.end), detailsTable);
1503 else if (c.isTemporary())
1504 c.setupTable(
"Focus",
"in progress", clockTime(c.start), clockTime(c.start), detailsTable);
1506 c.setupTable(
"Focus",
"FAILED", clockTime(c.start), clockTime(c.end), detailsTable);
1508 if (!c.isTemporary())
1512 if (c.hfrs.size() > 0)
1514 if (c.positions.size() > 0)
1520 if (!c.success && !c.isTemporary())
1521 addDetailsRow(detailsTable,
"Fail Reason",
Qt::yellow, AutofocusFailReasonStr[c.failCode],
Qt::white, c.failCodeInfo,
1524 c.addRow(
"Filter", c.filter);
1525 c.addRow(
"Temperature", (c.temperature == INVALID_VALUE) ?
"N/A" :
QString::
number(c.temperature,
'f', 1));
1527 if (c.isTemporary())
1528 resetGraphicsPlot();
1530 displayFocusGraphics(c.positions, c.hfrs, c.useWeights, c.weights, c.outliers, c.curve, c.title, c.success);
1537void Analyze::guideSessionClicked(GuideSession &c,
bool doubleClick)
1539 Q_UNUSED(doubleClick);
1540 highlightTimelineItem(c);
1543 if (c.simpleState == G_IDLE)
1545 else if (c.simpleState == G_GUIDING)
1547 else if (c.simpleState == G_CALIBRATING)
1549 else if (c.simpleState == G_SUSPENDED)
1551 else if (c.simpleState == G_DITHERING)
1554 c.setupTable(
"Guide", st, clockTime(c.start), clockTime(c.end), detailsTable);
1555 resetGraphicsPlot();
1556 if (c.simpleState == G_GUIDING)
1558 double raRMS, decRMS, totalRMS;
1560 displayGuideGraphics(c.start, c.end, &raRMS, &decRMS, &totalRMS, &numSamples);
1571void Analyze::displayGuideGraphics(
double start,
double end,
double *raRMS,
1572 double *decRMS,
double *totalRMS,
int *numSamples)
1574 resetGraphicsPlot();
1575 auto ra = statsPlot->graph(RA_GRAPH)->data()->findBegin(start);
1576 auto dec = statsPlot->graph(DEC_GRAPH)->data()->findBegin(start);
1577 auto raEnd = statsPlot->graph(RA_GRAPH)->data()->findEnd(end);
1578 auto decEnd = statsPlot->graph(DEC_GRAPH)->data()->findEnd(end);
1580 double raSquareErrorSum = 0, decSquareErrorSum = 0;
1581 while (ra != raEnd && dec != decEnd &&
1582 ra->mainKey() < end &&
dec->mainKey() < end &&
1583 ra != statsPlot->graph(RA_GRAPH)->data()->constEnd() &&
1584 dec != statsPlot->graph(DEC_GRAPH)->data()->constEnd() &&
1585 ra->mainKey() < end &&
dec->mainKey() < end)
1587 const double raVal = ra->mainValue();
1588 const double decVal =
dec->mainValue();
1589 graphicsPlot->graph(GUIDER_GRAPHICS)->addData(raVal, decVal);
1590 if (!qIsNaN(raVal) && !qIsNaN(decVal))
1592 raSquareErrorSum += raVal * raVal;
1593 decSquareErrorSum += decVal * decVal;
1599 if (numSamples !=
nullptr)
1603 if (raRMS !=
nullptr)
1604 *raRMS = sqrt(raSquareErrorSum / num);
1605 if (decRMS !=
nullptr)
1606 *decRMS = sqrt(decSquareErrorSum / num);
1607 if (totalRMS !=
nullptr)
1608 *totalRMS = sqrt((raSquareErrorSum + decSquareErrorSum) / num);
1609 if (numSamples !=
nullptr)
1626 graphicsPlot->xAxis->setRange(-2.5, 2.5);
1627 graphicsPlot->yAxis->setRange(-2.5, 2.5);
1628 graphicsPlot->xAxis->setScaleRatio(graphicsPlot->yAxis);
1633void Analyze::mountSessionClicked(MountSession &c,
bool doubleClick)
1635 Q_UNUSED(doubleClick);
1636 highlightTimelineItem(c);
1638 c.setupTable(
"Mount", mountStatusString(c.state), clockTime(c.start),
1639 clockTime(c.isTemporary() ? c.start : c.
end), detailsTable);
1644void Analyze::alignSessionClicked(AlignSession &c,
bool doubleClick)
1646 Q_UNUSED(doubleClick);
1647 highlightTimelineItem(c);
1648 c.setupTable(
"Align", getAlignStatusString(c.state), clockTime(c.start),
1649 clockTime(c.isTemporary() ? c.start : c.
end), detailsTable);
1654void Analyze::mountFlipSessionClicked(MountFlipSession &c,
bool doubleClick)
1656 Q_UNUSED(doubleClick);
1657 highlightTimelineItem(c);
1658 c.setupTable(
"Meridian Flip", MeridianFlipState::meridianFlipStatusString(c.state),
1659 clockTime(c.start), clockTime(c.isTemporary() ? c.start : c.
end), detailsTable);
1664void Analyze::schedulerSessionClicked(SchedulerJobSession &c,
bool doubleClick)
1666 Q_UNUSED(doubleClick);
1667 highlightTimelineItem(c);
1668 c.setupTable(
"Scheduler Job", c.jobName,
1669 clockTime(c.start), clockTime(c.isTemporary() ? c.start : c.
end), detailsTable);
1670 c.addRow(
"End reason", c.reason);
1676void Analyze::processTimelineClick(
QMouseEvent *event,
bool doubleClick)
1678 unhighlightTimelineItem();
1679 double xval = timelinePlot->xAxis->pixelToCoord(
event->x());
1680 double yval = timelinePlot->yAxis->pixelToCoord(
event->y());
1681 if (yval >= CAPTURE_Y - 0.5 && yval <= CAPTURE_Y + 0.5)
1684 if (candidates.
size() > 0)
1685 captureSessionClicked(candidates[0], doubleClick);
1686 else if ((temporaryCaptureSession.rect !=
nullptr) &&
1687 (xval > temporaryCaptureSession.start))
1688 captureSessionClicked(temporaryCaptureSession, doubleClick);
1690 else if (yval >= FOCUS_Y - 0.5 && yval <= FOCUS_Y + 0.5)
1693 if (candidates.
size() > 0)
1694 focusSessionClicked(candidates[0], doubleClick);
1695 else if ((temporaryFocusSession.rect !=
nullptr) &&
1696 (xval > temporaryFocusSession.start))
1697 focusSessionClicked(temporaryFocusSession, doubleClick);
1699 else if (yval >= GUIDE_Y - 0.5 && yval <= GUIDE_Y + 0.5)
1702 if (candidates.
size() > 0)
1703 guideSessionClicked(candidates[0], doubleClick);
1704 else if ((temporaryGuideSession.rect !=
nullptr) &&
1705 (xval > temporaryGuideSession.start))
1706 guideSessionClicked(temporaryGuideSession, doubleClick);
1708 else if (yval >= MOUNT_Y - 0.5 && yval <= MOUNT_Y + 0.5)
1711 if (candidates.
size() > 0)
1712 mountSessionClicked(candidates[0], doubleClick);
1713 else if ((temporaryMountSession.rect !=
nullptr) &&
1714 (xval > temporaryMountSession.start))
1715 mountSessionClicked(temporaryMountSession, doubleClick);
1717 else if (yval >= ALIGN_Y - 0.5 && yval <= ALIGN_Y + 0.5)
1720 if (candidates.
size() > 0)
1721 alignSessionClicked(candidates[0], doubleClick);
1722 else if ((temporaryAlignSession.rect !=
nullptr) &&
1723 (xval > temporaryAlignSession.start))
1724 alignSessionClicked(temporaryAlignSession, doubleClick);
1726 else if (yval >= MERIDIAN_MOUNT_FLIP_Y - 0.5 && yval <= MERIDIAN_MOUNT_FLIP_Y + 0.5)
1729 if (candidates.
size() > 0)
1730 mountFlipSessionClicked(candidates[0], doubleClick);
1731 else if ((temporaryMountFlipSession.rect !=
nullptr) &&
1732 (xval > temporaryMountFlipSession.start))
1733 mountFlipSessionClicked(temporaryMountFlipSession, doubleClick);
1735 else if (yval >= SCHEDULER_Y - 0.5 && yval <= SCHEDULER_Y + 0.5)
1738 if (candidates.
size() > 0)
1739 schedulerSessionClicked(candidates[0], doubleClick);
1740 else if ((temporarySchedulerJobSession.rect !=
nullptr) &&
1741 (xval > temporarySchedulerJobSession.start))
1742 schedulerSessionClicked(temporarySchedulerJobSession, doubleClick);
1744 setStatsCursor(xval);
1748void Analyze::nextTimelineItem()
1750 changeTimelineItem(
true);
1753void Analyze::previousTimelineItem()
1755 changeTimelineItem(
false);
1758void Analyze::changeTimelineItem(
bool next)
1760 if (m_selectedSession.start == 0 && m_selectedSession.end == 0)
return;
1761 switch(m_selectedSession.offset)
1765 auto nextSession =
next ? captureSessions.findNext(m_selectedSession.start)
1766 : captureSessions.findPrevious(m_selectedSession.start);
1770 while (nextSession && nextSession->aborted)
1771 nextSession =
next ? captureSessions.findNext(nextSession->start)
1772 : captureSessions.findPrevious(nextSession->start);
1777 captureSessionClicked(*nextSession,
true);
1778 setStatsCursor((nextSession->end + nextSession->start) / 2);
1784 auto nextSession =
next ? focusSessions.findNext(m_selectedSession.start)
1785 : focusSessions.findPrevious(m_selectedSession.start);
1788 focusSessionClicked(*nextSession,
true);
1789 setStatsCursor((nextSession->end + nextSession->start) / 2);
1795 auto nextSession =
next ? alignSessions.findNext(m_selectedSession.start)
1796 : alignSessions.findPrevious(m_selectedSession.start);
1799 alignSessionClicked(*nextSession,
true);
1800 setStatsCursor((nextSession->end + nextSession->start) / 2);
1806 auto nextSession =
next ? guideSessions.findNext(m_selectedSession.start)
1807 : guideSessions.findPrevious(m_selectedSession.start);
1810 guideSessionClicked(*nextSession,
true);
1811 setStatsCursor((nextSession->end + nextSession->start) / 2);
1817 auto nextSession =
next ? mountSessions.findNext(m_selectedSession.start)
1818 : mountSessions.findPrevious(m_selectedSession.start);
1821 mountSessionClicked(*nextSession,
true);
1822 setStatsCursor((nextSession->end + nextSession->start) / 2);
1828 auto nextSession =
next ? schedulerJobSessions.findNext(m_selectedSession.start)
1829 : schedulerJobSessions.findPrevious(m_selectedSession.start);
1832 schedulerSessionClicked(*nextSession,
true);
1833 setStatsCursor((nextSession->end + nextSession->start) / 2);
1839 if (!isVisible(m_selectedSession) && !isVisible(m_selectedSession))
1840 adjustView((m_selectedSession.start + m_selectedSession.end) / 2.0);
1844bool Analyze::isVisible(
const Session &s)
const
1846 if (fullWidthCB->isChecked())
1848 return !((s.start < plotStart && s.end < plotStart) ||
1849 (s.start > (plotStart + plotWidth) && s.end > (plotStart + plotWidth)));
1852void Analyze::adjustView(
double time)
1854 if (!fullWidthCB->isChecked())
1856 plotStart = time - plotWidth / 2;
1860void Analyze::setStatsCursor(
double time)
1862 removeStatsCursor();
1867 const double top = statsPlot->yAxis->range().upper;
1868 const double bottom = statsPlot->yAxis->range().lower;
1876 const double top2 = timelinePlot->yAxis->range().upper;
1877 const double bottom2 = timelinePlot->yAxis->range().lower;
1880 timelineCursor = line2;
1882 cursorTimeOut->setText(
QString(
"%1s").arg(time));
1883 cursorClockTimeOut->setText(
QString(
"%1")
1884 .arg(clockTime(time).
toString(
"hh:mm:ss")));
1885 statsCursorTime = time;
1889void Analyze::removeStatsCursor()
1891 if (statsCursor !=
nullptr)
1892 statsPlot->removeItem(statsCursor);
1893 statsCursor =
nullptr;
1895 if (timelineCursor !=
nullptr)
1896 timelinePlot->removeItem(timelineCursor);
1897 timelineCursor =
nullptr;
1899 cursorTimeOut->setText(
"");
1900 cursorClockTimeOut->setText(
"");
1901 statsCursorTime = -1;
1905void Analyze::processStatsClick(
QMouseEvent *event,
bool doubleClick)
1907 Q_UNUSED(doubleClick);
1908 double xval = statsPlot->xAxis->pixelToCoord(
event->x());
1909 setStatsCursor(xval);
1913void Analyze::timelineMousePress(
QMouseEvent *event)
1915 processTimelineClick(event,
false);
1918void Analyze::timelineMouseDoubleClick(
QMouseEvent *event)
1920 processTimelineClick(event,
true);
1929 if (statsPlot->xAxis->pixelToCoord(
event->x()) < plotStart)
1934 processStatsClick(event,
false);
1937void Analyze::statsMouseDoubleClick(
QMouseEvent *event)
1939 processStatsClick(event,
true);
1949 if (statsPlot->xAxis->pixelToCoord(
event->x()) < plotStart)
1951 auto range = yAxis->range();
1953 yAxis->
setRange(range.lower + yDiff, range.upper + yDiff);
1955 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == yAxis)
1956 m_YAxisTool.replot(
true);
1959 processStatsClick(event,
false);
1963void Analyze::scroll(
int value)
1965 double pct =
static_cast<double>(value) / MAX_SCROLL_VALUE;
1966 plotStart = std::max(0.0, maxXValue * pct - plotWidth / 2.0);
1972void Analyze::scrollRight()
1974 plotStart = std::min(maxXValue - plotWidth / 5, plotStart + plotWidth / 5);
1975 fullWidthCB->setChecked(
false);
1979void Analyze::scrollLeft()
1981 plotStart = std::max(0.0, plotStart - plotWidth / 5);
1982 fullWidthCB->setChecked(
false);
1986void Analyze::replot(
bool adjustSlider)
1988 adjustTemporarySessions();
1989 if (fullWidthCB->isChecked())
1992 plotWidth = std::max(10.0, maxXValue);
1994 else if (keepCurrentCB->isChecked())
1996 plotStart = std::max(0.0, maxXValue - plotWidth);
2000 if (keepCurrentCB->isChecked() && statsCursor ==
nullptr)
2002 cursorTimeOut->setText(
QString(
"%1s").arg(maxXValue));
2003 cursorClockTimeOut->setText(
QString(
"%1")
2004 .arg(clockTime(maxXValue).
toString(
"hh:mm:ss")));
2006 analyzeSB->setPageStep(
2007 std::min(MAX_SCROLL_VALUE,
2008 static_cast<int>(MAX_SCROLL_VALUE * plotWidth / maxXValue)));
2011 double sliderCenter = plotStart + plotWidth / 2.0;
2012 analyzeSB->setSliderPosition(MAX_SCROLL_VALUE * (sliderCenter / maxXValue));
2015 timelinePlot->xAxis->setRange(plotStart, plotStart + plotWidth);
2016 timelinePlot->yAxis->setRange(0, LAST_Y);
2018 statsPlot->xAxis->setRange(plotStart, plotStart + plotWidth);
2021 if (statsPlot->isVisible())
2023 for (
auto &pairs : yAxisMap)
2026 if (statsPlot->graph(info.graphIndex)->visible() && info.rescale)
2035 dateTicker->setOffset(displayStartTime.toMSecsSinceEpoch() / 1000.0);
2037 timelinePlot->replot();
2038 statsPlot->replot();
2039 graphicsPlot->replot();
2041 if (activeYAxis !=
nullptr)
2044 const int widthDiff = statsPlot->axisRect()->width() - timelinePlot->axisRect()->width();
2045 const int paddingSize = activeYAxis->padding();
2046 constexpr int maxPadding = 100;
2048 const int newPad = std::min(maxPadding, std::max(0, paddingSize + widthDiff));
2049 if (newPad != paddingSize)
2051 activeYAxis->setPadding(newPad);
2052 statsPlot->replot();
2055 updateStatsValues();
2058void Analyze::statsYZoom(
double zoomAmount)
2060 auto axis = activeYAxis;
2062 auto range = axis->range();
2063 const double halfDiff = (range.upper - range.lower) / 2.0;
2064 const double middle = (range.upper + range.lower) / 2.0;
2065 axis->setRange(
QCPRange(middle - halfDiff * zoomAmount, middle + halfDiff * zoomAmount));
2066 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == axis)
2067 m_YAxisTool.replot(
true);
2069void Analyze::statsYZoomIn()
2072 statsPlot->replot();
2074void Analyze::statsYZoomOut()
2077 statsPlot->replot();
2084template<
typename Func>
2085void updateStat(
double time,
QLineEdit *valueBox,
QCPGraph *graph, Func func,
bool useLastRealVal =
false)
2087 auto begin = graph->
data()->findBegin(time);
2088 double timeDiffThreshold = 10000000.0;
2089 if ((begin != graph->
data()->constEnd()) &&
2090 (fabs(
begin->mainKey() - time) < timeDiffThreshold))
2092 double foundVal =
begin->mainValue();
2094 if (qIsNaN(foundVal))
2097 const double MAX_TIME_DIFF = 600;
2098 while (useLastRealVal && index >= 0)
2100 const double val = graph->
data()->at(index)->mainValue();
2101 const double t = graph->
data()->at(index)->mainKey();
2102 if (time - t > MAX_TIME_DIFF)
2114 valueBox->
setText(func(foundVal));
2122void Analyze::updateStatsValues()
2124 const double time = statsCursorTime < 0 ? maxXValue : statsCursorTime;
2130 updateStat(time, hfrOut, statsPlot->graph(HFR_GRAPH), d2Fcn,
true);
2131 updateStat(time, eccentricityOut, statsPlot->graph(ECCENTRICITY_GRAPH), d2Fcn,
true);
2132 updateStat(time, skyBgOut, statsPlot->graph(SKYBG_GRAPH), d1Fcn);
2133 updateStat(time, snrOut, statsPlot->graph(SNR_GRAPH), d1Fcn);
2134 updateStat(time, raOut, statsPlot->graph(RA_GRAPH), d2Fcn);
2135 updateStat(time, decOut, statsPlot->graph(DEC_GRAPH), d2Fcn);
2136 updateStat(time, driftOut, statsPlot->graph(DRIFT_GRAPH), d2Fcn);
2137 updateStat(time, rmsOut, statsPlot->graph(RMS_GRAPH), d2Fcn);
2138 updateStat(time, rmsCOut, statsPlot->graph(CAPTURE_RMS_GRAPH), d2Fcn);
2139 updateStat(time, azOut, statsPlot->graph(AZ_GRAPH), d1Fcn);
2140 updateStat(time, altOut, statsPlot->graph(ALT_GRAPH), d2Fcn);
2141 updateStat(time, temperatureOut, statsPlot->graph(TEMPERATURE_GRAPH), d2Fcn);
2143 auto asFcn = [](
double d) ->
QString {
return QString(
"%1\"").
arg(d, 0,
'f', 0); };
2144 updateStat(time, targetDistanceOut, statsPlot->graph(TARGET_DISTANCE_GRAPH), asFcn,
true);
2146 auto hmsFcn = [](
double d) ->
QString
2153 updateStat(time, mountRaOut, statsPlot->graph(MOUNT_RA_GRAPH), hmsFcn);
2154 auto dmsFcn = [](
double d) ->
QString {
dms dec;
dec.setD(d);
return dec.toDMSString(); };
2155 updateStat(time, mountDecOut, statsPlot->graph(MOUNT_DEC_GRAPH), dmsFcn);
2156 auto haFcn = [](
double d) ->
QString
2162 if (ha.
Hours() > 12.0)
2170 updateStat(time, mountHaOut, statsPlot->graph(MOUNT_HA_GRAPH), haFcn);
2173 updateStat(time, numStarsOut, statsPlot->graph(NUMSTARS_GRAPH), intFcn);
2174 updateStat(time, raPulseOut, statsPlot->graph(RA_PULSE_GRAPH), intFcn);
2175 updateStat(time, decPulseOut, statsPlot->graph(DEC_PULSE_GRAPH), intFcn);
2176 updateStat(time, numCaptureStarsOut, statsPlot->graph(NUM_CAPTURE_STARS_GRAPH), intFcn,
true);
2177 updateStat(time, medianOut, statsPlot->graph(MEDIAN_GRAPH), intFcn,
true);
2178 updateStat(time, focusPositionOut, statsPlot->graph(FOCUS_POSITION_GRAPH), intFcn);
2180 auto pierFcn = [](
double d) ->
QString
2182 return d == 0.0 ?
"W->E" : d == 1.0 ?
"E->W" :
"?";
2184 updateStat(time, pierSideOut, statsPlot->graph(PIER_SIDE_GRAPH), pierFcn);
2187void Analyze::initStatsCheckboxes()
2189 hfrCB->setChecked(Options::analyzeHFR());
2190 numCaptureStarsCB->setChecked(Options::analyzeNumCaptureStars());
2191 medianCB->setChecked(Options::analyzeMedian());
2192 eccentricityCB->setChecked(Options::analyzeEccentricity());
2193 numStarsCB->setChecked(Options::analyzeNumStars());
2194 skyBgCB->setChecked(Options::analyzeSkyBg());
2195 snrCB->setChecked(Options::analyzeSNR());
2196 temperatureCB->setChecked(Options::analyzeTemperature());
2197 focusPositionCB->setChecked(Options::focusPosition());
2198 targetDistanceCB->setChecked(Options::analyzeTargetDistance());
2199 raCB->setChecked(Options::analyzeRA());
2200 decCB->setChecked(Options::analyzeDEC());
2201 raPulseCB->setChecked(Options::analyzeRAp());
2202 decPulseCB->setChecked(Options::analyzeDECp());
2203 driftCB->setChecked(Options::analyzeDrift());
2204 rmsCB->setChecked(Options::analyzeRMS());
2205 rmsCCB->setChecked(Options::analyzeRMSC());
2206 mountRaCB->setChecked(Options::analyzeMountRA());
2207 mountDecCB->setChecked(Options::analyzeMountDEC());
2208 mountHaCB->setChecked(Options::analyzeMountHA());
2209 azCB->setChecked(Options::analyzeAz());
2210 altCB->setChecked(Options::analyzeAlt());
2211 pierSideCB->setChecked(Options::analyzePierSide());
2214void Analyze::zoomIn()
2216 if (plotWidth > 0.5)
2218 if (keepCurrentCB->isChecked())
2220 plotStart = std::max(0.0, maxXValue - plotWidth / 4.0);
2221 else if (statsCursorTime >= 0)
2223 plotStart = std::max(0.0, statsCursorTime - plotWidth / 4.0);
2226 plotStart += plotWidth / 4.0;
2227 plotWidth = plotWidth / 2.0;
2229 fullWidthCB->setChecked(
false);
2233void Analyze::zoomOut()
2235 if (plotWidth < maxXValue)
2237 plotStart = std::max(0.0, plotStart - plotWidth / 2.0);
2238 plotWidth = plotWidth * 2;
2240 fullWidthCB->setChecked(
false);
2247void setupAxisDefaults(
QCPAxis *axis)
2265 setupAxisDefaults(plot->
yAxis);
2266 setupAxisDefaults(plot->
xAxis);
2271void Analyze::initTimelinePlot()
2273 initQCP(timelinePlot);
2277 textTicker->addTick(CAPTURE_Y,
i18n(
"Capture"));
2278 textTicker->addTick(FOCUS_Y,
i18n(
"Focus"));
2279 textTicker->addTick(ALIGN_Y,
i18n(
"Align"));
2280 textTicker->addTick(GUIDE_Y,
i18n(
"Guide"));
2281 textTicker->addTick(MERIDIAN_MOUNT_FLIP_Y,
i18n(
"Flip"));
2282 textTicker->addTick(MOUNT_Y,
i18n(
"Mount"));
2283 textTicker->addTick(SCHEDULER_Y,
i18n(
"Job"));
2284 timelinePlot->yAxis->setTicker(textTicker);
2286 ADAPTIVE_FOCUS_GRAPH = initGraph(timelinePlot, timelinePlot->yAxis,
QCPGraph::lsNone,
Qt::red,
"adaptiveFocus");
2287 timelinePlot->graph(ADAPTIVE_FOCUS_GRAPH)->setPen(
QPen(
Qt::red, 2));
2292void Analyze::toggleGraph(
int graph_id,
bool show)
2294 statsPlot->graph(graph_id)->setVisible(show);
2296 statsPlot->graph(graph_id)->addToLegend();
2298 statsPlot->graph(graph_id)->removeFromLegend();
2315 if (key ==
nullptr)
return;
2316 auto axisEntry = yAxisMap.find(key);
2317 if (axisEntry == yAxisMap.end())
2318 yAxisMap.insert(std::make_pair(key, axisInfo));
2320 axisEntry->second = axisInfo;
2323template <
typename Func>
2328 const int num = initGraph(plot, yAxis, lineStyle, color, shortName);
2331 const bool autoAxis = YAxisInfo::isRescale(yAxis->range());
2332 updateYAxisMap(out,
YAxisInfo(yAxis, yAxis->range(), autoAxis, num, plot, cb, name, shortName, color));
2347 this->toggleGraph(num, show);
2357 updateYAxisMap(key, axisInfo);
2358 statsPlot->graph(axisInfo.graphIndex)->setPen(
QPen(color));
2359 Options::setAnalyzeStatsYAxis(serializeYAxes());
2363void Analyze::userSetLeftAxis(
QCPAxis *axis)
2366 Options::setAnalyzeStatsYAxis(serializeYAxes());
2372 updateYAxisMap(key, axisInfo);
2373 Options::setAnalyzeStatsYAxis(serializeYAxes());
2378void Analyze::yAxisRangeChanged(
const QCPRange &newRange)
2381 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == activeYAxis)
2382 m_YAxisTool.replot(
true);
2385void Analyze::setLeftAxis(
QCPAxis *axis)
2387 if (axis !=
nullptr && axis != activeYAxis)
2389 for (
const auto &pair : yAxisMap)
2392 QOverload<const QCPRange &>::of(&Analyze::yAxisRangeChanged));
2393 pair.second.axis->setVisible(
false);
2399 QOverload<const QCPRange &>::of(&Analyze::yAxisRangeChanged));
2405 if (info.checkBox && !info.checkBox->isChecked())
2408 info.checkBox->setChecked(
true);
2409 statsPlot->graph(info.graphIndex)->setVisible(
true);
2410 statsPlot->graph(info.graphIndex)->addToLegend();
2413 m_YAxisTool.reset(key, info, info.axis == activeYAxis);
2417QCPAxis *Analyze::newStatsYAxis(
const QString &label,
double lower,
double upper)
2423 setupAxisDefaults(axis);
2427bool Analyze::restoreYAxes(
const QString &encoding)
2429 constexpr int headerSize = 2;
2430 constexpr int itemSize = 5;
2432 if (items.
size() <= headerSize)
return false;
2433 if ((items.
size() - headerSize) % itemSize != 0)
return false;
2434 if (items[0] !=
"AnalyzeStatsYAxis1.0")
return false;
2437 const QString leftID =
"left=";
2438 if (!items[1].startsWith(leftID))
return false;
2440 if (
left.size() <= 0)
return false;
2441 for (
const auto &pair : yAxisMap)
2443 if (pair.second.axis->label() == left)
2445 setLeftAxis(pair.second.axis);
2451 for (
int i = headerSize; i < items.
size(); i += itemSize)
2453 const QString shortName = items[i].toString();
2454 const double lower = items[i + 1].toDouble();
2455 const double upper = items[i + 2].toDouble();
2456 const bool rescale = items[i + 3] ==
"T";
2457 const QColor color(items[i + 4]);
2458 for (
auto &pair : yAxisMap)
2460 auto &info = pair.second;
2461 if (info.axis->label() == shortName)
2464 statsPlot->graph(info.graphIndex)->setPen(
QPen(color));
2465 info.rescale = rescale;
2467 info.axis->setRange(
2469 YAxisInfo::UPPER_RESCALE));
2471 info.axis->setRange(
QCPRange(lower, upper));
2480QString Analyze::serializeYAxes()
2482 QString encoding =
QString(
"AnalyzeStatsYAxis1.0,left=%1").
arg(activeYAxis->label());
2484 for (
const auto &pair : yAxisMap)
2487 const bool rescale = info.rescale;
2490 bool somethingChanged = (info.initialColor != info.color) ||
2491 (rescale != YAxisInfo::isRescale(info.initialRange)) ||
2492 (!rescale && info.axis->range() != info.initialRange);
2494 if (!somethingChanged)
continue;
2497 if (savedAxes.
contains(info.axis->label()))
continue;
2499 double lower = rescale ? YAxisInfo::LOWER_RESCALE : info.axis->range().lower;
2500 double upper = rescale ? YAxisInfo::UPPER_RESCALE : info.axis->range().upper;
2502 .
arg(info.axis->label()).
arg(lower).
arg(upper)
2503 .
arg(info.rescale ?
"T" :
"F").arg(info.color.
name()));
2504 savedAxes.
append(info.axis->label());
2509void Analyze::initStatsPlot()
2514 statsPlot->yAxis->setVisible(
true);
2515 statsPlot->yAxis->setLabel(
"RA/DEC");
2516 statsPlot->yAxis->setRange(-2, 5);
2517 setLeftAxis(statsPlot->yAxis);
2520 statsPlot->legend->setVisible(
true);
2521 statsPlot->legend->setFont(
QFont(
"Helvetica", 6));
2522 statsPlot->legend->setTextColor(
Qt::white);
2524 statsPlot->legend->setBrush(
QBrush(
QColor(0, 0, 0, 50)));
2528 statsPlot->legend->setRowSpacing(-10);
2534 statsPlot->legend->setIconSize(10, 18);
2535 statsPlot->legend->setIconTextPadding(3);
2544 if (statsPlot->legend->font().pointSize() < 6)
2547 statsPlot->legend->setRowSpacing(-10);
2548 statsPlot->legend->setIconSize(10, 18);
2549 statsPlot->legend->setFont(
QFont(
"Helvetica", 6));
2550 statsPlot->legend->setBrush(
QBrush(
QColor(0, 0, 0, 50)));
2555 statsPlot->legend->setRowSpacing(-10);
2556 statsPlot->legend->setIconSize(5, 5);
2557 statsPlot->legend->setFont(
QFont(
"Helvetica", 1));
2558 statsPlot->legend->setBrush(
QBrush(
QColor(0, 0, 0, 0)));
2560 statsPlot->replot();
2566 QCPAxis *hfrAxis = newStatsYAxis(shortName, -2, 6);
2568 Options::setAnalyzeHFR, hfrOut);
2572 if (show && !Options::autoHFR())
2573 KSNotification::info(
2574 i18n(
"The \"Auto Compute HFR\" option in the KStars "
2575 "FITS options menu is not set. You won't get HFR values "
2576 "without it. Once you set it, newly captured images "
2577 "will have their HFRs computed."));
2580 shortName =
"#SubStars";
2581 QCPAxis *numCaptureStarsAxis = newStatsYAxis(shortName);
2583 "#Stars in Capture", shortName,
2584 numCaptureStarsCB, Options::setAnalyzeNumCaptureStars, numCaptureStarsOut);
2588 if (show && !Options::autoHFR())
2589 KSNotification::info(
2590 i18n(
"The \"Auto Compute HFR\" option in the KStars "
2591 "FITS options menu is not set. You won't get # stars in capture image values "
2592 "without it. Once you set it, newly captured images "
2593 "will have their stars detected."));
2596 shortName =
"median";
2597 QCPAxis *medianAxis = newStatsYAxis(shortName);
2599 medianCB, Options::setAnalyzeMedian, medianOut);
2602 QCPAxis *eccAxis = newStatsYAxis(shortName, 0, 1.0);
2604 shortName, eccentricityCB, Options::setAnalyzeEccentricity, eccentricityOut);
2605 shortName =
"#Stars";
2606 QCPAxis *numStarsAxis = newStatsYAxis(shortName);
2608 shortName, numStarsCB, Options::setAnalyzeNumStars, numStarsOut);
2609 shortName =
"SkyBG";
2610 QCPAxis *skyBgAxis = newStatsYAxis(shortName);
2612 shortName, skyBgCB, Options::setAnalyzeSkyBg, skyBgOut);
2615 QCPAxis *temperatureAxis = newStatsYAxis(shortName, -40, 40);
2617 temperatureCB, Options::setAnalyzeTemperature, temperatureOut);
2618 shortName =
"focus";
2619 QCPAxis *focusPositionAxis = newStatsYAxis(shortName);
2621 focusPositionCB, Options::setFocusPosition, focusPositionOut);
2622 shortName =
"tDist";
2623 QCPAxis *targetDistanceAxis = newStatsYAxis(shortName, 0, 60);
2624 TARGET_DISTANCE_GRAPH = initGraphAndCB(statsPlot, targetDistanceAxis,
QCPGraph::lsLine,
2626 "Distance to Target (arcsec)", shortName, targetDistanceCB, Options::setAnalyzeTargetDistance, targetDistanceOut);
2628 QCPAxis *snrAxis = newStatsYAxis(shortName, -100, 100);
2630 Options::setAnalyzeSNR, snrOut);
2633 RA_GRAPH = initGraphAndCB(statsPlot, statsPlot->yAxis,
QCPGraph::lsLine, raColor,
"Guider RA Drift", shortName, raCB,
2634 Options::setAnalyzeRA, raOut);
2637 DEC_GRAPH = initGraphAndCB(statsPlot, statsPlot->yAxis,
QCPGraph::lsLine, decColor,
"Guider DEC Drift", shortName, decCB,
2638 Options::setAnalyzeDEC, decOut);
2642 QCPAxis *pulseAxis = newStatsYAxis(shortName, -2 * 150, 5 * 150);
2643 RA_PULSE_GRAPH = initGraphAndCB(statsPlot, pulseAxis,
QCPGraph::lsLine, raPulseColor,
"RA Correction Pulse (ms)", shortName,
2644 raPulseCB, Options::setAnalyzeRAp, raPulseOut);
2650 DEC_PULSE_GRAPH = initGraphAndCB(statsPlot, pulseAxis,
QCPGraph::lsLine, decPulseColor,
"DEC Correction Pulse (ms)",
2651 shortName, decPulseCB, Options::setAnalyzeDECp, decPulseOut);
2654 shortName =
"Drift";
2656 shortName, driftCB, Options::setAnalyzeDrift, driftOut);
2658 RMS_GRAPH = initGraphAndCB(statsPlot, statsPlot->yAxis,
QCPGraph::lsLine,
Qt::red,
"Guider RMS Drift", shortName, rmsCB,
2659 Options::setAnalyzeRMS, rmsOut);
2662 "Guider RMS Drift (during capture)", shortName, rmsCCB,
2663 Options::setAnalyzeRMSC, rmsCOut);
2664 shortName =
"MOUNT_RA";
2665 QCPAxis *mountRaDecAxis = newStatsYAxis(shortName, -10, 370);
2667 MOUNT_RA_GRAPH = initGraphAndCB(statsPlot, mountRaDecAxis,
QCPGraph::lsLine,
Qt::red,
"Mount RA Degrees", shortName,
2668 mountRaCB, Options::setAnalyzeMountRA, mountRaOut);
2669 shortName =
"MOUNT_DEC";
2670 MOUNT_DEC_GRAPH = initGraphAndCB(statsPlot, mountRaDecAxis,
QCPGraph::lsLine,
Qt::red,
"Mount DEC Degrees", shortName,
2671 mountDecCB, Options::setAnalyzeMountDEC, mountDecOut);
2672 shortName =
"MOUNT_HA";
2673 MOUNT_HA_GRAPH = initGraphAndCB(statsPlot, mountRaDecAxis,
QCPGraph::lsLine,
Qt::red,
"Mount Hour Angle", shortName,
2674 mountHaCB, Options::setAnalyzeMountHA, mountHaOut);
2676 QCPAxis *azAxis = newStatsYAxis(shortName, -10, 370);
2678 Options::setAnalyzeAz, azOut);
2680 QCPAxis *altAxis = newStatsYAxis(shortName, 0, 90);
2682 Options::setAnalyzeAlt, altOut);
2683 shortName =
"PierSide";
2684 QCPAxis *pierSideAxis = newStatsYAxis(shortName, -2, 2);
2686 pierSideCB, Options::setAnalyzePierSide, pierSideOut);
2689 statsPlot->setMouseTracking(
false);
2692 dateTicker.reset(
new OffsetDateTimeTicker);
2693 dateTicker->setDateTimeFormat(
"hh:mm:ss");
2694 statsPlot->xAxis->setTicker(dateTicker);
2699 restoreYAxes(Options::analyzeStatsYAxis());
2703void Analyze::reset()
2709 guiderRms->resetFilter();
2710 captureRms->resetFilter();
2712 unhighlightTimelineItem();
2714 for (
int i = 0; i < statsPlot->graphCount(); ++i)
2715 statsPlot->graph(i)->data()->clear();
2716 statsPlot->clearItems();
2718 for (
int i = 0; i < timelinePlot->graphCount(); ++i)
2719 timelinePlot->graph(i)->data()->clear();
2720 timelinePlot->clearItems();
2722 resetGraphicsPlot();
2724 detailsTable->clear();
2725 QPalette p = detailsTable->palette();
2728 detailsTable->setPalette(p);
2730 inputValue->clear();
2732 captureSessions.clear();
2733 focusSessions.clear();
2734 guideSessions.clear();
2735 mountSessions.clear();
2736 alignSessions.clear();
2737 mountFlipSessions.clear();
2738 schedulerJobSessions.clear();
2740 numStarsOut->setText(
"");
2741 skyBgOut->setText(
"");
2742 snrOut->setText(
"");
2743 temperatureOut->setText(
"");
2744 focusPositionOut->setText(
"");
2745 targetDistanceOut->setText(
"");
2746 eccentricityOut->setText(
"");
2747 medianOut->setText(
"");
2748 numCaptureStarsOut->setText(
"");
2751 decOut->setText(
"");
2752 driftOut->setText(
"");
2753 rmsOut->setText(
"");
2754 rmsCOut->setText(
"");
2756 removeStatsCursor();
2757 removeTemporarySessions();
2759 resetCaptureState();
2760 resetAutofocusState();
2766 resetMountFlipState();
2767 resetSchedulerJob();
2772void Analyze::initGraphicsPlot()
2774 initQCP(graphicsPlot);
2775 FOCUS_GRAPHICS = initGraph(graphicsPlot, graphicsPlot->yAxis,
2777 graphicsPlot->graph(FOCUS_GRAPHICS)->setScatterStyle(
2779 errorBars =
new QCPErrorBars(graphicsPlot->xAxis, graphicsPlot->yAxis);
2780 errorBars->setAntialiased(
false);
2781 errorBars->setDataPlottable(graphicsPlot->graph(FOCUS_GRAPHICS));
2782 errorBars->setPen(
QPen(
QColor(180, 180, 180)));
2784 FOCUS_GRAPHICS_FINAL = initGraph(graphicsPlot, graphicsPlot->yAxis,
2786 graphicsPlot->graph(FOCUS_GRAPHICS_FINAL)->setScatterStyle(
2788 finalErrorBars =
new QCPErrorBars(graphicsPlot->xAxis, graphicsPlot->yAxis);
2789 finalErrorBars->setAntialiased(
false);
2790 finalErrorBars->setDataPlottable(graphicsPlot->graph(FOCUS_GRAPHICS_FINAL));
2791 finalErrorBars->setPen(
QPen(
QColor(180, 180, 180)));
2793 FOCUS_GRAPHICS_CURVE = initGraph(graphicsPlot, graphicsPlot->yAxis,
2798 GUIDER_GRAPHICS = initGraph(graphicsPlot, graphicsPlot->yAxis,
2800 graphicsPlot->graph(GUIDER_GRAPHICS)->setScatterStyle(
2807 resetGraphicsPlot();
2808 auto graph = graphicsPlot->graph(FOCUS_GRAPHICS);
2809 auto finalGraph = graphicsPlot->graph(FOCUS_GRAPHICS_FINAL);
2810 double maxHfr = -1e8, maxPosition = -1e8, minHfr = 1e8, minPosition = 1e8;
2812 for (
int i = 0; i < positions.
size(); ++i)
2815 if (success && i == positions.
size() - 1)
2817 finalGraph->addData(positions[i], hfrs[i]);
2821 double sd = (weights[i] <= 0.0) ? 0.0 : std::pow(weights[i], -0.5);
2827 graph->
addData(positions[i], hfrs[i]);
2830 double sd = (weights[i] <= 0.0) ? 0.0 : std::pow(weights[i], -0.5);
2834 maxHfr = std::max(maxHfr, hfrs[i]);
2835 minHfr = std::min(minHfr, hfrs[i]);
2836 maxPosition = std::max(maxPosition, positions[i]);
2837 minPosition = std::min(minPosition, positions[i]);
2840 for (
int i = 0; i < positions.
size(); ++i)
2845 textLabel->position->
setCoords(positions[i], hfrs[i]);
2862 errorBars->setVisible(useWeights);
2863 finalErrorBars->setVisible(useWeights);
2866 errorBars->setData(errorData);
2867 finalErrorBars->setData(finalErrorData);
2870 const double xRange = maxPosition - minPosition;
2871 const double xPadding = hfrs.
size() > 1 ? xRange / (hfrs.
size() - 1.0) : 10;
2874 if (curve.
size() > 0)
2876 CurveFitting curveFitting(curve);
2877 const double interval = xRange / 20.0;
2878 auto curveGraph = graphicsPlot->graph(FOCUS_GRAPHICS_CURVE);
2879 for (
double x = minPosition - xPadding ; x <= maxPosition + xPadding; x += interval)
2880 curveGraph->addData(x, curveFitting.f(x));
2884 plotTitle->setColor(
QColor(255, 255, 255));
2887 plotTitle->position->setCoords(0.5, 0);
2888 plotTitle->setFont(
QFont(font().family(), 10));
2889 plotTitle->setVisible(
true);
2890 plotTitle->setText(title);
2893 const double upper = 1.5 * maxHfr;
2894 const double lower = minHfr - (0.25 * (upper - minHfr));
2895 graphicsPlot->xAxis->setRange(minPosition - xPadding, maxPosition + xPadding);
2896 graphicsPlot->yAxis->setRange(lower, upper);
2897 graphicsPlot->replot();
2900void Analyze::resetGraphicsPlot()
2902 for (
int i = 0; i < graphicsPlot->graphCount(); ++i)
2903 graphicsPlot->graph(i)->data()->clear();
2904 graphicsPlot->clearItems();
2905 errorBars->data().clear();
2906 finalErrorBars->data().clear();
2909void Analyze::displayFITS(
const QString &filename)
2913 if (fitsViewer.isNull())
2916 fitsViewerTabID = fitsViewer->loadFile(url);
2917 connect(fitsViewer.get(), &FITSViewer::terminated,
this, [
this]()
2924 if (fitsViewer->tabExists(fitsViewerTabID))
2925 fitsViewer->updateFile(url, fitsViewerTabID);
2927 fitsViewerTabID = fitsViewer->loadFile(url);
2933void Analyze::helpMessage()
2945double Analyze::logTime(
const QDateTime &time)
2947 if (!logInitialized)
2949 return (time.
toMSecsSinceEpoch() - analyzeStartTime.toMSecsSinceEpoch()) / 1000.0;
2955double Analyze::logTime()
2962QDateTime Analyze::clockTime(
double logSeconds)
2964 return displayStartTime.
addMSecs(logSeconds * 1000.0);
2969void Analyze::saveMessage(
const QString &type,
const QString &message)
2974 .arg(message.
size() > 0 ?
"," :
"", message));
2979void Analyze::restart()
2981 qCDebug(KSTARS_EKOS_ANALYZE) <<
"(Re)starting Analyze";
2988 inputCombo->setCurrentIndex(0);
2989 inputValue->setText(
"");
2990 maxXValue = readDataFromFile(logFilename);
2991 runtimeDisplay =
true;
2992 fullWidthCB->setChecked(
true);
2993 fullWidthCB->setVisible(
true);
2994 fullWidthCB->setDisabled(
false);
2999void Analyze::startLog()
3002 startTimeInitialized =
true;
3004 displayStartTime = analyzeStartTime;
3009 logFile.reset(
new QFile);
3011 logFile->setFileName(logFilename);
3015 logInitialized =
true;
3017 appendToLog(
QString(
"#KStars version %1. Analyze log version 1.0.\n\n")
3018 .arg(KSTARS_VERSION));
3019 appendToLog(
QString(
"%1,%2,%3\n")
3020 .arg(
"AnalyzeStartTime", analyzeStartTime.toString(timeFormat), analyzeStartTime.timeZoneAbbreviation()));
3023void Analyze::appendToLog(
const QString &lines)
3025 if (!logInitialized)
3033void Analyze::updateMaxX(
double time)
3035 maxXValue = std::max(time, maxXValue);
3043void Analyze::removeTemporarySession(
Session * session)
3045 if (session->rect !=
nullptr)
3046 timelinePlot->removeItem(session->rect);
3047 session->rect =
nullptr;
3053void Analyze::removeTemporarySessions()
3055 removeTemporarySession(&temporaryCaptureSession);
3056 removeTemporarySession(&temporaryMountFlipSession);
3057 removeTemporarySession(&temporaryFocusSession);
3058 removeTemporarySession(&temporaryGuideSession);
3059 removeTemporarySession(&temporaryMountSession);
3060 removeTemporarySession(&temporaryAlignSession);
3061 removeTemporarySession(&temporarySchedulerJobSession);
3065void Analyze::addTemporarySession(
Session * session,
double time,
double duration,
3066 int y_offset,
const QBrush &brush)
3068 if (time < 0)
return;
3069 removeTemporarySession(session);
3070 session->rect = addSession(time, time + duration, y_offset, brush);
3071 session->start = time;
3072 session->end = time + duration;
3073 session->offset = y_offset;
3074 session->temporaryBrush = brush;
3075 updateMaxX(time + duration);
3081void Analyze::adjustTemporarySession(
Session * session)
3083 if (session->rect !=
nullptr && session->end < maxXValue)
3085 QBrush brush = session->temporaryBrush;
3086 double start = session->start;
3087 int offset = session->offset;
3088 addTemporarySession(session, start, maxXValue - start, offset, brush);
3093void Analyze::adjustTemporarySessions()
3095 adjustTemporarySession(&temporaryCaptureSession);
3096 adjustTemporarySession(&temporaryMountFlipSession);
3097 adjustTemporarySession(&temporaryFocusSession);
3098 adjustTemporarySession(&temporaryGuideSession);
3099 adjustTemporarySession(&temporaryMountSession);
3100 adjustTemporarySession(&temporaryAlignSession);
3101 adjustTemporarySession(&temporarySchedulerJobSession);
3106void Analyze::captureStarting(
double exposureSeconds,
const QString &filter)
3108 saveMessage(
"CaptureStarting",
3110 processCaptureStarting(logTime(), exposureSeconds, filter);
3115void Analyze::processCaptureStarting(
double time,
double exposureSeconds,
const QString &filter)
3117 captureStartedTime = time;
3118 captureStartedFilter =
filter;
3121 addTemporarySession(&temporaryCaptureSession, time, 1, CAPTURE_Y, temporaryBrush);
3122 temporaryCaptureSession.duration = exposureSeconds;
3123 temporaryCaptureSession.filter =
filter;
3127void Analyze::captureComplete(
const QVariantMap &metadata)
3129 auto filename = metadata[
"filename"].toString();
3130 auto exposure = metadata[
"exposure"].toDouble();
3131 auto filter = metadata[
"filter"].toString();
3132 auto hfr = metadata[
"hfr"].toDouble();
3133 auto starCount = metadata[
"starCount"].toInt();
3134 auto median = metadata[
"median"].toDouble();
3135 auto eccentricity = metadata[
"eccentricity"].toDouble();
3137 saveMessage(
"CaptureComplete",
3138 QString(
"%1,%2,%3,%4,%5,%6,%7")
3143 if (runtimeDisplay && captureStartedTime >= 0)
3144 processCaptureComplete(logTime(), filename, exposure, filter, hfr, starCount, median, eccentricity);
3147void Analyze::processCaptureComplete(
double time,
const QString &filename,
3148 double exposureSeconds,
const QString &filter,
double hfr,
3149 int numStars,
int median,
double eccentricity,
bool batchMode)
3151 removeTemporarySession(&temporaryCaptureSession);
3153 if (captureStartedTime < 0)
3156 if (filterStripeBrush(filter, &stripe))
3157 addSession(captureStartedTime, time, CAPTURE_Y, successBrush, &stripe);
3159 addSession(captureStartedTime, time, CAPTURE_Y, successBrush,
nullptr);
3160 auto session = CaptureSession(captureStartedTime, time,
nullptr,
false,
3161 filename, exposureSeconds, filter);
3162 captureSessions.add(session);
3163 addHFR(hfr, numStars, median, eccentricity, time, captureStartedTime);
3167 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3168 captureSessionClicked(session,
false);
3171 previousCaptureStartedTime = captureStartedTime;
3172 previousCaptureCompletedTime = time;
3173 captureStartedTime = -1;
3176void Analyze::captureAborted(
double exposureSeconds)
3178 saveMessage(
"CaptureAborted",
3180 if (runtimeDisplay && captureStartedTime >= 0)
3181 processCaptureAborted(logTime(), exposureSeconds);
3184void Analyze::processCaptureAborted(
double time,
double exposureSeconds,
bool batchMode)
3186 removeTemporarySession(&temporaryCaptureSession);
3187 double duration = time - captureStartedTime;
3188 if (captureStartedTime >= 0 &&
3189 duration < (exposureSeconds + 30) &&
3194 addSession(captureStartedTime, time, CAPTURE_Y, failureBrush);
3195 auto session = CaptureSession(captureStartedTime, time,
nullptr,
true,
"",
3196 exposureSeconds, captureStartedFilter);
3197 captureSessions.add(session);
3201 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3202 captureSessionClicked(session,
false);
3205 captureStartedTime = -1;
3207 previousCaptureStartedTime = -1;
3208 previousCaptureCompletedTime = -1;
3211void Analyze::resetCaptureState()
3213 captureStartedTime = -1;
3214 captureStartedFilter =
"";
3216 numCaptureStarsMax = 1;
3217 previousCaptureStartedTime = -1;
3218 previousCaptureCompletedTime = -1;
3221void Analyze::autofocusStarting(
double temperature,
const QString &filter,
const AutofocusReason reason,
3224 saveMessage(
"AutofocusStarting",
3230 processAutofocusStarting(logTime(), temperature, filter, reason, reasonInfo);
3233void Analyze::processAutofocusStarting(
double time,
double temperature,
const QString &filter,
const AutofocusReason reason,
3236 autofocusStartedTime = time;
3237 autofocusStartedFilter =
filter;
3238 autofocusStartedTemperature = temperature;
3239 autofocusStartedReason = reason;
3240 autofocusStartedReasonInfo = reasonInfo;
3242 addTemperature(temperature, time);
3245 addTemporarySession(&temporaryFocusSession, time, 1, FOCUS_Y, temporaryBrush);
3246 temporaryFocusSession.temperature = temperature;
3247 temporaryFocusSession.filter =
filter;
3248 temporaryFocusSession.reason = reason;
3251void Analyze::adaptiveFocusComplete(
const QString &filter,
double temperature,
double tempTicks,
3252 double altitude,
double altTicks,
int prevPosError,
int thisPosError,
3253 int totalTicks,
int position,
bool focuserMoved)
3255 saveMessage(
"AdaptiveFocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8,%9,%10").arg(filter).arg(temperature, 0,
'f', 2)
3256 .arg(tempTicks, 0,
'f', 2).arg(altitude, 0,
'f', 2).arg(altTicks, 0,
'f', 2).arg(prevPosError)
3257 .arg(thisPosError).arg(totalTicks).arg(position).arg(focuserMoved ? 1 : 0));
3260 processAdaptiveFocusComplete(logTime(), filter, temperature, tempTicks, altitude, altTicks, prevPosError, thisPosError,
3261 totalTicks, position, focuserMoved);
3264void Analyze::processAdaptiveFocusComplete(
double time,
const QString &filter,
double temperature,
double tempTicks,
3265 double altitude,
double altTicks,
int prevPosError,
int thisPosError,
int totalTicks,
int position,
3266 bool focuserMoved,
bool batchMode)
3268 removeTemporarySession(&temporaryFocusSession);
3270 addFocusPosition(position, time);
3275 if (!focuserMoved || (abs(tempTicks) < 1.00 && abs(altTicks) < 1.0 && prevPosError == 0 && thisPosError == 0))
3279 timelinePlot->graph(ADAPTIVE_FOCUS_GRAPH)->addData(time, FOCUS_Y);
3282 constexpr int artificialInterval = 10;
3283 auto session = FocusSession(time - artificialInterval, time + artificialInterval,
nullptr,
3284 filter, temperature, tempTicks, altitude, altTicks, prevPosError, thisPosError, totalTicks,
3286 focusSessions.add(session);
3291 autofocusStartedTime = -1;
3294void Analyze::autofocusComplete(
const double temperature,
const QString &filter,
const QString &points,
3295 const bool useWeights,
const QString &curve,
const QString &rawTitle)
3310 QVariant reasonV = autofocusStartedReason;
3312 QString reasonInfo = autofocusStartedReasonInfo;
3314 if (curve.
size() == 0)
3315 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6").arg(temp, reason, reasonInfo, filter, points, weights));
3316 else if (title.
size() == 0)
3317 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7").arg(temp, reason, reasonInfo, filter, points, weights,
3320 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8").arg(temp, reason, reasonInfo, filter, points, weights,
3323 if (runtimeDisplay && autofocusStartedTime >= 0)
3324 processAutofocusCompleteV2(logTime(), temperature, filter, autofocusStartedReason, reasonInfo, points, useWeights, curve,
3329void Analyze::processAutofocusCompleteV2(
double time,
const double temperature,
const QString &filter,
3330 const AutofocusReason reason,
const QString &reasonInfo,
3331 const QString &points,
const bool useWeights,
const QString &curve,
const QString &title,
bool batchMode)
3333 removeTemporarySession(&temporaryFocusSession);
3335 if (autofocusStartedTime >= 0)
3338 if (filterStripeBrush(filter, &stripe))
3339 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush, &stripe);
3341 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush,
nullptr);
3343 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
true, temperature, filter, reason, reasonInfo, points,
3344 useWeights, curve, title, AutofocusFailReason::FOCUS_FAIL_NONE,
"");
3345 focusSessions.add(session);
3346 addFocusPosition(session.focusPosition(), autofocusStartedTime);
3349 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3350 focusSessionClicked(session,
false);
3354 autofocusStartedTime = -1;
3358void Analyze::processAutofocusComplete(
double time,
const QString &filter,
const QString &points,
3361 removeTemporarySession(&temporaryFocusSession);
3362 if (autofocusStartedTime < 0)
3366 if (filterStripeBrush(filter, &stripe))
3367 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush, &stripe);
3369 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush,
nullptr);
3370 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
true,
3371 autofocusStartedTemperature, filter, points, curve, title);
3372 focusSessions.add(session);
3373 addFocusPosition(session.focusPosition(), autofocusStartedTime);
3377 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3378 focusSessionClicked(session,
false);
3381 autofocusStartedTime = -1;
3384void Analyze::autofocusAborted(
const QString &filter,
const QString &points,
const bool useWeights,
3385 const AutofocusFailReason failCode,
const QString failCodeInfo)
3388 QVariant reasonV = autofocusStartedReason;
3390 QString reasonInfo = autofocusStartedReasonInfo;
3392 QVariant failReasonV =
static_cast<int>(failCode);
3394 saveMessage(
"AutofocusAborted",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8").arg(temperature, reason, reasonInfo, filter, points,
3395 weights, failReason, failCodeInfo));
3396 if (runtimeDisplay && autofocusStartedTime >= 0)
3397 processAutofocusAbortedV2(logTime(), autofocusStartedTemperature, filter, autofocusStartedReason, reasonInfo, points,
3398 useWeights, failCode, failCodeInfo);
3402void Analyze::processAutofocusAbortedV2(
double time,
double temperature,
const QString &filter,
3403 const AutofocusReason reason,
const QString &reasonInfo,
const QString &points,
const bool useWeights,
3404 const AutofocusFailReason failCode,
const QString failCodeInfo,
bool batchMode)
3406 Q_UNUSED(temperature);
3407 removeTemporarySession(&temporaryFocusSession);
3408 double duration = time - autofocusStartedTime;
3409 if (autofocusStartedTime >= 0 && duration < 1000)
3412 addSession(autofocusStartedTime, time, FOCUS_Y, failureBrush);
3413 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
false, autofocusStartedTemperature, filter, reason,
3414 reasonInfo, points, useWeights,
"",
"", failCode, failCodeInfo);
3415 focusSessions.add(session);
3419 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3420 focusSessionClicked(session,
false);
3423 autofocusStartedTime = -1;
3428void Analyze::processAutofocusAborted(
double time,
const QString &filter,
const QString &points,
bool batchMode)
3430 removeTemporarySession(&temporaryFocusSession);
3431 double duration = time - autofocusStartedTime;
3432 if (autofocusStartedTime >= 0 && duration < 1000)
3435 addSession(autofocusStartedTime, time, FOCUS_Y, failureBrush);
3436 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
false,
3437 autofocusStartedTemperature, filter, points,
"",
"");
3438 focusSessions.add(session);
3442 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3443 focusSessionClicked(session,
false);
3446 autofocusStartedTime = -1;
3450void Analyze::resetAutofocusState()
3452 autofocusStartedTime = -1;
3453 autofocusStartedFilter =
"";
3454 autofocusStartedTemperature = 0;
3455 autofocusStartedReason = AutofocusReason::FOCUS_NONE;
3456 autofocusStartedReasonInfo =
"";
3463Ekos::GuideState stringToGuideState(
const QString &str)
3465 if (str ==
i18n(
"Idle"))
3467 else if (str ==
i18n(
"Aborted"))
3468 return GUIDE_ABORTED;
3469 else if (str ==
i18n(
"Connected"))
3470 return GUIDE_CONNECTED;
3471 else if (str ==
i18n(
"Disconnected"))
3472 return GUIDE_DISCONNECTED;
3473 else if (str ==
i18n(
"Capturing"))
3474 return GUIDE_CAPTURE;
3475 else if (str ==
i18n(
"Looping"))
3476 return GUIDE_LOOPING;
3477 else if (str ==
i18n(
"Subtracting"))
3479 else if (str ==
i18n(
"Subframing"))
3480 return GUIDE_SUBFRAME;
3481 else if (str ==
i18n(
"Selecting star"))
3482 return GUIDE_STAR_SELECT;
3483 else if (str ==
i18n(
"Calibrating"))
3484 return GUIDE_CALIBRATING;
3485 else if (str ==
i18n(
"Calibration error"))
3486 return GUIDE_CALIBRATION_ERROR;
3487 else if (str ==
i18n(
"Calibrated"))
3488 return GUIDE_CALIBRATION_SUCCESS;
3489 else if (str ==
i18n(
"Guiding"))
3490 return GUIDE_GUIDING;
3491 else if (str ==
i18n(
"Suspended"))
3492 return GUIDE_SUSPENDED;
3493 else if (str ==
i18n(
"Reacquiring"))
3494 return GUIDE_REACQUIRE;
3495 else if (str ==
i18n(
"Dithering"))
3496 return GUIDE_DITHERING;
3497 else if (str ==
i18n(
"Manual Dithering"))
3498 return GUIDE_MANUAL_DITHERING;
3499 else if (str ==
i18n(
"Dithering error"))
3500 return GUIDE_DITHERING_ERROR;
3501 else if (str ==
i18n(
"Dithering successful"))
3502 return GUIDE_DITHERING_SUCCESS;
3503 else if (str ==
i18n(
"Settling"))
3504 return GUIDE_DITHERING_SETTLE;
3509Analyze::SimpleGuideState convertGuideState(Ekos::GuideState state)
3515 case GUIDE_CONNECTED:
3516 case GUIDE_DISCONNECTED:
3518 return Analyze::G_IDLE;
3520 return Analyze::G_GUIDING;
3523 case GUIDE_SUBFRAME:
3524 case GUIDE_STAR_SELECT:
3525 return Analyze::G_IGNORE;
3526 case GUIDE_CALIBRATING:
3527 case GUIDE_CALIBRATION_ERROR:
3528 case GUIDE_CALIBRATION_SUCCESS:
3529 return Analyze::G_CALIBRATING;
3530 case GUIDE_SUSPENDED:
3531 case GUIDE_REACQUIRE:
3532 return Analyze::G_SUSPENDED;
3533 case GUIDE_DITHERING:
3534 case GUIDE_MANUAL_DITHERING:
3535 case GUIDE_DITHERING_ERROR:
3536 case GUIDE_DITHERING_SUCCESS:
3537 case GUIDE_DITHERING_SETTLE:
3538 return Analyze::G_DITHERING;
3541 return Analyze::G_IDLE;
3544const QBrush guideBrush(Analyze::SimpleGuideState simpleState)
3546 switch (simpleState)
3548 case Analyze::G_IDLE:
3549 case Analyze::G_IGNORE:
3552 case Analyze::G_GUIDING:
3553 return successBrush;
3554 case Analyze::G_CALIBRATING:
3555 return progressBrush;
3556 case Analyze::G_SUSPENDED:
3557 return stoppedBrush;
3558 case Analyze::G_DITHERING:
3559 return progress2Brush;
3567void Analyze::guideState(Ekos::GuideState state)
3569 QString str = getGuideStatusString(state);
3570 saveMessage(
"GuideState", str);
3572 processGuideState(logTime(), str);
3575void Analyze::processGuideState(
double time,
const QString &stateStr,
bool batchMode)
3577 Ekos::GuideState gstate = stringToGuideState(stateStr);
3578 SimpleGuideState state = convertGuideState(gstate);
3579 if (state == G_IGNORE)
3581 if (state == lastGuideStateStarted)
3584 if (guideStateStartedTime >= 0)
3586 if (lastGuideStateStarted != G_IDLE)
3589 addSession(guideStateStartedTime, time, GUIDE_Y, guideBrush(lastGuideStateStarted));
3590 guideSessions.add(GuideSession(guideStateStartedTime, time,
nullptr, lastGuideStateStarted));
3593 if (state == G_GUIDING)
3595 addTemporarySession(&temporaryGuideSession, time, 1, GUIDE_Y, successBrush);
3596 temporaryGuideSession.simpleState = state;
3599 removeTemporarySession(&temporaryGuideSession);
3601 guideStateStartedTime = time;
3602 lastGuideStateStarted = state;
3608void Analyze::resetGuideState()
3610 lastGuideStateStarted = G_IDLE;
3611 guideStateStartedTime = -1;
3614void Analyze::newTemperature(
double temperatureDelta,
double temperature)
3616 Q_UNUSED(temperatureDelta);
3617 if (temperature > -200 && temperature != lastTemperature)
3620 lastTemperature = temperature;
3622 processTemperature(logTime(), temperature);
3626void Analyze::processTemperature(
double time,
double temperature,
bool batchMode)
3628 addTemperature(temperature, time);
3634void Analyze::resetTemperature()
3636 lastTemperature = -1000;
3639void Analyze::newTargetDistance(
double targetDistance)
3643 processTargetDistance(logTime(), targetDistance);
3646void Analyze::processTargetDistance(
double time,
double targetDistance,
bool batchMode)
3648 addTargetDistance(targetDistance, time);
3654void Analyze::guideStats(
double raError,
double decError,
int raPulse,
int decPulse,
3655 double snr,
double skyBg,
int numStars)
3657 saveMessage(
"GuideStats",
QString(
"%1,%2,%3,%4,%5,%6,%7")
3665 processGuideStats(logTime(), raError, decError, raPulse, decPulse, snr, skyBg, numStars);
3668void Analyze::processGuideStats(
double time,
double raError,
double decError,
3669 int raPulse,
int decPulse,
double snr,
double skyBg,
int numStars,
bool batchMode)
3671 addGuideStats(raError, decError, raPulse, decPulse, snr, numStars, skyBg, time);
3677void Analyze::resetGuideStats()
3679 lastGuideStatsTime = -1;
3680 lastCaptureRmsTime = -1;
3690AlignState convertAlignState(
const QString &str)
3692 for (
int i = 0; i < alignStates.size(); ++i)
3694 if (str == alignStates[i].
toString())
3695 return static_cast<AlignState
>(i);
3700const QBrush alignBrush(AlignState state)
3708 return successBrush;
3710 return failureBrush;
3712 return progress3Brush;
3714 return progress2Brush;
3716 return progressBrush;
3718 return progress4Brush;
3720 return failureBrush;
3729void Analyze::alignState(AlignState state)
3731 if (state == lastAlignStateReceived)
3733 lastAlignStateReceived = state;
3735 QString stateStr = getAlignStatusString(state);
3736 saveMessage(
"AlignState", stateStr);
3738 processAlignState(logTime(), stateStr);
3742void Analyze::processAlignState(
double time,
const QString &statusString,
bool batchMode)
3744 AlignState state = convertAlignState(statusString);
3746 if (state == lastAlignStateStarted)
3749 bool lastStateInteresting = (lastAlignStateStarted ==
ALIGN_PROGRESS ||
3752 if (lastAlignStateStartedTime >= 0 && lastStateInteresting)
3754 if (state == ALIGN_COMPLETE || state == ALIGN_FAILED || state == ALIGN_ABORTED)
3757 addSession(lastAlignStateStartedTime, time, ALIGN_Y, alignBrush(state));
3758 alignSessions.add(AlignSession(lastAlignStateStartedTime, time,
nullptr, state));
3762 addSession(lastAlignStateStartedTime, time, ALIGN_Y, alignBrush(lastAlignStateStarted));
3763 alignSessions.add(AlignSession(lastAlignStateStartedTime, time,
nullptr, lastAlignStateStarted));
3768 if (stateInteresting)
3770 addTemporarySession(&temporaryAlignSession, time, 1, ALIGN_Y, temporaryBrush);
3771 temporaryAlignSession.state = state;
3774 removeTemporarySession(&temporaryAlignSession);
3776 lastAlignStateStartedTime = time;
3777 lastAlignStateStarted = state;
3784void Analyze::resetAlignState()
3788 lastAlignStateStartedTime = -1;
3794const QBrush mountBrush(ISD::Mount::Status state)
3798 case ISD::Mount::MOUNT_IDLE:
3800 case ISD::Mount::MOUNT_ERROR:
3801 return failureBrush;
3802 case ISD::Mount::MOUNT_MOVING:
3803 case ISD::Mount::MOUNT_SLEWING:
3804 return progressBrush;
3805 case ISD::Mount::MOUNT_TRACKING:
3806 return successBrush;
3807 case ISD::Mount::MOUNT_PARKING:
3808 return stoppedBrush;
3809 case ISD::Mount::MOUNT_PARKED:
3810 return stopped2Brush;
3820void Analyze::mountState(ISD::Mount::Status state)
3822 QString statusString = mountStatusString(state);
3823 saveMessage(
"MountState", statusString);
3825 processMountState(logTime(), statusString);
3828void Analyze::processMountState(
double time,
const QString &statusString,
bool batchMode)
3830 ISD::Mount::Status state = toMountStatus(statusString);
3831 if (mountStateStartedTime >= 0 && lastMountState != ISD::Mount::MOUNT_IDLE)
3833 addSession(mountStateStartedTime, time, MOUNT_Y, mountBrush(lastMountState));
3834 mountSessions.add(MountSession(mountStateStartedTime, time,
nullptr, lastMountState));
3837 if (state != ISD::Mount::MOUNT_IDLE)
3839 addTemporarySession(&temporaryMountSession, time, 1, MOUNT_Y,
3840 (state == ISD::Mount::MOUNT_TRACKING) ? successBrush : temporaryBrush);
3841 temporaryMountSession.state = state;
3844 removeTemporarySession(&temporaryMountSession);
3846 mountStateStartedTime = time;
3847 lastMountState = state;
3853void Analyze::resetMountState()
3855 mountStateStartedTime = -1;
3856 lastMountState = ISD::Mount::Status::MOUNT_IDLE;
3860void Analyze::mountCoords(
const SkyPoint &position, ISD::Mount::PierSide pierSide,
const dms &haValue)
3864 double ha = haValue.
Degrees();
3869 constexpr double MIN_DEGREES_CHANGE = 0.25;
3870 if ((fabs(ra - lastMountRa) > MIN_DEGREES_CHANGE) ||
3871 (fabs(dec - lastMountDec) > MIN_DEGREES_CHANGE) ||
3872 (fabs(ha - lastMountHa) > MIN_DEGREES_CHANGE) ||
3873 (fabs(az - lastMountAz) > MIN_DEGREES_CHANGE) ||
3874 (fabs(alt - lastMountAlt) > MIN_DEGREES_CHANGE) ||
3875 (pierSide != lastMountPierSide))
3877 saveMessage(
"MountCoords",
QString(
"%1,%2,%3,%4,%5,%6")
3884 processMountCoords(logTime(), ra, dec, az, alt, pierSide, ha);
3891 lastMountPierSide = pierSide;
3895void Analyze::processMountCoords(
double time,
double ra,
double dec,
double az,
3896 double alt,
int pierSide,
double ha,
bool batchMode)
3898 addMountCoords(ra, dec, az, alt, pierSide, ha, time);
3904void Analyze::resetMountCoords()
3911 lastMountPierSide = -1;
3918MeridianFlipState::MeridianFlipMountState convertMountFlipState(
const QString &statusStr)
3920 if (statusStr ==
"MOUNT_FLIP_NONE")
3921 return MeridianFlipState::MOUNT_FLIP_NONE;
3922 else if (statusStr ==
"MOUNT_FLIP_PLANNED")
3923 return MeridianFlipState::MOUNT_FLIP_PLANNED;
3924 else if (statusStr ==
"MOUNT_FLIP_WAITING")
3925 return MeridianFlipState::MOUNT_FLIP_WAITING;
3926 else if (statusStr ==
"MOUNT_FLIP_ACCEPTED")
3927 return MeridianFlipState::MOUNT_FLIP_ACCEPTED;
3928 else if (statusStr ==
"MOUNT_FLIP_RUNNING")
3929 return MeridianFlipState::MOUNT_FLIP_RUNNING;
3930 else if (statusStr ==
"MOUNT_FLIP_COMPLETED")
3931 return MeridianFlipState::MOUNT_FLIP_COMPLETED;
3932 else if (statusStr ==
"MOUNT_FLIP_ERROR")
3933 return MeridianFlipState::MOUNT_FLIP_ERROR;
3934 return MeridianFlipState::MOUNT_FLIP_ERROR;
3937QBrush mountFlipStateBrush(MeridianFlipState::MeridianFlipMountState state)
3941 case MeridianFlipState::MOUNT_FLIP_NONE:
3943 case MeridianFlipState::MOUNT_FLIP_PLANNED:
3944 return stoppedBrush;
3945 case MeridianFlipState::MOUNT_FLIP_WAITING:
3946 return stopped2Brush;
3947 case MeridianFlipState::MOUNT_FLIP_ACCEPTED:
3948 return progressBrush;
3949 case MeridianFlipState::MOUNT_FLIP_RUNNING:
3950 return progress2Brush;
3951 case MeridianFlipState::MOUNT_FLIP_COMPLETED:
3952 return successBrush;
3953 case MeridianFlipState::MOUNT_FLIP_ERROR:
3954 return failureBrush;
3961void Analyze::mountFlipStatus(MeridianFlipState::MeridianFlipMountState state)
3963 if (state == lastMountFlipStateReceived)
3965 lastMountFlipStateReceived = state;
3967 QString stateStr = MeridianFlipState::meridianFlipStatusString(state);
3968 saveMessage(
"MeridianFlipState", stateStr);
3970 processMountFlipState(logTime(), stateStr);
3975void Analyze::processMountFlipState(
double time,
const QString &statusString,
bool batchMode)
3977 MeridianFlipState::MeridianFlipMountState state = convertMountFlipState(statusString);
3978 if (state == lastMountFlipStateStarted)
3981 bool lastStateInteresting =
3982 (lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_PLANNED ||
3983 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_WAITING ||
3984 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_ACCEPTED ||
3985 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_RUNNING);
3986 if (mountFlipStateStartedTime >= 0 && lastStateInteresting)
3988 if (state == MeridianFlipState::MOUNT_FLIP_COMPLETED || state == MeridianFlipState::MOUNT_FLIP_ERROR)
3991 addSession(mountFlipStateStartedTime, time, MERIDIAN_MOUNT_FLIP_Y, mountFlipStateBrush(state));
3992 mountFlipSessions.add(MountFlipSession(mountFlipStateStartedTime, time,
nullptr, state));
3996 addSession(mountFlipStateStartedTime, time, MERIDIAN_MOUNT_FLIP_Y, mountFlipStateBrush(lastMountFlipStateStarted));
3997 mountFlipSessions.add(MountFlipSession(mountFlipStateStartedTime, time,
nullptr, lastMountFlipStateStarted));
4000 bool stateInteresting =
4001 (state == MeridianFlipState::MOUNT_FLIP_PLANNED ||
4002 state == MeridianFlipState::MOUNT_FLIP_WAITING ||
4003 state == MeridianFlipState::MOUNT_FLIP_ACCEPTED ||
4004 state == MeridianFlipState::MOUNT_FLIP_RUNNING);
4005 if (stateInteresting)
4007 addTemporarySession(&temporaryMountFlipSession, time, 1, MERIDIAN_MOUNT_FLIP_Y, temporaryBrush);
4008 temporaryMountFlipSession.state = state;
4011 removeTemporarySession(&temporaryMountFlipSession);
4013 mountFlipStateStartedTime = time;
4014 lastMountFlipStateStarted = state;
4020void Analyze::resetMountFlipState()
4022 lastMountFlipStateReceived = MeridianFlipState::MOUNT_FLIP_NONE;
4023 lastMountFlipStateStarted = MeridianFlipState::MOUNT_FLIP_NONE;
4024 mountFlipStateStartedTime = -1;
4027QBrush Analyze::schedulerJobBrush(
const QString &jobName,
bool temporary)
4031 {110, 120, 150}, {150, 180, 180}, {180, 165, 130}, {180, 200, 140}, {250, 180, 130},
4032 {190, 170, 160}, {140, 110, 160}, {250, 240, 190}, {250, 200, 220}, {150, 125, 175}
4036 auto it = schedulerJobColors.constFind(jobName);
4037 if (it == schedulerJobColors.constEnd())
4039 const int numSoFar = schedulerJobColors.size();
4040 auto color = colors[numSoFar % colors.
size()];
4041 schedulerJobColors[jobName] = color;
4042 return QBrush(color, pattern);
4046 return QBrush(*it, pattern);
4050void Analyze::schedulerJobStarted(
const QString &jobName)
4052 saveMessage(
"SchedulerJobStart", jobName);
4054 processSchedulerJobStarted(logTime(), jobName);
4058void Analyze::schedulerJobEnded(
const QString &jobName,
const QString &reason)
4060 saveMessage(
"SchedulerJobEnd",
QString(
"%1,%2").arg(jobName, reason));
4062 processSchedulerJobEnded(logTime(), jobName, reason);
4068void Analyze::processSchedulerJobStarted(
double time,
const QString &jobName)
4070 checkForMissingSchedulerJobEnd(time - 1);
4071 schedulerJobStartedTime = time;
4072 schedulerJobStartedJobName = jobName;
4075 addTemporarySession(&temporarySchedulerJobSession, time, 1, SCHEDULER_Y, schedulerJobBrush(jobName,
true));
4076 temporarySchedulerJobSession.jobName = jobName;
4080void Analyze::processSchedulerJobEnded(
double time,
const QString &jobName,
const QString &reason,
bool batchMode)
4082 removeTemporarySession(&temporarySchedulerJobSession);
4084 if (schedulerJobStartedTime < 0)
4090 addSession(schedulerJobStartedTime, time, SCHEDULER_Y, schedulerJobBrush(jobName,
false));
4091 auto session = SchedulerJobSession(schedulerJobStartedTime, time,
nullptr, jobName, reason);
4092 schedulerJobSessions.add(session);
4094 resetSchedulerJob();
4100void Analyze::checkForMissingSchedulerJobEnd(
double time)
4102 if (schedulerJobStartedTime < 0)
4104 removeTemporarySession(&temporarySchedulerJobSession);
4105 addSession(schedulerJobStartedTime, time, SCHEDULER_Y, schedulerJobBrush(schedulerJobStartedJobName,
false));
4106 auto session = SchedulerJobSession(schedulerJobStartedTime, time,
nullptr, schedulerJobStartedJobName,
"missing job end");
4107 schedulerJobSessions.add(session);
4109 resetSchedulerJob();
4112void Analyze::resetSchedulerJob()
4114 schedulerJobStartedTime = -1;
4115 schedulerJobStartedJobName =
"";
4118void Analyze::appendLogText(
const QString &text)
4120 m_LogText.insert(0,
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
4121 KStarsData::Instance()->lt().
toString(
"yyyy-MM-ddThh:mm:ss"), text));
4123 qCInfo(KSTARS_EKOS_ANALYZE) << text;
4128void Analyze::clearLog()
QColor colorNamed(const QString &name) const
Retrieve a color by name.
ColorScheme * colorScheme()
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)
QString name(GameStandardAction id)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
void invokeHelp(const QString &anchor=QString(), const QString &appname=QString())
KIOCORE_EXPORT QString number(KIO::filesize_t size)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QAction * zoomIn(const QObject *recvr, const char *slot, QObject *parent)
QAction * zoomOut(const QObject *recvr, const char *slot, QObject *parent)
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
QAction * findNext(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & begin()
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
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)
void setInterval(int msec)
void setSingleShot(bool singleShot)
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.