9#include <knotification.h>
15#include "auxiliary/kspaths.h"
17#include "ekos/manager.h"
18#include "ekos/focus/curvefit.h"
19#include "fitsviewer/fitsdata.h"
20#include "fitsviewer/fitsviewer.h"
21#include "ksmessagebox.h"
23#include "kstarsdata.h"
25#include "qcustomplot.h"
27#include <ekos_analyze_debug.h>
37 void setOffset(
double offset)
41 QString getTickLabel(
double tick,
const QLocale &locale,
QChar formatChar,
int precision)
override
49 double timeOffset = 0;
56QString timeFormat =
"yyyy-MM-dd hh:mm:ss.zzz";
59constexpr int MAX_SCROLL_VALUE = 10000;
64constexpr double halfTimelineHeight = 0.35;
69int TEMPERATURE_GRAPH = -1;
70int FOCUS_POSITION_GRAPH = -1;
71int NUM_CAPTURE_STARS_GRAPH = -1;
73int ECCENTRICITY_GRAPH = -1;
74int NUMSTARS_GRAPH = -1;
79int RA_PULSE_GRAPH = -1;
80int DEC_PULSE_GRAPH = -1;
83int CAPTURE_RMS_GRAPH = -1;
84int MOUNT_RA_GRAPH = -1;
85int MOUNT_DEC_GRAPH = -1;
86int MOUNT_HA_GRAPH = -1;
89int PIER_SIDE_GRAPH = -1;
90int TARGET_DISTANCE_GRAPH = -1;
93int ADAPTIVE_FOCUS_GRAPH = -1;
96int FOCUS_GRAPHICS = -1;
97int FOCUS_GRAPHICS_FINAL = -1;
98int FOCUS_GRAPHICS_CURVE = -1;
99int GUIDER_GRAPHICS = -1;
118 return info.exists() && info.isFile();
123const QString mountStatusString(ISD::Mount::Status status)
127 case ISD::Mount::MOUNT_IDLE:
129 case ISD::Mount::MOUNT_PARKED:
130 return i18n(
"Parked");
131 case ISD::Mount::MOUNT_PARKING:
132 return i18n(
"Parking");
133 case ISD::Mount::MOUNT_SLEWING:
134 return i18n(
"Slewing");
135 case ISD::Mount::MOUNT_MOVING:
136 return i18n(
"Moving");
137 case ISD::Mount::MOUNT_TRACKING:
138 return i18n(
"Tracking");
139 case ISD::Mount::MOUNT_ERROR:
140 return i18n(
"Error");
142 return i18n(
"Error");
145ISD::Mount::Status toMountStatus(
const QString &str)
147 if (str ==
i18n(
"Idle"))
148 return ISD::Mount::MOUNT_IDLE;
149 else if (str ==
i18n(
"Parked"))
150 return ISD::Mount::MOUNT_PARKED;
151 else if (str ==
i18n(
"Parking"))
152 return ISD::Mount::MOUNT_PARKING;
153 else if (str ==
i18n(
"Slewing"))
154 return ISD::Mount::MOUNT_SLEWING;
155 else if (str ==
i18n(
"Moving"))
156 return ISD::Mount::MOUNT_MOVING;
157 else if (str ==
i18n(
"Tracking"))
158 return ISD::Mount::MOUNT_TRACKING;
160 return ISD::Mount::MOUNT_ERROR;
169 const QString rPattern(
"^(red|r)$");
175 const QString gPattern(
"^(green|g)$");
181 const QString bPattern(
"^(blue|b)$");
187 const QString hPattern(
"^(ha|h|h-a|h_a|h-alpha|hydrogen|hydrogen_alpha|hydrogen-alpha|h_alpha|halpha)$");
193 const QString oPattern(
"^(oiii|oxygen|oxygen_3|oxygen-3|oxygen_iii|oxygen-iii|o_iii|o-iii|o_3|o-3|o3)$");
200 sPattern(
"^(sii|sulphur|sulphur_2|sulphur-2|sulphur_ii|sulphur-ii|sulfur|sulfur_2|sulfur-2|sulfur_ii|sulfur-ii|s_ii|s-ii|s_2|s-2|s2)$");
207 const QString lPattern(
"^(lpr|L|UV-IR cut|UV-IR|white|monochrome|broadband|clear|focus|luminance|lum|lps|cls)$");
225 const QString &alternateDirectory = Options::analyzeAlternativeImageDirectory();
229 if (info.exists() && info.isFile())
239 int size = filename.
size();
240 int searchBackFrom = size -
name.
size();
242 while (searchBackFrom >= 0)
244 int index = filename.
lastIndexOf(
'/', searchBackFrom);
252 searchBackFrom = index - 1;
271 intervals.append(value);
280 for (
const auto &interval : intervals)
282 if (t >= interval.start && t <= interval.end)
290 double bestStart = 1e7;
292 for (
auto &interval : intervals)
294 if (interval.start > t && interval.start < bestStart)
296 bestStart = interval.start;
303 T *findPrevious(
double t)
305 double bestStart = -1e7;
307 for (
auto &interval : intervals)
309 if (interval.start < t && interval.start > bestStart)
311 bestStart = interval.start;
321IntervalFinder<Ekos::Analyze::CaptureSession> captureSessions;
322IntervalFinder<Ekos::Analyze::FocusSession> focusSessions;
323IntervalFinder<Ekos::Analyze::GuideSession> guideSessions;
324IntervalFinder<Ekos::Analyze::MountSession> mountSessions;
325IntervalFinder<Ekos::Analyze::AlignSession> alignSessions;
326IntervalFinder<Ekos::Analyze::MountFlipSession> mountFlipSessions;
327IntervalFinder<Ekos::Analyze::SchedulerJobSession> schedulerJobSessions;
343 constexpr double timeConstant = 40.0;
344 alpha = 1.0 / pow(timeConstant, 0.865);
350 double newSample(
double x,
double y)
352 const double valueSquared = x * x + y * y;
353 filteredRMS = alpha * valueSquared + (1.0 - alpha) * filteredRMS;
354 return sqrt(filteredRMS);
358 double filteredRMS { 0 };
371 auto axisEntry = yAxisMap.find(obj);
372 if (axisEntry == yAxisMap.end())
379 Qt::KeyboardModifier::ControlModifier);
382 Qt::KeyboardModifier::ShiftModifier);
386 startYAxisTool(axisEntry->first, axisEntry->second);
395 m_ClickTimerInfo = axisEntry->second;
400 if (m_ClickTimerInfo.checkBox && !m_ClickTimerInfo.checkBox->
isChecked())
404 statsPlot->graph(m_ClickTimerInfo.graphIndex)->setVisible(
true);
405 statsPlot->graph(m_ClickTimerInfo.graphIndex)->addToLegend();
407 userSetLeftAxis(m_ClickTimerInfo.axis);
414Analyze::Analyze() : m_YAxisTool(this)
418 captureRms.reset(
new RmsFilter);
419 guiderRms.reset(
new RmsFilter);
421 initInputSelection();
425 connect(&m_YAxisTool, &YAxisTool::axisChanged,
this, &Analyze::userChangedYAxis);
426 connect(&m_YAxisTool, &YAxisTool::leftAxisChanged,
this, &Analyze::userSetLeftAxis);
427 connect(&m_YAxisTool, &YAxisTool::axisColorChanged,
this, &Analyze::userSetAxisColor);
428 qApp->installEventFilter(
this);
431 fullWidthCB->setChecked(
true);
432 keepCurrentCB->setChecked(
true);
433 runtimeDisplay =
true;
434 fullWidthCB->setVisible(
true);
435 fullWidthCB->setDisabled(
false);
439 detailsCB->setChecked(
true);
440 statsCB->setChecked(
true);
441 graphsCB->setChecked(
true);
442 timelineCB->setChecked(
true);
455 initStatsCheckboxes();
468 analyzeSB->setRange(0, MAX_SCROLL_VALUE);
472 setupKeyboardShortcuts(
this);
478void Analyze::setVisibility()
480 detailsWidget->setVisible(detailsCB->isChecked());
481 statsGridWidget->setVisible(statsCB->isChecked());
482 timelinePlot->setVisible(timelineCB->isChecked());
483 statsPlot->setVisible(graphsCB->isChecked());
488void Analyze::timelineMouseWheel(
QWheelEvent *event)
490 if (
event->angleDelta().y() > 0)
492 else if (
event->angleDelta().y() < 0)
498void Analyze::keepCurrent(
int state)
501 if (keepCurrentCB->isChecked())
509QString Analyze::getNextFile(
bool after)
524 dirString = dirPath.toLocalFile();
529 dir.setPath(dirString);
531 filters <<
"*.analyze";
532 dir.setNameFilters(filters);
536 if (fileList.
size() == 0)
540 if (filename.
isEmpty() && fileList.
size() > 0 && !after)
545 for (
int i = fileList.
size() - 1; i >= 0; --i)
547 if (fileList[i] == filename)
557 else if (!after && index <= 0)
559 else if (after && index >= fileList.
size() - 1)
565void Analyze::nextFile()
567 QString filename = getNextFile(
true);
569 displayFile(
QUrl(),
true);
575void Analyze::prevFile()
577 QString filename = getNextFile(
false);
584void Analyze::displayFile(
const QUrl &url,
bool forceCurrentSession)
586 if (forceCurrentSession || (logFilename.size() > 0 && url.
toLocalFile() == logFilename))
589 inputCombo->setCurrentIndex(0);
590 inputValue->setText(
"");
594 maxXValue = readDataFromFile(logFilename);
596 runtimeDisplay =
true;
597 fullWidthCB->setChecked(
true);
598 fullWidthCB->setVisible(
true);
599 fullWidthCB->setDisabled(
false);
600 displayedSession =
QUrl();
605 inputCombo->setCurrentIndex(1);
606 displayedSession = url;
610 inputValue->setText(url.
fileName());
613 runtimeDisplay =
false;
616 checkForMissingSchedulerJobEnd(maxXValue);
618 plotWidth = maxXValue + 5;
624void Analyze::initInputSelection()
629 inputCombo->addItem(
i18n(
"Current Session"));
630 inputCombo->addItem(
i18n(
"Read from File"));
631 inputValue->setText(
"");
632 inputCombo->setCurrentIndex(0);
645 QString(
"Analyze %1 (*.analyze);;%2").arg(
i18n(
"Log")).arg(
i18n(
"All Files (*)")));
648 displayFile(inputURL);
655void Analyze::setupKeyboardShortcuts(
QWidget *plot)
697void Analyze::setSelectedSession(
const Session &s)
699 m_selectedSession = s;
702void Analyze::clearSelectedSession()
709void Analyze::unhighlightTimelineItem()
711 clearSelectedSession();
712 if (selectionHighlight !=
nullptr)
714 timelinePlot->removeItem(selectionHighlight);
715 selectionHighlight =
nullptr;
717 detailsTable->clear();
718 prevSessionB->setDisabled(
true);
719 nextSessionB->setDisabled(
true);
724void Analyze::highlightTimelineItem(
const Session &session)
726 constexpr double halfHeight = 0.5;
727 unhighlightTimelineItem();
729 setSelectedSession(session);
731 rect->topLeft->
setCoords(session.start, session.offset + halfHeight);
732 rect->bottomRight->
setCoords(session.end, session.offset - halfHeight);
733 rect->
setBrush(timelineSelectionBrush);
734 selectionHighlight = rect;
735 prevSessionB->setDisabled(
false);
736 nextSessionB->setDisabled(
false);
741QCPItemRect * Analyze::addSession(
double start,
double end,
double y,
746 rect->topLeft->
setCoords(start, y + halfTimelineHeight);
747 rect->bottomRight->
setCoords(end, y - halfTimelineHeight);
753 if (stripeBrush !=
nullptr)
756 stripe->topLeft->
setCoords(start, y + halfTimelineHeight / 2.0);
757 stripe->bottomRight->
setCoords(end, y - halfTimelineHeight / 2.0);
769void Analyze::addGuideStats(
double raDrift,
double decDrift,
int raPulse,
int decPulse,
double snr,
770 int numStars,
double skyBackground,
double time)
772 double MAX_GUIDE_STATS_GAP = 30;
774 if (time - lastGuideStatsTime > MAX_GUIDE_STATS_GAP &&
775 lastGuideStatsTime >= 0)
777 addGuideStatsInternal(qQNaN(), qQNaN(), 0, 0, qQNaN(), qQNaN(), qQNaN(), qQNaN(), qQNaN(),
778 lastGuideStatsTime + .0001);
779 addGuideStatsInternal(qQNaN(), qQNaN(), 0, 0, qQNaN(), qQNaN(), qQNaN(), qQNaN(), qQNaN(), time - .0001);
780 guiderRms->resetFilter();
783 const double drift = std::hypot(raDrift, decDrift);
788 const double rms = guiderRms->newSample(raDrift, decDrift);
789 addGuideStatsInternal(raDrift, decDrift,
double(raPulse),
double(decPulse), snr, numStars, skyBackground, drift, rms, time);
792 if (captureStartedTime >= 0)
797 if ((lastCaptureRmsTime >= 0) &&
798 (time - lastCaptureRmsTime > MAX_GUIDE_STATS_GAP))
801 statsPlot->graph(CAPTURE_RMS_GRAPH)->addData(lastCaptureRmsTime + .0001, qQNaN());
802 statsPlot->graph(CAPTURE_RMS_GRAPH)->addData(time - .0001, qQNaN());
803 captureRms->resetFilter();
805 const double rmsC = captureRms->newSample(raDrift, decDrift);
806 statsPlot->graph(CAPTURE_RMS_GRAPH)->addData(time, rmsC);
807 lastCaptureRmsTime = time;
810 lastGuideStatsTime = time;
813void Analyze::addGuideStatsInternal(
double raDrift,
double decDrift,
double raPulse,
814 double decPulse,
double snr,
815 double numStars,
double skyBackground,
816 double drift,
double rms,
double time)
818 statsPlot->graph(RA_GRAPH)->addData(time, raDrift);
819 statsPlot->graph(DEC_GRAPH)->addData(time, decDrift);
820 statsPlot->graph(RA_PULSE_GRAPH)->addData(time, raPulse);
821 statsPlot->graph(DEC_PULSE_GRAPH)->addData(time, decPulse);
822 statsPlot->graph(DRIFT_GRAPH)->addData(time, drift);
823 statsPlot->graph(RMS_GRAPH)->addData(time, rms);
827 snrMax = std::max(snr, snrMax);
828 if (!qIsNaN(skyBackground))
829 skyBgMax = std::max(skyBackground, skyBgMax);
830 if (!qIsNaN(numStars))
831 numStarsMax = std::max(numStars,
static_cast<double>(numStarsMax));
833 statsPlot->graph(SNR_GRAPH)->addData(time, snr);
834 statsPlot->graph(NUMSTARS_GRAPH)->addData(time, numStars);
835 statsPlot->graph(SKYBG_GRAPH)->addData(time, skyBackground);
838void Analyze::addTemperature(
double temperature,
double time)
842 if (temperature > -200)
843 statsPlot->graph(TEMPERATURE_GRAPH)->addData(time, temperature);
846void Analyze::addFocusPosition(
double focusPosition,
double time)
848 statsPlot->graph(FOCUS_POSITION_GRAPH)->addData(time, focusPosition);
851void Analyze::addTargetDistance(
double targetDistance,
double time)
854 if (previousCaptureStartedTime >= 0 && previousCaptureCompletedTime >= 0 &&
855 previousCaptureStartedTime < previousCaptureCompletedTime &&
856 previousCaptureCompletedTime <= time)
858 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureStartedTime - .0001, qQNaN());
859 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureStartedTime, targetDistance);
860 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureCompletedTime, targetDistance);
861 statsPlot->graph(TARGET_DISTANCE_GRAPH)->addData(previousCaptureCompletedTime + .0001, qQNaN());
866void Analyze::addHFR(
double hfr,
int numCaptureStars,
int median,
double eccentricity,
867 double time,
double startTime)
870 statsPlot->graph(HFR_GRAPH)->addData(startTime - .0001, qQNaN());
871 statsPlot->graph(HFR_GRAPH)->addData(startTime, hfr);
872 statsPlot->graph(HFR_GRAPH)->addData(time, hfr);
873 statsPlot->graph(HFR_GRAPH)->addData(time + .0001, qQNaN());
875 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(startTime - .0001, qQNaN());
876 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(startTime, numCaptureStars);
877 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(time, numCaptureStars);
878 statsPlot->graph(NUM_CAPTURE_STARS_GRAPH)->addData(time + .0001, qQNaN());
880 statsPlot->graph(MEDIAN_GRAPH)->addData(startTime - .0001, qQNaN());
881 statsPlot->graph(MEDIAN_GRAPH)->addData(startTime, median);
882 statsPlot->graph(MEDIAN_GRAPH)->addData(time, median);
883 statsPlot->graph(MEDIAN_GRAPH)->addData(time + .0001, qQNaN());
885 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(startTime - .0001, qQNaN());
886 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(startTime, eccentricity);
887 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(time, eccentricity);
888 statsPlot->graph(ECCENTRICITY_GRAPH)->addData(time + .0001, qQNaN());
890 medianMax = std::max(median, medianMax);
891 numCaptureStarsMax = std::max(numCaptureStars, numCaptureStarsMax);
896void Analyze::addMountCoords(
double ra,
double dec,
double az,
897 double alt,
int pierSide,
double ha,
double time)
899 statsPlot->graph(MOUNT_RA_GRAPH)->addData(time, ra);
900 statsPlot->graph(MOUNT_DEC_GRAPH)->addData(time, dec);
901 statsPlot->graph(MOUNT_HA_GRAPH)->addData(time, ha);
902 statsPlot->graph(AZ_GRAPH)->addData(time, az);
903 statsPlot->graph(ALT_GRAPH)->addData(time, alt);
904 statsPlot->graph(PIER_SIDE_GRAPH)->addData(time,
double(pierSide));
908double Analyze::readDataFromFile(
const QString &filename)
910 double lastTime = 10;
911 QFile inputFile(filename);
918 double time = processInputLine(line);
928double Analyze::processInputLine(
const QString &line)
936 if (list[0].at(0).toLatin1() ==
'#')
942 if ((list[0] ==
"AnalyzeStartTime") &&
list.
size() == 3)
945 startTimeInitialized =
true;
946 analyzeTimeZone =
list[2];
955 if (time < 0 || time > 3600 * 24 * 10)
958 if ((list[0] ==
"CaptureStarting") && (
list.
size() == 4))
964 processCaptureStarting(time, exposureSeconds, filter);
966 else if ((list[0] ==
"CaptureComplete") && (
list.
size() >= 6) && (
list.
size() <= 9))
985 processCaptureComplete(time, filename, exposureSeconds, filter, hfr, numStars, median, eccentricity,
true);
987 else if ((list[0] ==
"CaptureAborted") && (
list.
size() == 3))
992 processCaptureAborted(time, exposureSeconds,
true);
994 else if ((list[0] ==
"AutofocusStarting") && (
list.
size() >= 4))
1000 AutofocusReason reason;
1004 reason = AutofocusReason::FOCUS_NONE;
1009 reason =
static_cast<AutofocusReason
>(
QString(list[4]).
toInt(&ok));
1012 reasonInfo =
list[5];
1014 processAutofocusStarting(time, temperature, filter, reason, reasonInfo);
1016 else if ((list[0] ==
"AutofocusComplete") && (
list.
size() >= 8))
1023 int reasonInt = reasonV.
toInt();
1024 if (reasonInt < 0 || reasonInt >= AutofocusReason::FOCUS_MAX_REASONS)
1026 AutofocusReason reason =
static_cast<AutofocusReason
>(reasonInt);
1035 processAutofocusCompleteV2(time, temperature, filter, reason, reasonInfo, samples, useWeights, curve, title,
true);
1037 else if ((list[0] ==
"AutofocusComplete") && (
list.
size() >= 4))
1044 processAutofocusComplete(time, filter, samples, curve, title,
true);
1046 else if ((list[0] ==
"AutofocusAborted") && (
list.
size() >= 9))
1052 int reasonInt = reasonV.
toInt();
1053 if (reasonInt < 0 || reasonInt >= AutofocusReason::FOCUS_MAX_REASONS)
1055 AutofocusReason reason =
static_cast<AutofocusReason
>(reasonInt);
1062 AutofocusFailReason failCode;
1064 int failCodeInt = failCodeV.
toInt();
1065 if (failCodeInt < 0 || failCodeInt >= AutofocusFailReason::FOCUS_FAIL_MAX_REASONS)
1067 failCode =
static_cast<AutofocusFailReason
>(failCodeInt);
1072 failCodeInfo =
QString(list[9]);
1073 processAutofocusAbortedV2(time, temperature, filter, reason, reasonInfo, samples, useWeights, failCode, failCodeInfo,
true);
1075 else if ((list[0] ==
"AutofocusAborted") && (
list.
size() >= 4))
1079 processAutofocusAborted(time, filter, samples,
true);
1081 else if ((list[0] ==
"AdaptiveFocusComplete") && (
list.
size() == 12))
1093 const bool focuserMoved =
QString(list[11]).
toInt(&ok) != 0;
1094 processAdaptiveFocusComplete(time, filter, temperature, tempTicks, altitude, altTicks, prevPosError,
1095 thisPosError, totalTicks, position, focuserMoved,
true);
1097 else if ((list[0] ==
"AdaptiveFocusComplete") && (
list.
size() >= 9))
1109 processAdaptiveFocusComplete(time, filter, temperature, tempTicks,
1110 altitude, altTicks, 0, 0, totalTicks, position, focuserMoved,
true);
1112 else if ((list[0] ==
"GuideState") &&
list.
size() == 3)
1114 processGuideState(time, list[2],
true);
1116 else if ((list[0] ==
"GuideStats") &&
list.
size() == 9)
1139 processGuideStats(time, ra, dec, raPulse, decPulse, snr, skyBg, numStars,
true);
1141 else if ((list[0] ==
"Temperature") &&
list.
size() == 3)
1146 processTemperature(time, temperature,
true);
1148 else if ((list[0] ==
"TargetDistance") &&
list.
size() == 3)
1153 processTargetDistance(time, targetDistance,
true);
1155 else if ((list[0] ==
"MountState") &&
list.
size() == 3)
1157 processMountState(time, list[2],
true);
1159 else if ((list[0] ==
"MountCoords") && (
list.
size() == 7 ||
list.
size() == 8))
1179 processMountCoords(time, ra, dec, az, alt, side, ha,
true);
1181 else if ((list[0] ==
"AlignState") &&
list.
size() == 3)
1183 processAlignState(time, list[2],
true);
1185 else if ((list[0] ==
"MeridianFlipState") &&
list.
size() == 3)
1187 processMountFlipState(time, list[2],
true);
1189 else if ((list[0] ==
"SchedulerJobStart") &&
list.
size() == 3)
1192 processSchedulerJobStarted(time, jobName);
1194 else if ((list[0] ==
"SchedulerJobEnd") &&
list.
size() == 4)
1198 processSchedulerJobEnded(time, jobName, reason,
true);
1217 if (col1 ==
"Filename")
1240 if (col1 ==
"Filename")
1249 if (col3.size() > 0)
1267void Analyze::Session::setupTable(
const QString &name,
const QString &status,
1272 details->setRowCount(0);
1274 details->setColumnCount(3);
1275 details->verticalHeader()->setDefaultSectionSize(20);
1276 details->horizontalHeader()->setStretchLastSection(
true);
1277 details->setColumnWidth(0, 100);
1278 details->setColumnWidth(1, 100);
1279 details->setShowGrid(
false);
1280 details->setWordWrap(
true);
1281 details->horizontalHeader()->hide();
1282 details->verticalHeader()->hide();
1286 QString endTimeStr = isTemporary() ?
"Ongoing"
1298void Analyze::Session::addRow(
const QString &key,
const QString &value)
1303bool Analyze::Session::isTemporary()
const
1305 return rect !=
nullptr;
1311Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
bool ok,
double temperature_,
1312 const QString &filter_,
const AutofocusReason reason_,
const QString &reasonInfo_,
const QString &points_,
1313 const bool useWeights_,
const QString &curve_,
const QString &title_,
const AutofocusFailReason failCode_,
1315 :
Session(start_, end_, FOCUS_Y, rect), success(
ok), temperature(temperature_),
filter(filter_), reason(reason_),
1316 reasonInfo(reasonInfo_), points(points_), useWeights(useWeights_), curve(curve_), title(title_), failCode(failCode_),
1317 failCodeInfo(failCodeInfo_)
1325 for (
int i = 0; i < size; )
1327 bool parsed1, parsed2, parsed3, parsed4;
1334 if (!parsed1 || !parsed2 || !parsed3 || !parsed4)
1342 positions.push_back(position);
1343 hfrs.push_back(hfr);
1344 weights.push_back(weight);
1345 outliers.push_back(outlier);
1352Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
bool ok,
double temperature_,
1354 :
Session(start_, end_, FOCUS_Y, rect), success(
ok),
1355 temperature(temperature_),
filter(filter_), points(points_), curve(curve_), title(title_)
1358 reason = AutofocusReason::FOCUS_NONE;
1361 failCode = AutofocusFailReason::FOCUS_FAIL_NONE;
1370 for (
int i = 0; i < size; )
1372 bool parsed1, parsed2;
1377 if (!parsed1 || !parsed2)
1385 positions.push_back(position);
1386 hfrs.push_back(hfr);
1387 weights.push_back(1.0);
1388 outliers.push_back(
false);
1392Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
1393 const QString &filter_,
double temperature_,
double tempTicks_,
double altitude_,
1394 double altTicks_,
int prevPosError_,
int thisPosError_,
int totalTicks_,
int position_)
1395 :
Session(start_, end_, FOCUS_Y, rect), temperature(temperature_),
filter(filter_), tempTicks(tempTicks_),
1396 altitude(altitude_), altTicks(altTicks_), prevPosError(prevPosError_), thisPosError(thisPosError_),
1397 totalTicks(totalTicks_), adaptedPosition(position_)
1399 standardSession =
false;
1402double Analyze::FocusSession::focusPosition()
1404 if (!standardSession)
1405 return adaptedPosition;
1407 if (positions.size() > 0)
1408 return positions.last();
1414bool isTemporaryFile(
const QString &filename)
1417 return filename.
startsWith(tempFileLocation);
1424void Analyze::captureSessionClicked(CaptureSession &c,
bool doubleClick)
1426 highlightTimelineItem(c);
1428 if (c.isTemporary())
1429 c.setupTable(
"Capture",
"in progress", clockTime(c.start), clockTime(c.start), detailsTable);
1431 c.setupTable(
"Capture",
"ABORTED", clockTime(c.start), clockTime(c.end), detailsTable);
1433 c.setupTable(
"Capture",
"successful", clockTime(c.start), clockTime(c.end), detailsTable);
1435 c.addRow(
"Filter", c.filter);
1437 double raRMS, decRMS, totalRMS;
1439 displayGuideGraphics(c.start, c.end, &raRMS, &decRMS, &totalRMS, &numSamples);
1444 if (!c.isTemporary())
1445 c.addRow(
"Filename", c.filename);
1449 if (doubleClick && !c.isTemporary())
1451 QString filename = findFilename(c.filename);
1453 bool tempImage = isTemporaryFile(c.filename);
1454 if (!tempImage && filename.
size() == 0)
1455 appendLogText(
i18n(
"Could not find image file: %1", c.filename));
1456 else if (!tempImage)
1457 displayFITS(filename);
1458 else appendLogText(
i18n(
"Cannot display temporary image file: %1", c.filename));
1466 if (val == 0)
return "";
1467 else if (val > 0)
return "+";
1470QString signedIntString(
int val)
1481void Analyze::focusSessionClicked(FocusSession &c,
bool doubleClick)
1483 Q_UNUSED(doubleClick);
1484 highlightTimelineItem(c);
1486 if (!c.standardSession)
1489 c.setupTable(
"Focus",
"Adaptive", clockTime(c.end), clockTime(c.end), detailsTable);
1490 c.addRow(
"Filter", c.filter);
1491 addDetailsRow(detailsTable,
"Temperature",
Qt::yellow,
QString(
"%1°").arg(c.temperature, 0,
'f', 1),
1493 addDetailsRow(detailsTable,
"Altitude",
Qt::yellow,
QString(
"%1°").arg(c.altitude, 0,
'f', 1),
1496 QString(
"%1 / %2").arg(c.prevPosError).
arg(c.thisPosError));
1498 Qt::white, signedIntString(c.totalTicks));
1503 c.setupTable(
"Focus",
"successful", clockTime(c.start), clockTime(c.end), detailsTable);
1504 else if (c.isTemporary())
1505 c.setupTable(
"Focus",
"in progress", clockTime(c.start), clockTime(c.start), detailsTable);
1507 c.setupTable(
"Focus",
"FAILED", clockTime(c.start), clockTime(c.end), detailsTable);
1509 if (!c.isTemporary())
1513 if (c.hfrs.size() > 0)
1515 if (c.positions.size() > 0)
1521 if (!c.success && !c.isTemporary())
1522 addDetailsRow(detailsTable,
"Fail Reason",
Qt::yellow, AutofocusFailReasonStr[c.failCode],
Qt::white, c.failCodeInfo,
1525 c.addRow(
"Filter", c.filter);
1526 c.addRow(
"Temperature", (c.temperature == INVALID_VALUE) ?
"N/A" :
QString::number(c.temperature,
'f', 1));
1528 if (c.isTemporary())
1529 resetGraphicsPlot();
1531 displayFocusGraphics(c.positions, c.hfrs, c.useWeights, c.weights, c.outliers, c.curve, c.title, c.success);
1538void Analyze::guideSessionClicked(GuideSession &c,
bool doubleClick)
1540 Q_UNUSED(doubleClick);
1541 highlightTimelineItem(c);
1544 if (c.simpleState == G_IDLE)
1546 else if (c.simpleState == G_GUIDING)
1548 else if (c.simpleState == G_CALIBRATING)
1550 else if (c.simpleState == G_SUSPENDED)
1552 else if (c.simpleState == G_DITHERING)
1555 c.setupTable(
"Guide", st, clockTime(c.start), clockTime(c.end), detailsTable);
1556 resetGraphicsPlot();
1557 if (c.simpleState == G_GUIDING)
1559 double raRMS, decRMS, totalRMS;
1561 displayGuideGraphics(c.start, c.end, &raRMS, &decRMS, &totalRMS, &numSamples);
1572void Analyze::displayGuideGraphics(
double start,
double end,
double *raRMS,
1573 double *decRMS,
double *totalRMS,
int *numSamples)
1575 resetGraphicsPlot();
1576 auto ra = statsPlot->graph(RA_GRAPH)->data()->findBegin(start);
1577 auto dec = statsPlot->graph(DEC_GRAPH)->data()->findBegin(start);
1578 auto raEnd = statsPlot->graph(RA_GRAPH)->data()->findEnd(end);
1579 auto decEnd = statsPlot->graph(DEC_GRAPH)->data()->findEnd(end);
1581 double raSquareErrorSum = 0, decSquareErrorSum = 0;
1582 while (ra != raEnd && dec != decEnd &&
1583 ra->mainKey() < end &&
dec->mainKey() < end &&
1584 ra != statsPlot->graph(RA_GRAPH)->data()->constEnd() &&
1585 dec != statsPlot->graph(DEC_GRAPH)->data()->constEnd() &&
1586 ra->mainKey() < end &&
dec->mainKey() < end)
1588 const double raVal = ra->mainValue();
1589 const double decVal =
dec->mainValue();
1590 graphicsPlot->graph(GUIDER_GRAPHICS)->addData(raVal, decVal);
1591 if (!qIsNaN(raVal) && !qIsNaN(decVal))
1593 raSquareErrorSum += raVal * raVal;
1594 decSquareErrorSum += decVal * decVal;
1600 if (numSamples !=
nullptr)
1604 if (raRMS !=
nullptr)
1605 *raRMS = sqrt(raSquareErrorSum / num);
1606 if (decRMS !=
nullptr)
1607 *decRMS = sqrt(decSquareErrorSum / num);
1608 if (totalRMS !=
nullptr)
1609 *totalRMS = sqrt((raSquareErrorSum + decSquareErrorSum) / num);
1610 if (numSamples !=
nullptr)
1627 graphicsPlot->xAxis->setRange(-2.5, 2.5);
1628 graphicsPlot->yAxis->setRange(-2.5, 2.5);
1629 graphicsPlot->xAxis->setScaleRatio(graphicsPlot->yAxis);
1634void Analyze::mountSessionClicked(MountSession &c,
bool doubleClick)
1636 Q_UNUSED(doubleClick);
1637 highlightTimelineItem(c);
1639 c.setupTable(
"Mount", mountStatusString(c.state), clockTime(c.start),
1640 clockTime(c.isTemporary() ? c.start : c.end), detailsTable);
1645void Analyze::alignSessionClicked(AlignSession &c,
bool doubleClick)
1647 Q_UNUSED(doubleClick);
1648 highlightTimelineItem(c);
1649 c.setupTable(
"Align", getAlignStatusString(c.state), clockTime(c.start),
1650 clockTime(c.isTemporary() ? c.start : c.end), detailsTable);
1655void Analyze::mountFlipSessionClicked(MountFlipSession &c,
bool doubleClick)
1657 Q_UNUSED(doubleClick);
1658 highlightTimelineItem(c);
1659 c.setupTable(
"Meridian Flip", MeridianFlipState::meridianFlipStatusString(c.state),
1660 clockTime(c.start), clockTime(c.isTemporary() ? c.start : c.end), detailsTable);
1665void Analyze::schedulerSessionClicked(SchedulerJobSession &c,
bool doubleClick)
1667 Q_UNUSED(doubleClick);
1668 highlightTimelineItem(c);
1669 c.setupTable(
"Scheduler Job", c.jobName,
1670 clockTime(c.start), clockTime(c.isTemporary() ? c.start : c.end), detailsTable);
1671 c.addRow(
"End reason", c.reason);
1677void Analyze::processTimelineClick(
QMouseEvent *event,
bool doubleClick)
1679 unhighlightTimelineItem();
1680 double xval = timelinePlot->xAxis->pixelToCoord(
event->x());
1681 double yval = timelinePlot->yAxis->pixelToCoord(
event->y());
1682 if (yval >= CAPTURE_Y - 0.5 && yval <= CAPTURE_Y + 0.5)
1685 if (candidates.
size() > 0)
1686 captureSessionClicked(candidates[0], doubleClick);
1687 else if ((temporaryCaptureSession.rect !=
nullptr) &&
1688 (xval > temporaryCaptureSession.start))
1689 captureSessionClicked(temporaryCaptureSession, doubleClick);
1691 else if (yval >= FOCUS_Y - 0.5 && yval <= FOCUS_Y + 0.5)
1694 if (candidates.
size() > 0)
1695 focusSessionClicked(candidates[0], doubleClick);
1696 else if ((temporaryFocusSession.rect !=
nullptr) &&
1697 (xval > temporaryFocusSession.start))
1698 focusSessionClicked(temporaryFocusSession, doubleClick);
1700 else if (yval >= GUIDE_Y - 0.5 && yval <= GUIDE_Y + 0.5)
1703 if (candidates.
size() > 0)
1704 guideSessionClicked(candidates[0], doubleClick);
1705 else if ((temporaryGuideSession.rect !=
nullptr) &&
1706 (xval > temporaryGuideSession.start))
1707 guideSessionClicked(temporaryGuideSession, doubleClick);
1709 else if (yval >= MOUNT_Y - 0.5 && yval <= MOUNT_Y + 0.5)
1712 if (candidates.
size() > 0)
1713 mountSessionClicked(candidates[0], doubleClick);
1714 else if ((temporaryMountSession.rect !=
nullptr) &&
1715 (xval > temporaryMountSession.start))
1716 mountSessionClicked(temporaryMountSession, doubleClick);
1718 else if (yval >= ALIGN_Y - 0.5 && yval <= ALIGN_Y + 0.5)
1721 if (candidates.
size() > 0)
1722 alignSessionClicked(candidates[0], doubleClick);
1723 else if ((temporaryAlignSession.rect !=
nullptr) &&
1724 (xval > temporaryAlignSession.start))
1725 alignSessionClicked(temporaryAlignSession, doubleClick);
1727 else if (yval >= MERIDIAN_MOUNT_FLIP_Y - 0.5 && yval <= MERIDIAN_MOUNT_FLIP_Y + 0.5)
1730 if (candidates.
size() > 0)
1731 mountFlipSessionClicked(candidates[0], doubleClick);
1732 else if ((temporaryMountFlipSession.rect !=
nullptr) &&
1733 (xval > temporaryMountFlipSession.start))
1734 mountFlipSessionClicked(temporaryMountFlipSession, doubleClick);
1736 else if (yval >= SCHEDULER_Y - 0.5 && yval <= SCHEDULER_Y + 0.5)
1739 if (candidates.
size() > 0)
1740 schedulerSessionClicked(candidates[0], doubleClick);
1741 else if ((temporarySchedulerJobSession.rect !=
nullptr) &&
1742 (xval > temporarySchedulerJobSession.start))
1743 schedulerSessionClicked(temporarySchedulerJobSession, doubleClick);
1745 setStatsCursor(xval);
1749void Analyze::nextTimelineItem()
1751 changeTimelineItem(
true);
1754void Analyze::previousTimelineItem()
1756 changeTimelineItem(
false);
1759void Analyze::changeTimelineItem(
bool next)
1761 if (m_selectedSession.start == 0 && m_selectedSession.end == 0)
return;
1762 switch(m_selectedSession.offset)
1766 auto nextSession =
next ? captureSessions.findNext(m_selectedSession.start)
1767 : captureSessions.findPrevious(m_selectedSession.start);
1771 while (nextSession && nextSession->aborted)
1772 nextSession =
next ? captureSessions.findNext(nextSession->start)
1773 : captureSessions.findPrevious(nextSession->start);
1778 captureSessionClicked(*nextSession,
true);
1779 setStatsCursor((nextSession->end + nextSession->start) / 2);
1785 auto nextSession =
next ? focusSessions.findNext(m_selectedSession.start)
1786 : focusSessions.findPrevious(m_selectedSession.start);
1789 focusSessionClicked(*nextSession,
true);
1790 setStatsCursor((nextSession->end + nextSession->start) / 2);
1796 auto nextSession =
next ? alignSessions.findNext(m_selectedSession.start)
1797 : alignSessions.findPrevious(m_selectedSession.start);
1800 alignSessionClicked(*nextSession,
true);
1801 setStatsCursor((nextSession->end + nextSession->start) / 2);
1807 auto nextSession =
next ? guideSessions.findNext(m_selectedSession.start)
1808 : guideSessions.findPrevious(m_selectedSession.start);
1811 guideSessionClicked(*nextSession,
true);
1812 setStatsCursor((nextSession->end + nextSession->start) / 2);
1818 auto nextSession =
next ? mountSessions.findNext(m_selectedSession.start)
1819 : mountSessions.findPrevious(m_selectedSession.start);
1822 mountSessionClicked(*nextSession,
true);
1823 setStatsCursor((nextSession->end + nextSession->start) / 2);
1829 auto nextSession =
next ? schedulerJobSessions.findNext(m_selectedSession.start)
1830 : schedulerJobSessions.findPrevious(m_selectedSession.start);
1833 schedulerSessionClicked(*nextSession,
true);
1834 setStatsCursor((nextSession->end + nextSession->start) / 2);
1840 if (!isVisible(m_selectedSession) && !isVisible(m_selectedSession))
1841 adjustView((m_selectedSession.start + m_selectedSession.end) / 2.0);
1845bool Analyze::isVisible(
const Session &s)
const
1847 if (fullWidthCB->isChecked())
1849 return !((s.start < plotStart && s.end < plotStart) ||
1850 (s.start > (plotStart + plotWidth) && s.end > (plotStart + plotWidth)));
1853void Analyze::adjustView(
double time)
1855 if (!fullWidthCB->isChecked())
1857 plotStart = time - plotWidth / 2;
1861void Analyze::setStatsCursor(
double time)
1863 removeStatsCursor();
1868 const double top = statsPlot->yAxis->range().upper;
1869 const double bottom = statsPlot->yAxis->range().lower;
1877 const double top2 = timelinePlot->yAxis->range().upper;
1878 const double bottom2 = timelinePlot->yAxis->range().lower;
1881 timelineCursor = line2;
1883 cursorTimeOut->setText(
QString(
"%1s").arg(time));
1884 cursorClockTimeOut->setText(
QString(
"%1")
1885 .arg(clockTime(time).
toString(
"hh:mm:ss")));
1886 statsCursorTime = time;
1890void Analyze::removeStatsCursor()
1892 if (statsCursor !=
nullptr)
1893 statsPlot->removeItem(statsCursor);
1894 statsCursor =
nullptr;
1896 if (timelineCursor !=
nullptr)
1897 timelinePlot->removeItem(timelineCursor);
1898 timelineCursor =
nullptr;
1900 cursorTimeOut->setText(
"");
1901 cursorClockTimeOut->setText(
"");
1902 statsCursorTime = -1;
1906void Analyze::processStatsClick(
QMouseEvent *event,
bool doubleClick)
1908 Q_UNUSED(doubleClick);
1909 double xval = statsPlot->xAxis->pixelToCoord(
event->x());
1910 setStatsCursor(xval);
1914void Analyze::timelineMousePress(
QMouseEvent *event)
1916 processTimelineClick(event,
false);
1919void Analyze::timelineMouseDoubleClick(
QMouseEvent *event)
1921 processTimelineClick(event,
true);
1930 if (statsPlot->xAxis->pixelToCoord(
event->x()) < plotStart)
1935 processStatsClick(event,
false);
1938void Analyze::statsMouseDoubleClick(
QMouseEvent *event)
1940 processStatsClick(event,
true);
1950 if (statsPlot->xAxis->pixelToCoord(
event->x()) < plotStart)
1952 auto range = yAxis->range();
1954 yAxis->
setRange(range.lower + yDiff, range.upper + yDiff);
1956 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == yAxis)
1957 m_YAxisTool.replot(
true);
1960 processStatsClick(event,
false);
1964void Analyze::scroll(
int value)
1966 double pct =
static_cast<double>(value) / MAX_SCROLL_VALUE;
1967 plotStart = std::max(0.0, maxXValue * pct - plotWidth / 2.0);
1973void Analyze::scrollRight()
1975 plotStart = std::min(maxXValue - plotWidth / 5, plotStart + plotWidth / 5);
1976 fullWidthCB->setChecked(
false);
1980void Analyze::scrollLeft()
1982 plotStart = std::max(0.0, plotStart - plotWidth / 5);
1983 fullWidthCB->setChecked(
false);
1987void Analyze::replot(
bool adjustSlider)
1989 adjustTemporarySessions();
1990 if (fullWidthCB->isChecked())
1993 plotWidth = std::max(10.0, maxXValue);
1995 else if (keepCurrentCB->isChecked())
1997 plotStart = std::max(0.0, maxXValue - plotWidth);
2001 if (keepCurrentCB->isChecked() && statsCursor ==
nullptr)
2003 cursorTimeOut->setText(
QString(
"%1s").arg(maxXValue));
2004 cursorClockTimeOut->setText(
QString(
"%1")
2005 .arg(clockTime(maxXValue).
toString(
"hh:mm:ss")));
2007 analyzeSB->setPageStep(
2008 std::min(MAX_SCROLL_VALUE,
2009 static_cast<int>(MAX_SCROLL_VALUE * plotWidth / maxXValue)));
2012 double sliderCenter = plotStart + plotWidth / 2.0;
2013 analyzeSB->setSliderPosition(MAX_SCROLL_VALUE * (sliderCenter / maxXValue));
2016 timelinePlot->xAxis->setRange(plotStart, plotStart + plotWidth);
2017 timelinePlot->yAxis->setRange(0, LAST_Y);
2019 statsPlot->xAxis->setRange(plotStart, plotStart + plotWidth);
2022 if (statsPlot->isVisible())
2024 for (
auto &pairs : yAxisMap)
2027 if (statsPlot->graph(info.graphIndex)->visible() && info.rescale)
2036 dateTicker->setOffset(displayStartTime.toMSecsSinceEpoch() / 1000.0);
2038 timelinePlot->replot();
2039 statsPlot->replot();
2040 graphicsPlot->replot();
2042 if (activeYAxis !=
nullptr)
2045 const int widthDiff = statsPlot->axisRect()->width() - timelinePlot->axisRect()->width();
2046 const int paddingSize = activeYAxis->padding();
2047 constexpr int maxPadding = 100;
2049 const int newPad = std::min(maxPadding, std::max(0, paddingSize + widthDiff));
2050 if (newPad != paddingSize)
2052 activeYAxis->setPadding(newPad);
2053 statsPlot->replot();
2056 updateStatsValues();
2059void Analyze::statsYZoom(
double zoomAmount)
2061 auto axis = activeYAxis;
2063 auto range = axis->range();
2064 const double halfDiff = (range.upper - range.lower) / 2.0;
2065 const double middle = (range.upper + range.lower) / 2.0;
2066 axis->setRange(
QCPRange(middle - halfDiff * zoomAmount, middle + halfDiff * zoomAmount));
2067 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == axis)
2068 m_YAxisTool.replot(
true);
2070void Analyze::statsYZoomIn()
2073 statsPlot->replot();
2075void Analyze::statsYZoomOut()
2078 statsPlot->replot();
2085template<
typename Func>
2086void updateStat(
double time,
QLineEdit *valueBox,
QCPGraph *graph, Func func,
bool useLastRealVal =
false)
2088 auto begin = graph->
data()->findBegin(time);
2089 double timeDiffThreshold = 10000000.0;
2090 if ((begin != graph->
data()->constEnd()) &&
2091 (fabs(
begin->mainKey() - time) < timeDiffThreshold))
2093 double foundVal =
begin->mainValue();
2095 if (qIsNaN(foundVal))
2098 const double MAX_TIME_DIFF = 600;
2099 while (useLastRealVal && index >= 0)
2101 const double val = graph->
data()->at(index)->mainValue();
2102 const double t = graph->
data()->at(index)->mainKey();
2103 if (time - t > MAX_TIME_DIFF)
2115 valueBox->
setText(func(foundVal));
2123void Analyze::updateStatsValues()
2125 const double time = statsCursorTime < 0 ? maxXValue : statsCursorTime;
2131 updateStat(time, hfrOut, statsPlot->graph(HFR_GRAPH), d2Fcn,
true);
2132 updateStat(time, eccentricityOut, statsPlot->graph(ECCENTRICITY_GRAPH), d2Fcn,
true);
2133 updateStat(time, skyBgOut, statsPlot->graph(SKYBG_GRAPH), d1Fcn);
2134 updateStat(time, snrOut, statsPlot->graph(SNR_GRAPH), d1Fcn);
2135 updateStat(time, raOut, statsPlot->graph(RA_GRAPH), d2Fcn);
2136 updateStat(time, decOut, statsPlot->graph(DEC_GRAPH), d2Fcn);
2137 updateStat(time, driftOut, statsPlot->graph(DRIFT_GRAPH), d2Fcn);
2138 updateStat(time, rmsOut, statsPlot->graph(RMS_GRAPH), d2Fcn);
2139 updateStat(time, rmsCOut, statsPlot->graph(CAPTURE_RMS_GRAPH), d2Fcn);
2140 updateStat(time, azOut, statsPlot->graph(AZ_GRAPH), d1Fcn);
2141 updateStat(time, altOut, statsPlot->graph(ALT_GRAPH), d2Fcn);
2142 updateStat(time, temperatureOut, statsPlot->graph(TEMPERATURE_GRAPH), d2Fcn);
2144 auto asFcn = [](
double d) ->
QString {
return QString(
"%1\"").
arg(d, 0,
'f', 0); };
2145 updateStat(time, targetDistanceOut, statsPlot->graph(TARGET_DISTANCE_GRAPH), asFcn,
true);
2147 auto hmsFcn = [](
double d) ->
QString
2154 updateStat(time, mountRaOut, statsPlot->graph(MOUNT_RA_GRAPH), hmsFcn);
2155 auto dmsFcn = [](
double d) ->
QString {
dms dec;
dec.setD(d);
return dec.toDMSString(); };
2156 updateStat(time, mountDecOut, statsPlot->graph(MOUNT_DEC_GRAPH), dmsFcn);
2157 auto haFcn = [](
double d) ->
QString
2163 if (ha.
Hours() > 12.0)
2171 updateStat(time, mountHaOut, statsPlot->graph(MOUNT_HA_GRAPH), haFcn);
2174 updateStat(time, numStarsOut, statsPlot->graph(NUMSTARS_GRAPH), intFcn);
2175 updateStat(time, raPulseOut, statsPlot->graph(RA_PULSE_GRAPH), intFcn);
2176 updateStat(time, decPulseOut, statsPlot->graph(DEC_PULSE_GRAPH), intFcn);
2177 updateStat(time, numCaptureStarsOut, statsPlot->graph(NUM_CAPTURE_STARS_GRAPH), intFcn,
true);
2178 updateStat(time, medianOut, statsPlot->graph(MEDIAN_GRAPH), intFcn,
true);
2179 updateStat(time, focusPositionOut, statsPlot->graph(FOCUS_POSITION_GRAPH), intFcn);
2181 auto pierFcn = [](
double d) ->
QString
2183 return d == 0.0 ?
"W->E" : d == 1.0 ?
"E->W" :
"?";
2185 updateStat(time, pierSideOut, statsPlot->graph(PIER_SIDE_GRAPH), pierFcn);
2188void Analyze::initStatsCheckboxes()
2190 hfrCB->setChecked(Options::analyzeHFR());
2191 numCaptureStarsCB->setChecked(Options::analyzeNumCaptureStars());
2192 medianCB->setChecked(Options::analyzeMedian());
2193 eccentricityCB->setChecked(Options::analyzeEccentricity());
2194 numStarsCB->setChecked(Options::analyzeNumStars());
2195 skyBgCB->setChecked(Options::analyzeSkyBg());
2196 snrCB->setChecked(Options::analyzeSNR());
2197 temperatureCB->setChecked(Options::analyzeTemperature());
2198 focusPositionCB->setChecked(Options::focusPosition());
2199 targetDistanceCB->setChecked(Options::analyzeTargetDistance());
2200 raCB->setChecked(Options::analyzeRA());
2201 decCB->setChecked(Options::analyzeDEC());
2202 raPulseCB->setChecked(Options::analyzeRAp());
2203 decPulseCB->setChecked(Options::analyzeDECp());
2204 driftCB->setChecked(Options::analyzeDrift());
2205 rmsCB->setChecked(Options::analyzeRMS());
2206 rmsCCB->setChecked(Options::analyzeRMSC());
2207 mountRaCB->setChecked(Options::analyzeMountRA());
2208 mountDecCB->setChecked(Options::analyzeMountDEC());
2209 mountHaCB->setChecked(Options::analyzeMountHA());
2210 azCB->setChecked(Options::analyzeAz());
2211 altCB->setChecked(Options::analyzeAlt());
2212 pierSideCB->setChecked(Options::analyzePierSide());
2215void Analyze::zoomIn()
2217 if (plotWidth > 0.5)
2219 if (keepCurrentCB->isChecked())
2221 plotStart = std::max(0.0, maxXValue - plotWidth / 4.0);
2222 else if (statsCursorTime >= 0)
2224 plotStart = std::max(0.0, statsCursorTime - plotWidth / 4.0);
2227 plotStart += plotWidth / 4.0;
2228 plotWidth = plotWidth / 2.0;
2230 fullWidthCB->setChecked(
false);
2234void Analyze::zoomOut()
2236 if (plotWidth < maxXValue)
2238 plotStart = std::max(0.0, plotStart - plotWidth / 2.0);
2239 plotWidth = plotWidth * 2;
2241 fullWidthCB->setChecked(
false);
2248void setupAxisDefaults(
QCPAxis *axis)
2266 setupAxisDefaults(plot->
yAxis);
2267 setupAxisDefaults(plot->
xAxis);
2272void Analyze::initTimelinePlot()
2274 initQCP(timelinePlot);
2278 textTicker->addTick(CAPTURE_Y,
i18n(
"Capture"));
2279 textTicker->addTick(FOCUS_Y,
i18n(
"Focus"));
2280 textTicker->addTick(ALIGN_Y,
i18n(
"Align"));
2281 textTicker->addTick(GUIDE_Y,
i18n(
"Guide"));
2282 textTicker->addTick(MERIDIAN_MOUNT_FLIP_Y,
i18n(
"Flip"));
2283 textTicker->addTick(MOUNT_Y,
i18n(
"Mount"));
2284 textTicker->addTick(SCHEDULER_Y,
i18n(
"Job"));
2285 timelinePlot->yAxis->setTicker(textTicker);
2287 ADAPTIVE_FOCUS_GRAPH = initGraph(timelinePlot, timelinePlot->yAxis,
QCPGraph::lsNone,
Qt::red,
"adaptiveFocus");
2288 timelinePlot->graph(ADAPTIVE_FOCUS_GRAPH)->setPen(
QPen(
Qt::red, 2));
2293void Analyze::toggleGraph(
int graph_id,
bool show)
2295 statsPlot->graph(graph_id)->setVisible(show);
2297 statsPlot->graph(graph_id)->addToLegend();
2299 statsPlot->graph(graph_id)->removeFromLegend();
2316 if (key ==
nullptr)
return;
2317 auto axisEntry = yAxisMap.find(key);
2318 if (axisEntry == yAxisMap.end())
2319 yAxisMap.insert(std::make_pair(key, axisInfo));
2321 axisEntry->second = axisInfo;
2324template <
typename Func>
2329 const int num = initGraph(plot, yAxis, lineStyle, color, shortName);
2332 const bool autoAxis = YAxisInfo::isRescale(yAxis->range());
2333 updateYAxisMap(out,
YAxisInfo(yAxis, yAxis->range(), autoAxis, num, plot, cb, name, shortName, color));
2348 this->toggleGraph(num, show);
2358 updateYAxisMap(key, axisInfo);
2359 statsPlot->graph(axisInfo.graphIndex)->setPen(
QPen(color));
2360 Options::setAnalyzeStatsYAxis(serializeYAxes());
2364void Analyze::userSetLeftAxis(
QCPAxis *axis)
2367 Options::setAnalyzeStatsYAxis(serializeYAxes());
2373 updateYAxisMap(key, axisInfo);
2374 Options::setAnalyzeStatsYAxis(serializeYAxes());
2379void Analyze::yAxisRangeChanged(
const QCPRange &newRange)
2382 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == activeYAxis)
2383 m_YAxisTool.replot(
true);
2386void Analyze::setLeftAxis(
QCPAxis *axis)
2388 if (axis !=
nullptr && axis != activeYAxis)
2390 for (
const auto &pair : yAxisMap)
2393 QOverload<const QCPRange &>::of(&Analyze::yAxisRangeChanged));
2394 pair.second.axis->setVisible(
false);
2400 QOverload<const QCPRange &>::of(&Analyze::yAxisRangeChanged));
2406 if (info.checkBox && !info.checkBox->isChecked())
2409 info.checkBox->setChecked(
true);
2410 statsPlot->graph(info.graphIndex)->setVisible(
true);
2411 statsPlot->graph(info.graphIndex)->addToLegend();
2414 m_YAxisTool.reset(key, info, info.axis == activeYAxis);
2418QCPAxis *Analyze::newStatsYAxis(
const QString &label,
double lower,
double upper)
2424 setupAxisDefaults(axis);
2428bool Analyze::restoreYAxes(
const QString &encoding)
2430 constexpr int headerSize = 2;
2431 constexpr int itemSize = 5;
2433 if (items.
size() <= headerSize)
return false;
2434 if ((items.
size() - headerSize) % itemSize != 0)
return false;
2435 if (items[0] !=
"AnalyzeStatsYAxis1.0")
return false;
2438 const QString leftID =
"left=";
2439 if (!items[1].startsWith(leftID))
return false;
2441 if (
left.size() <= 0)
return false;
2442 for (
const auto &pair : yAxisMap)
2444 if (pair.second.axis->label() == left)
2446 setLeftAxis(pair.second.axis);
2452 for (
int i = headerSize; i < items.
size(); i += itemSize)
2454 const QString shortName = items[i];
2455 const double lower = items[i + 1].toDouble();
2456 const double upper = items[i + 2].toDouble();
2457 const bool rescale = items[i + 3] ==
"T";
2458 const QColor color(items[i + 4]);
2459 for (
auto &pair : yAxisMap)
2461 auto &info = pair.second;
2462 if (info.axis->label() == shortName)
2465 statsPlot->graph(info.graphIndex)->setPen(
QPen(color));
2466 info.rescale = rescale;
2468 info.axis->setRange(
2470 YAxisInfo::UPPER_RESCALE));
2472 info.axis->setRange(
QCPRange(lower, upper));
2481QString Analyze::serializeYAxes()
2483 QString encoding =
QString(
"AnalyzeStatsYAxis1.0,left=%1").
arg(activeYAxis->label());
2485 for (
const auto &pair : yAxisMap)
2488 const bool rescale = info.rescale;
2491 bool somethingChanged = (info.initialColor != info.color) ||
2492 (rescale != YAxisInfo::isRescale(info.initialRange)) ||
2493 (!rescale && info.axis->range() != info.initialRange);
2495 if (!somethingChanged)
continue;
2498 if (savedAxes.
contains(info.axis->label()))
continue;
2500 double lower = rescale ? YAxisInfo::LOWER_RESCALE : info.axis->range().lower;
2501 double upper = rescale ? YAxisInfo::UPPER_RESCALE : info.axis->range().upper;
2503 .arg(info.axis->label()).
arg(lower).
arg(upper)
2504 .
arg(info.rescale ?
"T" :
"F").
arg(info.color.name()));
2505 savedAxes.
append(info.axis->label());
2510void Analyze::initStatsPlot()
2515 statsPlot->yAxis->setVisible(
true);
2516 statsPlot->yAxis->setLabel(
"RA/DEC");
2517 statsPlot->yAxis->setRange(-2, 5);
2518 setLeftAxis(statsPlot->yAxis);
2521 statsPlot->legend->setVisible(
true);
2522 statsPlot->legend->setFont(
QFont(
"Helvetica", 6));
2523 statsPlot->legend->setTextColor(
Qt::white);
2525 statsPlot->legend->setBrush(
QBrush(
QColor(0, 0, 0, 50)));
2529 statsPlot->legend->setRowSpacing(-10);
2535 statsPlot->legend->setIconSize(10, 18);
2536 statsPlot->legend->setIconTextPadding(3);
2545 if (statsPlot->legend->font().pointSize() < 6)
2548 statsPlot->legend->setRowSpacing(-10);
2549 statsPlot->legend->setIconSize(10, 18);
2550 statsPlot->legend->setFont(
QFont(
"Helvetica", 6));
2551 statsPlot->legend->setBrush(
QBrush(
QColor(0, 0, 0, 50)));
2556 statsPlot->legend->setRowSpacing(-10);
2557 statsPlot->legend->setIconSize(5, 5);
2558 statsPlot->legend->setFont(
QFont(
"Helvetica", 1));
2559 statsPlot->legend->setBrush(
QBrush(
QColor(0, 0, 0, 0)));
2561 statsPlot->replot();
2567 QCPAxis *hfrAxis = newStatsYAxis(shortName, -2, 6);
2569 Options::setAnalyzeHFR, hfrOut);
2573 if (show && !Options::autoHFR())
2574 KSNotification::info(
2575 i18n(
"The \"Auto Compute HFR\" option in the KStars "
2576 "FITS options menu is not set. You won't get HFR values "
2577 "without it. Once you set it, newly captured images "
2578 "will have their HFRs computed."));
2581 shortName =
"#SubStars";
2582 QCPAxis *numCaptureStarsAxis = newStatsYAxis(shortName);
2584 "#Stars in Capture", shortName,
2585 numCaptureStarsCB, Options::setAnalyzeNumCaptureStars, numCaptureStarsOut);
2589 if (show && !Options::autoHFR())
2590 KSNotification::info(
2591 i18n(
"The \"Auto Compute HFR\" option in the KStars "
2592 "FITS options menu is not set. You won't get # stars in capture image values "
2593 "without it. Once you set it, newly captured images "
2594 "will have their stars detected."));
2597 shortName =
"median";
2598 QCPAxis *medianAxis = newStatsYAxis(shortName);
2600 medianCB, Options::setAnalyzeMedian, medianOut);
2603 QCPAxis *eccAxis = newStatsYAxis(shortName, 0, 1.0);
2605 shortName, eccentricityCB, Options::setAnalyzeEccentricity, eccentricityOut);
2606 shortName =
"#Stars";
2607 QCPAxis *numStarsAxis = newStatsYAxis(shortName);
2609 shortName, numStarsCB, Options::setAnalyzeNumStars, numStarsOut);
2610 shortName =
"SkyBG";
2611 QCPAxis *skyBgAxis = newStatsYAxis(shortName);
2613 shortName, skyBgCB, Options::setAnalyzeSkyBg, skyBgOut);
2616 QCPAxis *temperatureAxis = newStatsYAxis(shortName, -40, 40);
2618 temperatureCB, Options::setAnalyzeTemperature, temperatureOut);
2619 shortName =
"focus";
2620 QCPAxis *focusPositionAxis = newStatsYAxis(shortName);
2622 focusPositionCB, Options::setFocusPosition, focusPositionOut);
2623 shortName =
"tDist";
2624 QCPAxis *targetDistanceAxis = newStatsYAxis(shortName, 0, 60);
2625 TARGET_DISTANCE_GRAPH = initGraphAndCB(statsPlot, targetDistanceAxis,
QCPGraph::lsLine,
2627 "Distance to Target (arcsec)", shortName, targetDistanceCB, Options::setAnalyzeTargetDistance, targetDistanceOut);
2629 QCPAxis *snrAxis = newStatsYAxis(shortName, -100, 100);
2631 Options::setAnalyzeSNR, snrOut);
2634 RA_GRAPH = initGraphAndCB(statsPlot, statsPlot->yAxis,
QCPGraph::lsLine, raColor,
"Guider RA Drift", shortName, raCB,
2635 Options::setAnalyzeRA, raOut);
2638 DEC_GRAPH = initGraphAndCB(statsPlot, statsPlot->yAxis,
QCPGraph::lsLine, decColor,
"Guider DEC Drift", shortName, decCB,
2639 Options::setAnalyzeDEC, decOut);
2643 QCPAxis *pulseAxis = newStatsYAxis(shortName, -2 * 150, 5 * 150);
2644 RA_PULSE_GRAPH = initGraphAndCB(statsPlot, pulseAxis,
QCPGraph::lsLine, raPulseColor,
"RA Correction Pulse (ms)", shortName,
2645 raPulseCB, Options::setAnalyzeRAp, raPulseOut);
2651 DEC_PULSE_GRAPH = initGraphAndCB(statsPlot, pulseAxis,
QCPGraph::lsLine, decPulseColor,
"DEC Correction Pulse (ms)",
2652 shortName, decPulseCB, Options::setAnalyzeDECp, decPulseOut);
2655 shortName =
"Drift";
2657 shortName, driftCB, Options::setAnalyzeDrift, driftOut);
2659 RMS_GRAPH = initGraphAndCB(statsPlot, statsPlot->yAxis,
QCPGraph::lsLine,
Qt::red,
"Guider RMS Drift", shortName, rmsCB,
2660 Options::setAnalyzeRMS, rmsOut);
2663 "Guider RMS Drift (during capture)", shortName, rmsCCB,
2664 Options::setAnalyzeRMSC, rmsCOut);
2665 shortName =
"MOUNT_RA";
2666 QCPAxis *mountRaDecAxis = newStatsYAxis(shortName, -10, 370);
2668 MOUNT_RA_GRAPH = initGraphAndCB(statsPlot, mountRaDecAxis,
QCPGraph::lsLine,
Qt::red,
"Mount RA Degrees", shortName,
2669 mountRaCB, Options::setAnalyzeMountRA, mountRaOut);
2670 shortName =
"MOUNT_DEC";
2671 MOUNT_DEC_GRAPH = initGraphAndCB(statsPlot, mountRaDecAxis,
QCPGraph::lsLine,
Qt::red,
"Mount DEC Degrees", shortName,
2672 mountDecCB, Options::setAnalyzeMountDEC, mountDecOut);
2673 shortName =
"MOUNT_HA";
2674 MOUNT_HA_GRAPH = initGraphAndCB(statsPlot, mountRaDecAxis,
QCPGraph::lsLine,
Qt::red,
"Mount Hour Angle", shortName,
2675 mountHaCB, Options::setAnalyzeMountHA, mountHaOut);
2677 QCPAxis *azAxis = newStatsYAxis(shortName, -10, 370);
2679 Options::setAnalyzeAz, azOut);
2681 QCPAxis *altAxis = newStatsYAxis(shortName, 0, 90);
2683 Options::setAnalyzeAlt, altOut);
2684 shortName =
"PierSide";
2685 QCPAxis *pierSideAxis = newStatsYAxis(shortName, -2, 2);
2687 pierSideCB, Options::setAnalyzePierSide, pierSideOut);
2690 statsPlot->setMouseTracking(
false);
2693 dateTicker.reset(
new OffsetDateTimeTicker);
2694 dateTicker->setDateTimeFormat(
"hh:mm:ss");
2695 statsPlot->xAxis->setTicker(dateTicker);
2700 restoreYAxes(Options::analyzeStatsYAxis());
2704void Analyze::reset()
2710 guiderRms->resetFilter();
2711 captureRms->resetFilter();
2713 unhighlightTimelineItem();
2715 for (
int i = 0; i < statsPlot->graphCount(); ++i)
2716 statsPlot->graph(i)->data()->clear();
2717 statsPlot->clearItems();
2719 for (
int i = 0; i < timelinePlot->graphCount(); ++i)
2720 timelinePlot->graph(i)->data()->clear();
2721 timelinePlot->clearItems();
2723 resetGraphicsPlot();
2725 detailsTable->clear();
2726 QPalette p = detailsTable->palette();
2729 detailsTable->setPalette(p);
2731 inputValue->clear();
2733 captureSessions.clear();
2734 focusSessions.clear();
2735 guideSessions.clear();
2736 mountSessions.clear();
2737 alignSessions.clear();
2738 mountFlipSessions.clear();
2739 schedulerJobSessions.clear();
2741 numStarsOut->setText(
"");
2742 skyBgOut->setText(
"");
2743 snrOut->setText(
"");
2744 temperatureOut->setText(
"");
2745 focusPositionOut->setText(
"");
2746 targetDistanceOut->setText(
"");
2747 eccentricityOut->setText(
"");
2748 medianOut->setText(
"");
2749 numCaptureStarsOut->setText(
"");
2752 decOut->setText(
"");
2753 driftOut->setText(
"");
2754 rmsOut->setText(
"");
2755 rmsCOut->setText(
"");
2757 removeStatsCursor();
2758 removeTemporarySessions();
2760 resetCaptureState();
2761 resetAutofocusState();
2767 resetMountFlipState();
2768 resetSchedulerJob();
2773void Analyze::initGraphicsPlot()
2775 initQCP(graphicsPlot);
2776 FOCUS_GRAPHICS = initGraph(graphicsPlot, graphicsPlot->yAxis,
2778 graphicsPlot->graph(FOCUS_GRAPHICS)->setScatterStyle(
2780 errorBars =
new QCPErrorBars(graphicsPlot->xAxis, graphicsPlot->yAxis);
2781 errorBars->setAntialiased(
false);
2782 errorBars->setDataPlottable(graphicsPlot->graph(FOCUS_GRAPHICS));
2783 errorBars->setPen(
QPen(
QColor(180, 180, 180)));
2785 FOCUS_GRAPHICS_FINAL = initGraph(graphicsPlot, graphicsPlot->yAxis,
2787 graphicsPlot->graph(FOCUS_GRAPHICS_FINAL)->setScatterStyle(
2789 finalErrorBars =
new QCPErrorBars(graphicsPlot->xAxis, graphicsPlot->yAxis);
2790 finalErrorBars->setAntialiased(
false);
2791 finalErrorBars->setDataPlottable(graphicsPlot->graph(FOCUS_GRAPHICS_FINAL));
2792 finalErrorBars->setPen(
QPen(
QColor(180, 180, 180)));
2794 FOCUS_GRAPHICS_CURVE = initGraph(graphicsPlot, graphicsPlot->yAxis,
2799 GUIDER_GRAPHICS = initGraph(graphicsPlot, graphicsPlot->yAxis,
2801 graphicsPlot->graph(GUIDER_GRAPHICS)->setScatterStyle(
2808 resetGraphicsPlot();
2809 auto graph = graphicsPlot->graph(FOCUS_GRAPHICS);
2810 auto finalGraph = graphicsPlot->graph(FOCUS_GRAPHICS_FINAL);
2811 double maxHfr = -1e8, maxPosition = -1e8, minHfr = 1e8, minPosition = 1e8;
2813 for (
int i = 0; i < positions.
size(); ++i)
2816 if (success && i == positions.
size() - 1)
2818 finalGraph->addData(positions[i], hfrs[i]);
2822 double sd = (weights[i] <= 0.0) ? 0.0 : std::pow(weights[i], -0.5);
2828 graph->
addData(positions[i], hfrs[i]);
2831 double sd = (weights[i] <= 0.0) ? 0.0 : std::pow(weights[i], -0.5);
2835 maxHfr = std::max(maxHfr, hfrs[i]);
2836 minHfr = std::min(minHfr, hfrs[i]);
2837 maxPosition = std::max(maxPosition, positions[i]);
2838 minPosition = std::min(minPosition, positions[i]);
2841 for (
int i = 0; i < positions.
size(); ++i)
2846 textLabel->position->
setCoords(positions[i], hfrs[i]);
2863 errorBars->setVisible(useWeights);
2864 finalErrorBars->setVisible(useWeights);
2867 errorBars->setData(errorData);
2868 finalErrorBars->setData(finalErrorData);
2871 const double xRange = maxPosition - minPosition;
2872 const double xPadding = hfrs.
size() > 1 ? xRange / (hfrs.
size() - 1.0) : 10;
2875 if (curve.
size() > 0)
2877 CurveFitting curveFitting(curve);
2878 const double interval = xRange / 20.0;
2879 auto curveGraph = graphicsPlot->graph(FOCUS_GRAPHICS_CURVE);
2880 for (
double x = minPosition - xPadding ; x <= maxPosition + xPadding; x += interval)
2881 curveGraph->addData(x, curveFitting.f(x));
2885 plotTitle->setColor(
QColor(255, 255, 255));
2888 plotTitle->position->setCoords(0.5, 0);
2889 plotTitle->setFont(
QFont(font().family(), 10));
2890 plotTitle->setVisible(
true);
2891 plotTitle->setText(title);
2894 const double upper = 1.5 * maxHfr;
2895 const double lower = minHfr - (0.25 * (upper - minHfr));
2896 graphicsPlot->xAxis->setRange(minPosition - xPadding, maxPosition + xPadding);
2897 graphicsPlot->yAxis->setRange(lower, upper);
2898 graphicsPlot->replot();
2901void Analyze::resetGraphicsPlot()
2903 for (
int i = 0; i < graphicsPlot->graphCount(); ++i)
2904 graphicsPlot->graph(i)->data()->clear();
2905 graphicsPlot->clearItems();
2906 errorBars->data().clear();
2907 finalErrorBars->data().clear();
2910void Analyze::displayFITS(
const QString &filename)
2914 if (fitsViewer.isNull())
2917 fitsViewerTabID = fitsViewer->loadFile(url);
2918 connect(fitsViewer.get(), &FITSViewer::terminated,
this, [
this]()
2925 if (fitsViewer->tabExists(fitsViewerTabID))
2926 fitsViewer->updateFile(url, fitsViewerTabID);
2928 fitsViewerTabID = fitsViewer->loadFile(url);
2934void Analyze::helpMessage()
2946double Analyze::logTime(
const QDateTime &time)
2948 if (!logInitialized)
2950 return (time.
toMSecsSinceEpoch() - analyzeStartTime.toMSecsSinceEpoch()) / 1000.0;
2956double Analyze::logTime()
2963QDateTime Analyze::clockTime(
double logSeconds)
2965 return displayStartTime.
addMSecs(logSeconds * 1000.0);
2970void Analyze::saveMessage(
const QString &type,
const QString &message)
2975 .arg(message.
size() > 0 ?
"," :
"", message));
2980void Analyze::restart()
2982 qCDebug(KSTARS_EKOS_ANALYZE) <<
"(Re)starting Analyze";
2989 inputCombo->setCurrentIndex(0);
2990 inputValue->setText(
"");
2991 maxXValue = readDataFromFile(logFilename);
2992 runtimeDisplay =
true;
2993 fullWidthCB->setChecked(
true);
2994 fullWidthCB->setVisible(
true);
2995 fullWidthCB->setDisabled(
false);
3000void Analyze::startLog()
3003 startTimeInitialized =
true;
3005 displayStartTime = analyzeStartTime;
3010 logFile.reset(
new QFile);
3012 logFile->setFileName(logFilename);
3016 logInitialized =
true;
3018 appendToLog(
QString(
"#KStars version %1. Analyze log version 1.0.\n\n")
3019 .arg(KSTARS_VERSION));
3020 appendToLog(
QString(
"%1,%2,%3\n")
3021 .arg(
"AnalyzeStartTime", analyzeStartTime.toString(timeFormat), analyzeStartTime.timeZoneAbbreviation()));
3024void Analyze::appendToLog(
const QString &lines)
3026 if (!logInitialized)
3034void Analyze::updateMaxX(
double time)
3036 maxXValue = std::max(time, maxXValue);
3044void Analyze::removeTemporarySession(
Session * session)
3046 if (session->rect !=
nullptr)
3047 timelinePlot->removeItem(session->rect);
3048 session->rect =
nullptr;
3054void Analyze::removeTemporarySessions()
3056 removeTemporarySession(&temporaryCaptureSession);
3057 removeTemporarySession(&temporaryMountFlipSession);
3058 removeTemporarySession(&temporaryFocusSession);
3059 removeTemporarySession(&temporaryGuideSession);
3060 removeTemporarySession(&temporaryMountSession);
3061 removeTemporarySession(&temporaryAlignSession);
3062 removeTemporarySession(&temporarySchedulerJobSession);
3066void Analyze::addTemporarySession(
Session * session,
double time,
double duration,
3067 int y_offset,
const QBrush &brush)
3069 if (time < 0)
return;
3070 removeTemporarySession(session);
3071 session->rect = addSession(time, time + duration, y_offset, brush);
3072 session->start = time;
3073 session->end = time + duration;
3074 session->offset = y_offset;
3075 session->temporaryBrush = brush;
3076 updateMaxX(time + duration);
3082void Analyze::adjustTemporarySession(
Session * session)
3084 if (session->rect !=
nullptr && session->end < maxXValue)
3086 QBrush brush = session->temporaryBrush;
3087 double start = session->start;
3088 int offset = session->offset;
3089 addTemporarySession(session, start, maxXValue - start, offset, brush);
3094void Analyze::adjustTemporarySessions()
3096 adjustTemporarySession(&temporaryCaptureSession);
3097 adjustTemporarySession(&temporaryMountFlipSession);
3098 adjustTemporarySession(&temporaryFocusSession);
3099 adjustTemporarySession(&temporaryGuideSession);
3100 adjustTemporarySession(&temporaryMountSession);
3101 adjustTemporarySession(&temporaryAlignSession);
3102 adjustTemporarySession(&temporarySchedulerJobSession);
3107void Analyze::captureStarting(
double exposureSeconds,
const QString &filter)
3109 saveMessage(
"CaptureStarting",
3111 processCaptureStarting(logTime(), exposureSeconds, filter);
3116void Analyze::processCaptureStarting(
double time,
double exposureSeconds,
const QString &filter)
3118 captureStartedTime = time;
3119 captureStartedFilter =
filter;
3122 addTemporarySession(&temporaryCaptureSession, time, 1, CAPTURE_Y, temporaryBrush);
3123 temporaryCaptureSession.duration = exposureSeconds;
3124 temporaryCaptureSession.filter =
filter;
3128void Analyze::captureComplete(
const QVariantMap &metadata)
3130 auto filename = metadata[
"filename"].toString();
3131 auto exposure = metadata[
"exposure"].toDouble();
3132 auto filter = metadata[
"filter"].toString();
3133 auto hfr = metadata[
"hfr"].toDouble();
3134 auto starCount = metadata[
"starCount"].toInt();
3135 auto median = metadata[
"median"].toDouble();
3136 auto eccentricity = metadata[
"eccentricity"].toDouble();
3138 saveMessage(
"CaptureComplete",
3139 QString(
"%1,%2,%3,%4,%5,%6,%7")
3144 if (runtimeDisplay && captureStartedTime >= 0)
3145 processCaptureComplete(logTime(), filename, exposure, filter, hfr, starCount, median, eccentricity);
3148void Analyze::processCaptureComplete(
double time,
const QString &filename,
3149 double exposureSeconds,
const QString &filter,
double hfr,
3150 int numStars,
int median,
double eccentricity,
bool batchMode)
3152 removeTemporarySession(&temporaryCaptureSession);
3154 if (captureStartedTime < 0)
3157 if (filterStripeBrush(filter, &stripe))
3158 addSession(captureStartedTime, time, CAPTURE_Y, successBrush, &stripe);
3160 addSession(captureStartedTime, time, CAPTURE_Y, successBrush,
nullptr);
3161 auto session = CaptureSession(captureStartedTime, time,
nullptr,
false,
3162 filename, exposureSeconds, filter);
3163 captureSessions.add(session);
3164 addHFR(hfr, numStars, median, eccentricity, time, captureStartedTime);
3168 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3169 captureSessionClicked(session,
false);
3172 previousCaptureStartedTime = captureStartedTime;
3173 previousCaptureCompletedTime = time;
3174 captureStartedTime = -1;
3177void Analyze::captureAborted(
double exposureSeconds)
3179 saveMessage(
"CaptureAborted",
3181 if (runtimeDisplay && captureStartedTime >= 0)
3182 processCaptureAborted(logTime(), exposureSeconds);
3185void Analyze::processCaptureAborted(
double time,
double exposureSeconds,
bool batchMode)
3187 removeTemporarySession(&temporaryCaptureSession);
3188 double duration = time - captureStartedTime;
3189 if (captureStartedTime >= 0 &&
3190 duration < (exposureSeconds + 30) &&
3195 addSession(captureStartedTime, time, CAPTURE_Y, failureBrush);
3196 auto session = CaptureSession(captureStartedTime, time,
nullptr,
true,
"",
3197 exposureSeconds, captureStartedFilter);
3198 captureSessions.add(session);
3202 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3203 captureSessionClicked(session,
false);
3206 captureStartedTime = -1;
3208 previousCaptureStartedTime = -1;
3209 previousCaptureCompletedTime = -1;
3212void Analyze::resetCaptureState()
3214 captureStartedTime = -1;
3215 captureStartedFilter =
"";
3217 numCaptureStarsMax = 1;
3218 previousCaptureStartedTime = -1;
3219 previousCaptureCompletedTime = -1;
3222void Analyze::autofocusStarting(
double temperature,
const QString &filter,
const AutofocusReason reason,
3225 saveMessage(
"AutofocusStarting",
3231 processAutofocusStarting(logTime(), temperature, filter, reason, reasonInfo);
3234void Analyze::processAutofocusStarting(
double time,
double temperature,
const QString &filter,
const AutofocusReason reason,
3237 autofocusStartedTime = time;
3238 autofocusStartedFilter =
filter;
3239 autofocusStartedTemperature = temperature;
3240 autofocusStartedReason = reason;
3241 autofocusStartedReasonInfo = reasonInfo;
3243 addTemperature(temperature, time);
3246 addTemporarySession(&temporaryFocusSession, time, 1, FOCUS_Y, temporaryBrush);
3247 temporaryFocusSession.temperature = temperature;
3248 temporaryFocusSession.filter =
filter;
3249 temporaryFocusSession.reason = reason;
3252void Analyze::adaptiveFocusComplete(
const QString &filter,
double temperature,
double tempTicks,
3253 double altitude,
double altTicks,
int prevPosError,
int thisPosError,
3254 int totalTicks,
int position,
bool focuserMoved)
3256 saveMessage(
"AdaptiveFocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8,%9,%10").arg(filter).arg(temperature, 0,
'f', 2)
3257 .arg(tempTicks, 0,
'f', 2).arg(altitude, 0,
'f', 2).arg(altTicks, 0,
'f', 2).arg(prevPosError)
3258 .arg(thisPosError).arg(totalTicks).arg(position).arg(focuserMoved ? 1 : 0));
3261 processAdaptiveFocusComplete(logTime(), filter, temperature, tempTicks, altitude, altTicks, prevPosError, thisPosError,
3262 totalTicks, position, focuserMoved);
3265void Analyze::processAdaptiveFocusComplete(
double time,
const QString &filter,
double temperature,
double tempTicks,
3266 double altitude,
double altTicks,
int prevPosError,
int thisPosError,
int totalTicks,
int position,
3267 bool focuserMoved,
bool batchMode)
3269 removeTemporarySession(&temporaryFocusSession);
3271 addFocusPosition(position, time);
3276 if (!focuserMoved || (abs(tempTicks) < 1.00 && abs(altTicks) < 1.0 && prevPosError == 0 && thisPosError == 0))
3280 timelinePlot->graph(ADAPTIVE_FOCUS_GRAPH)->addData(time, FOCUS_Y);
3283 constexpr int artificialInterval = 10;
3284 auto session = FocusSession(time - artificialInterval, time + artificialInterval,
nullptr,
3285 filter, temperature, tempTicks, altitude, altTicks, prevPosError, thisPosError, totalTicks,
3287 focusSessions.add(session);
3292 autofocusStartedTime = -1;
3295void Analyze::autofocusComplete(
const double temperature,
const QString &filter,
const QString &points,
3296 const bool useWeights,
const QString &curve,
const QString &rawTitle)
3311 QVariant reasonV = autofocusStartedReason;
3313 QString reasonInfo = autofocusStartedReasonInfo;
3315 if (curve.
size() == 0)
3316 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6").arg(temp, reason, reasonInfo, filter, points, weights));
3317 else if (title.
size() == 0)
3318 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7").arg(temp, reason, reasonInfo, filter, points, weights,
3321 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8").arg(temp, reason, reasonInfo, filter, points, weights,
3324 if (runtimeDisplay && autofocusStartedTime >= 0)
3325 processAutofocusCompleteV2(logTime(), temperature, filter, autofocusStartedReason, reasonInfo, points, useWeights, curve,
3330void Analyze::processAutofocusCompleteV2(
double time,
const double temperature,
const QString &filter,
3331 const AutofocusReason reason,
const QString &reasonInfo,
3332 const QString &points,
const bool useWeights,
const QString &curve,
const QString &title,
bool batchMode)
3334 removeTemporarySession(&temporaryFocusSession);
3336 if (autofocusStartedTime >= 0)
3339 if (filterStripeBrush(filter, &stripe))
3340 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush, &stripe);
3342 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush,
nullptr);
3344 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
true, temperature, filter, reason, reasonInfo, points,
3345 useWeights, curve, title, AutofocusFailReason::FOCUS_FAIL_NONE,
"");
3346 focusSessions.add(session);
3347 addFocusPosition(session.focusPosition(), autofocusStartedTime);
3350 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3351 focusSessionClicked(session,
false);
3355 autofocusStartedTime = -1;
3359void Analyze::processAutofocusComplete(
double time,
const QString &filter,
const QString &points,
3362 removeTemporarySession(&temporaryFocusSession);
3363 if (autofocusStartedTime < 0)
3367 if (filterStripeBrush(filter, &stripe))
3368 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush, &stripe);
3370 addSession(autofocusStartedTime, time, FOCUS_Y, successBrush,
nullptr);
3371 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
true,
3372 autofocusStartedTemperature, filter, points, curve, title);
3373 focusSessions.add(session);
3374 addFocusPosition(session.focusPosition(), autofocusStartedTime);
3378 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3379 focusSessionClicked(session,
false);
3382 autofocusStartedTime = -1;
3385void Analyze::autofocusAborted(
const QString &filter,
const QString &points,
const bool useWeights,
3386 const AutofocusFailReason failCode,
const QString failCodeInfo)
3389 QVariant reasonV = autofocusStartedReason;
3391 QString reasonInfo = autofocusStartedReasonInfo;
3393 QVariant failReasonV =
static_cast<int>(failCode);
3395 saveMessage(
"AutofocusAborted",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8").arg(temperature, reason, reasonInfo, filter, points,
3396 weights, failReason, failCodeInfo));
3397 if (runtimeDisplay && autofocusStartedTime >= 0)
3398 processAutofocusAbortedV2(logTime(), autofocusStartedTemperature, filter, autofocusStartedReason, reasonInfo, points,
3399 useWeights, failCode, failCodeInfo);
3403void Analyze::processAutofocusAbortedV2(
double time,
double temperature,
const QString &filter,
3404 const AutofocusReason reason,
const QString &reasonInfo,
const QString &points,
const bool useWeights,
3405 const AutofocusFailReason failCode,
const QString failCodeInfo,
bool batchMode)
3407 Q_UNUSED(temperature);
3408 removeTemporarySession(&temporaryFocusSession);
3409 double duration = time - autofocusStartedTime;
3410 if (autofocusStartedTime >= 0 && duration < 1000)
3413 addSession(autofocusStartedTime, time, FOCUS_Y, failureBrush);
3414 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
false, autofocusStartedTemperature, filter, reason,
3415 reasonInfo, points, useWeights,
"",
"", failCode, failCodeInfo);
3416 focusSessions.add(session);
3420 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3421 focusSessionClicked(session,
false);
3424 autofocusStartedTime = -1;
3429void Analyze::processAutofocusAborted(
double time,
const QString &filter,
const QString &points,
bool batchMode)
3431 removeTemporarySession(&temporaryFocusSession);
3432 double duration = time - autofocusStartedTime;
3433 if (autofocusStartedTime >= 0 && duration < 1000)
3436 addSession(autofocusStartedTime, time, FOCUS_Y, failureBrush);
3437 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
false,
3438 autofocusStartedTemperature, filter, points,
"",
"");
3439 focusSessions.add(session);
3443 if (runtimeDisplay && keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3444 focusSessionClicked(session,
false);
3447 autofocusStartedTime = -1;
3451void Analyze::resetAutofocusState()
3453 autofocusStartedTime = -1;
3454 autofocusStartedFilter =
"";
3455 autofocusStartedTemperature = 0;
3456 autofocusStartedReason = AutofocusReason::FOCUS_NONE;
3457 autofocusStartedReasonInfo =
"";
3464Ekos::GuideState stringToGuideState(
const QString &str)
3466 if (str ==
i18n(
"Idle"))
3468 else if (str ==
i18n(
"Aborted"))
3469 return GUIDE_ABORTED;
3470 else if (str ==
i18n(
"Connected"))
3471 return GUIDE_CONNECTED;
3472 else if (str ==
i18n(
"Disconnected"))
3473 return GUIDE_DISCONNECTED;
3474 else if (str ==
i18n(
"Capturing"))
3475 return GUIDE_CAPTURE;
3476 else if (str ==
i18n(
"Looping"))
3477 return GUIDE_LOOPING;
3478 else if (str ==
i18n(
"Subtracting"))
3480 else if (str ==
i18n(
"Subframing"))
3481 return GUIDE_SUBFRAME;
3482 else if (str ==
i18n(
"Selecting star"))
3483 return GUIDE_STAR_SELECT;
3484 else if (str ==
i18n(
"Calibrating"))
3485 return GUIDE_CALIBRATING;
3486 else if (str ==
i18n(
"Calibration error"))
3487 return GUIDE_CALIBRATION_ERROR;
3488 else if (str ==
i18n(
"Calibrated"))
3489 return GUIDE_CALIBRATION_SUCCESS;
3490 else if (str ==
i18n(
"Guiding"))
3491 return GUIDE_GUIDING;
3492 else if (str ==
i18n(
"Suspended"))
3493 return GUIDE_SUSPENDED;
3494 else if (str ==
i18n(
"Reacquiring"))
3495 return GUIDE_REACQUIRE;
3496 else if (str ==
i18n(
"Dithering"))
3497 return GUIDE_DITHERING;
3498 else if (str ==
i18n(
"Manual Dithering"))
3499 return GUIDE_MANUAL_DITHERING;
3500 else if (str ==
i18n(
"Dithering error"))
3501 return GUIDE_DITHERING_ERROR;
3502 else if (str ==
i18n(
"Dithering successful"))
3503 return GUIDE_DITHERING_SUCCESS;
3504 else if (str ==
i18n(
"Settling"))
3505 return GUIDE_DITHERING_SETTLE;
3510Analyze::SimpleGuideState convertGuideState(Ekos::GuideState state)
3516 case GUIDE_CONNECTED:
3517 case GUIDE_DISCONNECTED:
3519 return Analyze::G_IDLE;
3521 return Analyze::G_GUIDING;
3524 case GUIDE_SUBFRAME:
3525 case GUIDE_STAR_SELECT:
3526 return Analyze::G_IGNORE;
3527 case GUIDE_CALIBRATING:
3528 case GUIDE_CALIBRATION_ERROR:
3529 case GUIDE_CALIBRATION_SUCCESS:
3530 return Analyze::G_CALIBRATING;
3531 case GUIDE_SUSPENDED:
3532 case GUIDE_REACQUIRE:
3533 return Analyze::G_SUSPENDED;
3534 case GUIDE_DITHERING:
3535 case GUIDE_MANUAL_DITHERING:
3536 case GUIDE_DITHERING_ERROR:
3537 case GUIDE_DITHERING_SUCCESS:
3538 case GUIDE_DITHERING_SETTLE:
3539 return Analyze::G_DITHERING;
3542 return Analyze::G_IDLE;
3545const QBrush guideBrush(Analyze::SimpleGuideState simpleState)
3547 switch (simpleState)
3549 case Analyze::G_IDLE:
3550 case Analyze::G_IGNORE:
3553 case Analyze::G_GUIDING:
3554 return successBrush;
3555 case Analyze::G_CALIBRATING:
3556 return progressBrush;
3557 case Analyze::G_SUSPENDED:
3558 return stoppedBrush;
3559 case Analyze::G_DITHERING:
3560 return progress2Brush;
3568void Analyze::guideState(Ekos::GuideState state)
3570 QString str = getGuideStatusString(state);
3571 saveMessage(
"GuideState", str);
3573 processGuideState(logTime(), str);
3576void Analyze::processGuideState(
double time,
const QString &stateStr,
bool batchMode)
3578 Ekos::GuideState gstate = stringToGuideState(stateStr);
3579 SimpleGuideState state = convertGuideState(gstate);
3580 if (state == G_IGNORE)
3582 if (state == lastGuideStateStarted)
3585 if (guideStateStartedTime >= 0)
3587 if (lastGuideStateStarted != G_IDLE)
3590 addSession(guideStateStartedTime, time, GUIDE_Y, guideBrush(lastGuideStateStarted));
3591 guideSessions.add(GuideSession(guideStateStartedTime, time,
nullptr, lastGuideStateStarted));
3594 if (state == G_GUIDING)
3596 addTemporarySession(&temporaryGuideSession, time, 1, GUIDE_Y, successBrush);
3597 temporaryGuideSession.simpleState = state;
3600 removeTemporarySession(&temporaryGuideSession);
3602 guideStateStartedTime = time;
3603 lastGuideStateStarted = state;
3609void Analyze::resetGuideState()
3611 lastGuideStateStarted = G_IDLE;
3612 guideStateStartedTime = -1;
3615void Analyze::newTemperature(
double temperatureDelta,
double temperature)
3617 Q_UNUSED(temperatureDelta);
3618 if (temperature > -200 && temperature != lastTemperature)
3621 lastTemperature = temperature;
3623 processTemperature(logTime(), temperature);
3627void Analyze::processTemperature(
double time,
double temperature,
bool batchMode)
3629 addTemperature(temperature, time);
3635void Analyze::resetTemperature()
3637 lastTemperature = -1000;
3640void Analyze::newTargetDistance(
double targetDistance)
3644 processTargetDistance(logTime(), targetDistance);
3647void Analyze::processTargetDistance(
double time,
double targetDistance,
bool batchMode)
3649 addTargetDistance(targetDistance, time);
3655void Analyze::guideStats(
double raError,
double decError,
int raPulse,
int decPulse,
3656 double snr,
double skyBg,
int numStars)
3658 saveMessage(
"GuideStats",
QString(
"%1,%2,%3,%4,%5,%6,%7")
3666 processGuideStats(logTime(), raError, decError, raPulse, decPulse, snr, skyBg, numStars);
3669void Analyze::processGuideStats(
double time,
double raError,
double decError,
3670 int raPulse,
int decPulse,
double snr,
double skyBg,
int numStars,
bool batchMode)
3672 addGuideStats(raError, decError, raPulse, decPulse, snr, numStars, skyBg, time);
3678void Analyze::resetGuideStats()
3680 lastGuideStatsTime = -1;
3681 lastCaptureRmsTime = -1;
3691AlignState convertAlignState(
const QString &str)
3693 for (
int i = 0; i < alignStates.size(); ++i)
3695 if (str == alignStates[i].
toString())
3696 return static_cast<AlignState
>(i);
3701const QBrush alignBrush(AlignState state)
3709 return successBrush;
3711 return failureBrush;
3713 return progress3Brush;
3715 return progress2Brush;
3717 return progressBrush;
3719 return progress4Brush;
3721 return failureBrush;
3730void Analyze::alignState(AlignState state)
3732 if (state == lastAlignStateReceived)
3734 lastAlignStateReceived = state;
3736 QString stateStr = getAlignStatusString(state);
3737 saveMessage(
"AlignState", stateStr);
3739 processAlignState(logTime(), stateStr);
3743void Analyze::processAlignState(
double time,
const QString &statusString,
bool batchMode)
3745 AlignState state = convertAlignState(statusString);
3747 if (state == lastAlignStateStarted)
3750 bool lastStateInteresting = (lastAlignStateStarted ==
ALIGN_PROGRESS ||
3753 if (lastAlignStateStartedTime >= 0 && lastStateInteresting)
3755 if (state == ALIGN_COMPLETE || state == ALIGN_FAILED || state == ALIGN_ABORTED)
3758 addSession(lastAlignStateStartedTime, time, ALIGN_Y, alignBrush(state));
3759 alignSessions.add(AlignSession(lastAlignStateStartedTime, time,
nullptr, state));
3763 addSession(lastAlignStateStartedTime, time, ALIGN_Y, alignBrush(lastAlignStateStarted));
3764 alignSessions.add(AlignSession(lastAlignStateStartedTime, time,
nullptr, lastAlignStateStarted));
3769 if (stateInteresting)
3771 addTemporarySession(&temporaryAlignSession, time, 1, ALIGN_Y, temporaryBrush);
3772 temporaryAlignSession.state = state;
3775 removeTemporarySession(&temporaryAlignSession);
3777 lastAlignStateStartedTime = time;
3778 lastAlignStateStarted = state;
3785void Analyze::resetAlignState()
3789 lastAlignStateStartedTime = -1;
3795const QBrush mountBrush(ISD::Mount::Status state)
3799 case ISD::Mount::MOUNT_IDLE:
3801 case ISD::Mount::MOUNT_ERROR:
3802 return failureBrush;
3803 case ISD::Mount::MOUNT_MOVING:
3804 case ISD::Mount::MOUNT_SLEWING:
3805 return progressBrush;
3806 case ISD::Mount::MOUNT_TRACKING:
3807 return successBrush;
3808 case ISD::Mount::MOUNT_PARKING:
3809 return stoppedBrush;
3810 case ISD::Mount::MOUNT_PARKED:
3811 return stopped2Brush;
3821void Analyze::mountState(ISD::Mount::Status state)
3823 QString statusString = mountStatusString(state);
3824 saveMessage(
"MountState", statusString);
3826 processMountState(logTime(), statusString);
3829void Analyze::processMountState(
double time,
const QString &statusString,
bool batchMode)
3831 ISD::Mount::Status state = toMountStatus(statusString);
3832 if (mountStateStartedTime >= 0 && lastMountState != ISD::Mount::MOUNT_IDLE)
3834 addSession(mountStateStartedTime, time, MOUNT_Y, mountBrush(lastMountState));
3835 mountSessions.add(MountSession(mountStateStartedTime, time,
nullptr, lastMountState));
3838 if (state != ISD::Mount::MOUNT_IDLE)
3840 addTemporarySession(&temporaryMountSession, time, 1, MOUNT_Y,
3841 (state == ISD::Mount::MOUNT_TRACKING) ? successBrush : temporaryBrush);
3842 temporaryMountSession.state = state;
3845 removeTemporarySession(&temporaryMountSession);
3847 mountStateStartedTime = time;
3848 lastMountState = state;
3854void Analyze::resetMountState()
3856 mountStateStartedTime = -1;
3857 lastMountState = ISD::Mount::Status::MOUNT_IDLE;
3861void Analyze::mountCoords(
const SkyPoint &position, ISD::Mount::PierSide pierSide,
const dms &haValue)
3863 double ra = position.
ra().Degrees();
3864 double dec = position.
dec().Degrees();
3865 double ha = haValue.
Degrees();
3866 double az = position.
az().Degrees();
3867 double alt = position.
alt().Degrees();
3870 constexpr double MIN_DEGREES_CHANGE = 0.25;
3871 if ((fabs(ra - lastMountRa) > MIN_DEGREES_CHANGE) ||
3872 (fabs(dec - lastMountDec) > MIN_DEGREES_CHANGE) ||
3873 (fabs(ha - lastMountHa) > MIN_DEGREES_CHANGE) ||
3874 (fabs(az - lastMountAz) > MIN_DEGREES_CHANGE) ||
3875 (fabs(alt - lastMountAlt) > MIN_DEGREES_CHANGE) ||
3876 (pierSide != lastMountPierSide))
3878 saveMessage(
"MountCoords",
QString(
"%1,%2,%3,%4,%5,%6")
3885 processMountCoords(logTime(), ra, dec, az, alt, pierSide, ha);
3892 lastMountPierSide = pierSide;
3896void Analyze::processMountCoords(
double time,
double ra,
double dec,
double az,
3897 double alt,
int pierSide,
double ha,
bool batchMode)
3899 addMountCoords(ra, dec, az, alt, pierSide, ha, time);
3905void Analyze::resetMountCoords()
3912 lastMountPierSide = -1;
3919MeridianFlipState::MeridianFlipMountState convertMountFlipState(
const QString &statusStr)
3921 if (statusStr ==
"MOUNT_FLIP_NONE")
3922 return MeridianFlipState::MOUNT_FLIP_NONE;
3923 else if (statusStr ==
"MOUNT_FLIP_PLANNED")
3924 return MeridianFlipState::MOUNT_FLIP_PLANNED;
3925 else if (statusStr ==
"MOUNT_FLIP_WAITING")
3926 return MeridianFlipState::MOUNT_FLIP_WAITING;
3927 else if (statusStr ==
"MOUNT_FLIP_ACCEPTED")
3928 return MeridianFlipState::MOUNT_FLIP_ACCEPTED;
3929 else if (statusStr ==
"MOUNT_FLIP_RUNNING")
3930 return MeridianFlipState::MOUNT_FLIP_RUNNING;
3931 else if (statusStr ==
"MOUNT_FLIP_COMPLETED")
3932 return MeridianFlipState::MOUNT_FLIP_COMPLETED;
3933 else if (statusStr ==
"MOUNT_FLIP_ERROR")
3934 return MeridianFlipState::MOUNT_FLIP_ERROR;
3935 return MeridianFlipState::MOUNT_FLIP_ERROR;
3938QBrush mountFlipStateBrush(MeridianFlipState::MeridianFlipMountState state)
3942 case MeridianFlipState::MOUNT_FLIP_NONE:
3944 case MeridianFlipState::MOUNT_FLIP_PLANNED:
3945 return stoppedBrush;
3946 case MeridianFlipState::MOUNT_FLIP_WAITING:
3947 return stopped2Brush;
3948 case MeridianFlipState::MOUNT_FLIP_ACCEPTED:
3949 return progressBrush;
3950 case MeridianFlipState::MOUNT_FLIP_RUNNING:
3951 return progress2Brush;
3952 case MeridianFlipState::MOUNT_FLIP_COMPLETED:
3953 return successBrush;
3954 case MeridianFlipState::MOUNT_FLIP_ERROR:
3955 return failureBrush;
3962void Analyze::mountFlipStatus(MeridianFlipState::MeridianFlipMountState state)
3964 if (state == lastMountFlipStateReceived)
3966 lastMountFlipStateReceived = state;
3968 QString stateStr = MeridianFlipState::meridianFlipStatusString(state);
3969 saveMessage(
"MeridianFlipState", stateStr);
3971 processMountFlipState(logTime(), stateStr);
3976void Analyze::processMountFlipState(
double time,
const QString &statusString,
bool batchMode)
3978 MeridianFlipState::MeridianFlipMountState state = convertMountFlipState(statusString);
3979 if (state == lastMountFlipStateStarted)
3982 bool lastStateInteresting =
3983 (lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_PLANNED ||
3984 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_WAITING ||
3985 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_ACCEPTED ||
3986 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_RUNNING);
3987 if (mountFlipStateStartedTime >= 0 && lastStateInteresting)
3989 if (state == MeridianFlipState::MOUNT_FLIP_COMPLETED || state == MeridianFlipState::MOUNT_FLIP_ERROR)
3992 addSession(mountFlipStateStartedTime, time, MERIDIAN_MOUNT_FLIP_Y, mountFlipStateBrush(state));
3993 mountFlipSessions.add(MountFlipSession(mountFlipStateStartedTime, time,
nullptr, state));
3997 addSession(mountFlipStateStartedTime, time, MERIDIAN_MOUNT_FLIP_Y, mountFlipStateBrush(lastMountFlipStateStarted));
3998 mountFlipSessions.add(MountFlipSession(mountFlipStateStartedTime, time,
nullptr, lastMountFlipStateStarted));
4001 bool stateInteresting =
4002 (state == MeridianFlipState::MOUNT_FLIP_PLANNED ||
4003 state == MeridianFlipState::MOUNT_FLIP_WAITING ||
4004 state == MeridianFlipState::MOUNT_FLIP_ACCEPTED ||
4005 state == MeridianFlipState::MOUNT_FLIP_RUNNING);
4006 if (stateInteresting)
4008 addTemporarySession(&temporaryMountFlipSession, time, 1, MERIDIAN_MOUNT_FLIP_Y, temporaryBrush);
4009 temporaryMountFlipSession.state = state;
4012 removeTemporarySession(&temporaryMountFlipSession);
4014 mountFlipStateStartedTime = time;
4015 lastMountFlipStateStarted = state;
4021void Analyze::resetMountFlipState()
4023 lastMountFlipStateReceived = MeridianFlipState::MOUNT_FLIP_NONE;
4024 lastMountFlipStateStarted = MeridianFlipState::MOUNT_FLIP_NONE;
4025 mountFlipStateStartedTime = -1;
4028QBrush Analyze::schedulerJobBrush(
const QString &jobName,
bool temporary)
4032 {110, 120, 150}, {150, 180, 180}, {180, 165, 130}, {180, 200, 140}, {250, 180, 130},
4033 {190, 170, 160}, {140, 110, 160}, {250, 240, 190}, {250, 200, 220}, {150, 125, 175}
4037 auto it = schedulerJobColors.constFind(jobName);
4038 if (it == schedulerJobColors.constEnd())
4040 const int numSoFar = schedulerJobColors.size();
4041 auto color = colors[numSoFar % colors.
size()];
4042 schedulerJobColors[jobName] = color;
4043 return QBrush(color, pattern);
4047 return QBrush(*it, pattern);
4051void Analyze::schedulerJobStarted(
const QString &jobName)
4053 saveMessage(
"SchedulerJobStart", jobName);
4055 processSchedulerJobStarted(logTime(), jobName);
4059void Analyze::schedulerJobEnded(
const QString &jobName,
const QString &reason)
4061 saveMessage(
"SchedulerJobEnd",
QString(
"%1,%2").arg(jobName, reason));
4063 processSchedulerJobEnded(logTime(), jobName, reason);
4069void Analyze::processSchedulerJobStarted(
double time,
const QString &jobName)
4071 checkForMissingSchedulerJobEnd(time - 1);
4072 schedulerJobStartedTime = time;
4073 schedulerJobStartedJobName = jobName;
4076 addTemporarySession(&temporarySchedulerJobSession, time, 1, SCHEDULER_Y, schedulerJobBrush(jobName,
true));
4077 temporarySchedulerJobSession.jobName = jobName;
4081void Analyze::processSchedulerJobEnded(
double time,
const QString &jobName,
const QString &reason,
bool batchMode)
4083 removeTemporarySession(&temporarySchedulerJobSession);
4085 if (schedulerJobStartedTime < 0)
4091 addSession(schedulerJobStartedTime, time, SCHEDULER_Y, schedulerJobBrush(jobName,
false));
4092 auto session = SchedulerJobSession(schedulerJobStartedTime, time,
nullptr, jobName, reason);
4093 schedulerJobSessions.add(session);
4095 resetSchedulerJob();
4101void Analyze::checkForMissingSchedulerJobEnd(
double time)
4103 if (schedulerJobStartedTime < 0)
4105 removeTemporarySession(&temporarySchedulerJobSession);
4106 addSession(schedulerJobStartedTime, time, SCHEDULER_Y, schedulerJobBrush(schedulerJobStartedJobName,
false));
4107 auto session = SchedulerJobSession(schedulerJobStartedTime, time,
nullptr, schedulerJobStartedJobName,
"missing job end");
4108 schedulerJobSessions.add(session);
4110 resetSchedulerJob();
4113void Analyze::resetSchedulerJob()
4115 schedulerJobStartedTime = -1;
4116 schedulerJobStartedJobName =
"";
4119void Analyze::appendLogText(
const QString &text)
4121 m_LogText.insert(0,
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
4122 KStarsData::Instance()->lt().
toString(
"yyyy-MM-ddThh:mm:ss"), text));
4124 qCInfo(KSTARS_EKOS_ANALYZE) << text;
4129void 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)
void invokeHelp(const QString &anchor=QString(), const QString &appname=QString())
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KIOCORE_EXPORT void add(const QString &fileClass, const QString &directory)
QString name(StandardAction id)
const QList< QKeySequence > & begin()
const QList< QKeySequence > & zoomIn()
const QList< QKeySequence > & zoomOut()
const QList< QKeySequence > & next()
const QList< QKeySequence > & find()
const QList< QKeySequence > & findNext()
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
void valueChanged(int value)
void stateChanged(int state)
void activated(int index)
QDateTime addMSecs(qint64 msecs) const const
QDateTime currentDateTime()
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
qint64 toMSecsSinceEpoch() const const
QString toString(QStringView format, QCalendar cal) const const
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QString absoluteFilePath() const const
QString fileName() const const
void setPointSizeF(qreal pointSize)
void setText(const QString &)
void append(QList< T > &&value)
bool contains(const AT &value) const const
QList< T > mid(qsizetype pos, qsizetype length) const const
void push_back(parameter_type value)
void push_front(parameter_type value)
qsizetype size() const const
QString toString(QDate date, FormatType format) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QString writableLocation(StandardLocation type)
QString & append(QChar ch)
QString arg(Args &&... args) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString right(qsizetype n) const const
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
double toDouble(bool *ok) const const
int toInt(bool *ok, int base) const const
QTextStream & dec(QTextStream &stream)
QTextStream & left(QTextStream &stream)
void setSpan(int row, int column, int rowSpanCount, int columnSpanCount)
void setText(const QString &text)
void setTextAlignment(Qt::Alignment alignment)
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
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.