9#include <KNotifications/KNotification>
15#include "auxiliary/kspaths.h"
17#include "ekos/manager.h"
18#include "ekos/focus/curvefit.h"
19#include "fitsviewer/fitsdata.h"
20#include "fitsviewer/fitsviewer.h"
21#include "ksmessagebox.h"
23#include "kstarsdata.h"
25#include "qcustomplot.h"
27#include <ekos_analyze_debug.h>
36 void setOffset(
double offset)
48 double timeOffset = 0;
55QString timeFormat =
"yyyy-MM-dd hh:mm:ss.zzz";
117 return info.exists() && info.isFile();
126 case ISD::Mount::MOUNT_IDLE:
128 case ISD::Mount::MOUNT_PARKED:
129 return i18n(
"Parked");
130 case ISD::Mount::MOUNT_PARKING:
131 return i18n(
"Parking");
132 case ISD::Mount::MOUNT_SLEWING:
133 return i18n(
"Slewing");
134 case ISD::Mount::MOUNT_MOVING:
135 return i18n(
"Moving");
136 case ISD::Mount::MOUNT_TRACKING:
137 return i18n(
"Tracking");
138 case ISD::Mount::MOUNT_ERROR:
139 return i18n(
"Error");
141 return i18n(
"Error");
146 if (str ==
i18n(
"Idle"))
147 return ISD::Mount::MOUNT_IDLE;
148 else if (str ==
i18n(
"Parked"))
149 return ISD::Mount::MOUNT_PARKED;
150 else if (str ==
i18n(
"Parking"))
151 return ISD::Mount::MOUNT_PARKING;
152 else if (str ==
i18n(
"Slewing"))
153 return ISD::Mount::MOUNT_SLEWING;
154 else if (str ==
i18n(
"Moving"))
155 return ISD::Mount::MOUNT_MOVING;
156 else if (str ==
i18n(
"Tracking"))
157 return ISD::Mount::MOUNT_TRACKING;
159 return ISD::Mount::MOUNT_ERROR;
186 const QString hPattern(
"^(ha|h|h-a|h_a|h-alpha|hydrogen|hydrogen_alpha|hydrogen-alpha|h_alpha|halpha)$");
192 const QString oPattern(
"^(oiii|oxygen|oxygen_3|oxygen-3|oxygen_iii|oxygen-iii|o_iii|o-iii|o_3|o-3|o3)$");
199 sPattern(
"^(sii|sulphur|sulphur_2|sulphur-2|sulphur_ii|sulphur-ii|sulfur|sulfur_2|sulfur-2|sulfur_ii|sulfur-ii|s_ii|s-ii|s_2|s-2|s2)$");
206 const QString lPattern(
"^(lpr|L|UV-IR cut|UV-IR|white|monochrome|broadband|clear|focus|luminance|lum|lps|cls)$");
226 if (info.exists() && info.isFile())
236 int size = filename.
size();
268 intervals.append(value);
277 for (
const auto &interval : intervals)
279 if (t >= interval.start && t <= interval.end)
280 result.push_back(interval);
289 for (
auto &interval : intervals)
291 if (interval.start > t && interval.start <
bestStart)
300 T *findPrevious(
double t)
304 for (
auto &interval : intervals)
306 if (interval.start < t && interval.start >
bestStart)
347 double newSample(
double x,
double y)
350 filteredRMS = alpha *
valueSquared + (1.0 - alpha) * filteredRMS;
351 return sqrt(filteredRMS);
355 double filteredRMS { 0 };
376 Qt::KeyboardModifier::ControlModifier);
379 Qt::KeyboardModifier::ShiftModifier);
397 if (m_ClickTimerInfo.checkBox && !m_ClickTimerInfo.checkBox->
isChecked())
400 m_ClickTimerInfo.checkBox->setChecked(true);
401 statsPlot->graph(m_ClickTimerInfo.graphIndex)->setVisible(true);
402 statsPlot->graph(m_ClickTimerInfo.graphIndex)->addToLegend();
404 userSetLeftAxis(m_ClickTimerInfo.axis);
411Analyze::Analyze() : m_YAxisTool(
this)
415 captureRms.reset(
new RmsFilter);
416 guiderRms.reset(
new RmsFilter);
420 initInputSelection();
424 connect(&m_YAxisTool, &YAxisTool::axisChanged,
this, &Analyze::userChangedYAxis);
425 connect(&m_YAxisTool, &YAxisTool::leftAxisChanged,
this, &Analyze::userSetLeftAxis);
426 connect(&m_YAxisTool, &YAxisTool::axisColorChanged,
this, &Analyze::userSetAxisColor);
427 qApp->installEventFilter(
this);
432 runtimeDisplay =
true;
454 initStatsCheckboxes();
471 setupKeyboardShortcuts(
this);
477void Analyze::setVisibility()
487void Analyze::timelineMouseWheel(
QWheelEvent *event)
489 if (
event->angleDelta().y() > 0)
491 else if (
event->angleDelta().y() < 0)
497void Analyze::keepCurrent(
int state)
509void Analyze::initInputSelection()
516 inputCombo->addItem(
i18n(
"Set alternative image-file base directory"));
527 maxXValue = readDataFromFile(logFilename);
528 runtimeDisplay =
true;
541 QString(
"Analyze %1 (*.analyze);;%2").arg(
i18n(
"Log")).arg(
i18n(
"All Files (*)")));
550 runtimeDisplay =
false;
552 maxXValue = readDataFromFile(
inputURL.toLocalFile());
553 checkForMissingSchedulerJobEnd(maxXValue);
555 plotWidth = maxXValue + 5;
561 this,
i18n(
"Set an alternate base directory for your captured images"),
567 alternateFolder =
dir;
578void Analyze::setupKeyboardShortcuts(
QWidget *plot)
620void Analyze::setSelectedSession(
const Session &s)
622 m_selectedSession = s;
625void Analyze::clearSelectedSession()
632void Analyze::unhighlightTimelineItem()
634 clearSelectedSession();
635 if (selectionHighlight !=
nullptr)
638 selectionHighlight =
nullptr;
647void Analyze::highlightTimelineItem(
const Session &session)
650 unhighlightTimelineItem();
652 setSelectedSession(session);
657 selectionHighlight = rect;
664QCPItemRect * Analyze::addSession(
double start,
double end,
double y,
693 int numStars,
double skyBackground,
double time)
698 lastGuideStatsTime >= 0)
701 lastGuideStatsTime + .0001);
703 guiderRms->resetFilter();
715 if (captureStartedTime >= 0)
720 if ((lastCaptureRmsTime >= 0) &&
726 captureRms->resetFilter();
730 lastCaptureRmsTime = time;
733 lastGuideStatsTime = time;
738 double numStars,
double skyBackground,
739 double drift,
double rms,
double time)
750 snrMax = std::max(snr, snrMax);
751 if (!qIsNaN(skyBackground))
752 skyBgMax = std::max(skyBackground, skyBgMax);
754 numStarsMax = std::max(
numStars,
static_cast<double>(numStarsMax));
761void Analyze::addTemperature(
double temperature,
double time)
765 if (temperature > -200)
769void Analyze::addFocusPosition(
double focusPosition,
double time)
774void Analyze::addTargetDistance(
double targetDistance,
double time)
777 if (previousCaptureStartedTime >= 0 && previousCaptureCompletedTime >= 0 &&
778 previousCaptureStartedTime < previousCaptureCompletedTime &&
779 previousCaptureCompletedTime <= time)
790 double time,
double startTime)
813 medianMax = std::max(median, medianMax);
819void Analyze::addMountCoords(
double ra,
double dec,
double az,
820 double alt,
int pierSide,
double ha,
double time)
831double Analyze::readDataFromFile(
const QString &filename)
841 double time = processInputLine(line);
851double Analyze::processInputLine(
const QString &line)
859 if (list[0].at(0).toLatin1() ==
'#')
865 if ((list[0] ==
"AnalyzeStartTime") &&
list.size() == 3)
868 startTimeInitialized =
true;
869 analyzeTimeZone =
list[2];
881 if ((list[0] ==
"CaptureStarting") && (
list.size() == 4))
889 else if ((list[0] ==
"CaptureComplete") && (
list.size() >= 6) && (
list.size() <= 9))
910 else if ((list[0] ==
"CaptureAborted") && (
list.size() == 3))
917 else if ((list[0] ==
"AutofocusStarting") && (
list.size() >= 4))
923 AutofocusReason reason;
925 if (
list.size() == 4)
927 reason = AutofocusReason::FOCUS_NONE;
932 reason =
static_cast<AutofocusReason
>(
QString(list[4]).
toInt(&ok));
935 reasonInfo =
list[5];
937 processAutofocusStarting(time, temperature, filter, reason, reasonInfo);
939 else if ((list[0] ==
"AutofocusComplete") && (
list.size() >= 8))
949 AutofocusReason reason =
static_cast<AutofocusReason
>(
reasonInt);
958 processAutofocusCompleteV2(time, temperature, filter, reason, reasonInfo, samples, useWeights, curve, title,
true);
960 else if ((list[0] ==
"AutofocusComplete") && (
list.size() >= 4))
967 processAutofocusComplete(time, filter, samples, curve, title,
true);
969 else if ((list[0] ==
"AutofocusAborted") && (
list.size() >= 9))
978 AutofocusReason reason =
static_cast<AutofocusReason
>(
reasonInt);
985 AutofocusFailReason failCode;
990 failCode =
static_cast<AutofocusFailReason
>(
failCodeInt);
995 failCodeInfo =
QString(list[9]);
996 processAutofocusAbortedV2(time, temperature, filter, reason, reasonInfo, samples, useWeights, failCode, failCodeInfo,
true);
998 else if ((list[0] ==
"AutofocusAborted") && (
list.size() >= 4))
1002 processAutofocusAborted(time, filter, samples,
true);
1004 else if ((list[0] ==
"AdaptiveFocusComplete") && (
list.size() == 12))
1017 processAdaptiveFocusComplete(time, filter, temperature, tempTicks, altitude, altTicks, prevPosError,
1018 thisPosError, totalTicks, position,
focuserMoved,
true);
1020 else if ((list[0] ==
"AdaptiveFocusComplete") && (
list.size() >= 9))
1032 processAdaptiveFocusComplete(time, filter, temperature, tempTicks,
1033 altitude, altTicks, 0, 0, totalTicks, position,
focuserMoved,
true);
1035 else if ((list[0] ==
"GuideState") &&
list.size() == 3)
1037 processGuideState(time, list[2],
true);
1039 else if ((list[0] ==
"GuideStats") &&
list.size() == 9)
1064 else if ((list[0] ==
"Temperature") &&
list.size() == 3)
1069 processTemperature(time, temperature,
true);
1071 else if ((list[0] ==
"TargetDistance") &&
list.size() == 3)
1076 processTargetDistance(time, targetDistance,
true);
1078 else if ((list[0] ==
"MountState") &&
list.size() == 3)
1080 processMountState(time, list[2],
true);
1082 else if ((list[0] ==
"MountCoords") && (
list.size() == 7 ||
list.size() == 8))
1102 processMountCoords(time, ra, dec, az, alt, side, ha,
true);
1104 else if ((list[0] ==
"AlignState") &&
list.size() == 3)
1106 processAlignState(time, list[2],
true);
1108 else if ((list[0] ==
"MeridianFlipState") &&
list.size() == 3)
1110 processMountFlipState(time, list[2],
true);
1112 else if ((list[0] ==
"SchedulerJobStart") &&
list.size() == 3)
1115 processSchedulerJobStarted(time, jobName);
1117 else if ((list[0] ==
"SchedulerJobEnd") &&
list.size() == 4)
1121 processSchedulerJobEnded(time, jobName, reason,
true);
1140 if (
col1 ==
"Filename")
1144 ft.setPointSizeF(8.0);
1163 if (
col1 ==
"Filename")
1167 ft.setPointSizeF(8.0);
1172 if (
col3.size() > 0)
1190void Analyze::Session::setupTable(
const QString &name,
const QString &status,
1195 details->setRowCount(0);
1197 details->setColumnCount(3);
1198 details->verticalHeader()->setDefaultSectionSize(20);
1199 details->horizontalHeader()->setStretchLastSection(
true);
1200 details->setColumnWidth(0, 100);
1201 details->setColumnWidth(1, 100);
1202 details->setShowGrid(
false);
1203 details->setWordWrap(
true);
1204 details->horizontalHeader()->hide();
1205 details->verticalHeader()->hide();
1221void Analyze::Session::addRow(
const QString &key,
const QString &value)
1226bool Analyze::Session::isTemporary()
const
1228 return rect !=
nullptr;
1234Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
bool ok,
double temperature_,
1243 const int size =
list.size();
1248 for (
int i = 0; i < size; )
1265 positions.push_back(position);
1266 hfrs.push_back(hfr);
1267 weights.push_back(weight);
1275Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
bool ok,
double temperature_,
1277 :
Session(start_, end_, FOCUS_Y, rect), success(
ok),
1281 reason = AutofocusReason::FOCUS_NONE;
1284 failCode = AutofocusFailReason::FOCUS_FAIL_NONE;
1288 const int size =
list.size();
1293 for (
int i = 0; i < size; )
1308 positions.push_back(position);
1309 hfrs.push_back(hfr);
1310 weights.push_back(1.0);
1311 outliers.push_back(
false);
1315Analyze::FocusSession::FocusSession(
double start_,
double end_,
QCPItemRect *rect,
1322 standardSession =
false;
1325double Analyze::FocusSession::focusPosition()
1327 if (!standardSession)
1328 return adaptedPosition;
1330 if (positions.size() > 0)
1331 return positions.last();
1347void Analyze::captureSessionClicked(CaptureSession &c,
bool doubleClick)
1349 highlightTimelineItem(c);
1351 if (c.isTemporary())
1352 c.setupTable(
"Capture",
"in progress", clockTime(c.start), clockTime(c.start),
detailsTable);
1354 c.setupTable(
"Capture",
"ABORTED", clockTime(c.start), clockTime(c.end),
detailsTable);
1356 c.setupTable(
"Capture",
"successful", clockTime(c.start), clockTime(c.end),
detailsTable);
1358 c.addRow(
"Filter", c.filter);
1367 if (!c.isTemporary())
1368 c.addRow(
"Filename", c.filename);
1378 appendLogText(
i18n(
"Could not find image file: %1", c.filename));
1380 displayFITS(filename);
1381 else appendLogText(
i18n(
"Cannot display temporary image file: %1", c.filename));
1389 if (val == 0)
return "";
1390 else if (val > 0)
return "+";
1404void Analyze::focusSessionClicked(FocusSession &c,
bool doubleClick)
1407 highlightTimelineItem(c);
1409 if (!c.standardSession)
1412 c.setupTable(
"Focus",
"Adaptive", clockTime(c.end), clockTime(c.end),
detailsTable);
1413 c.addRow(
"Filter", c.filter);
1419 QString(
"%1 / %2").arg(c.prevPosError).
arg(c.thisPosError));
1426 c.setupTable(
"Focus",
"successful", clockTime(c.start), clockTime(c.end),
detailsTable);
1427 else if (c.isTemporary())
1428 c.setupTable(
"Focus",
"in progress", clockTime(c.start), clockTime(c.start),
detailsTable);
1430 c.setupTable(
"Focus",
"FAILED", clockTime(c.start), clockTime(c.end),
detailsTable);
1432 if (!c.isTemporary())
1436 if (c.hfrs.size() > 0)
1438 if (c.positions.size() > 0)
1444 if (!c.success && !c.isTemporary())
1448 c.addRow(
"Filter", c.filter);
1449 c.addRow(
"Temperature", (c.temperature == INVALID_VALUE) ?
"N/A" :
QString::
number(c.temperature,
'f', 1));
1451 if (c.isTemporary())
1452 resetGraphicsPlot();
1454 displayFocusGraphics(c.positions, c.hfrs, c.useWeights, c.weights, c.outliers, c.curve, c.title, c.success);
1461void Analyze::guideSessionClicked(GuideSession &c,
bool doubleClick)
1464 highlightTimelineItem(c);
1467 if (c.simpleState == G_IDLE)
1469 else if (c.simpleState == G_GUIDING)
1471 else if (c.simpleState == G_CALIBRATING)
1473 else if (c.simpleState == G_SUSPENDED)
1475 else if (c.simpleState == G_DITHERING)
1478 c.setupTable(
"Guide",
st, clockTime(c.start), clockTime(c.end),
detailsTable);
1479 resetGraphicsPlot();
1480 if (c.simpleState == G_GUIDING)
1495void Analyze::displayGuideGraphics(
double start,
double end,
double *
raRMS,
1498 resetGraphicsPlot();
1506 ra->mainKey() < end &&
dec->mainKey() < end &&
1509 ra->mainKey() < end &&
dec->mainKey() < end)
1511 const double raVal = ra->mainValue();
1523 if (numSamples !=
nullptr)
1527 if (
raRMS !=
nullptr)
1533 if (numSamples !=
nullptr)
1537 c1->bottomRight->setCoords(1.0, -1.0);
1538 c1->topLeft->setCoords(-1.0, 1.0);
1540 c2->bottomRight->setCoords(2.0, -2.0);
1541 c2->topLeft->setCoords(-2.0, 2.0);
1557void Analyze::mountSessionClicked(MountSession &c,
bool doubleClick)
1560 highlightTimelineItem(c);
1568void Analyze::alignSessionClicked(AlignSession &c,
bool doubleClick)
1571 highlightTimelineItem(c);
1572 c.setupTable(
"Align", getAlignStatusString(c.state), clockTime(c.start),
1578void Analyze::mountFlipSessionClicked(MountFlipSession &c,
bool doubleClick)
1581 highlightTimelineItem(c);
1582 c.setupTable(
"Meridian Flip", MeridianFlipState::meridianFlipStatusString(c.state),
1583 clockTime(c.start), clockTime(c.isTemporary() ? c.start : c.
end),
detailsTable);
1588void Analyze::schedulerSessionClicked(SchedulerJobSession &c,
bool doubleClick)
1591 highlightTimelineItem(c);
1592 c.setupTable(
"Scheduler Job", c.jobName,
1593 clockTime(c.start), clockTime(c.isTemporary() ? c.start : c.
end),
detailsTable);
1594 c.addRow(
"End reason", c.reason);
1602 unhighlightTimelineItem();
1605 if (
yval >= CAPTURE_Y - 0.5 &&
yval <= CAPTURE_Y + 0.5)
1610 else if ((temporaryCaptureSession.rect !=
nullptr) &&
1611 (
xval > temporaryCaptureSession.start))
1612 captureSessionClicked(temporaryCaptureSession,
doubleClick);
1614 else if (
yval >= FOCUS_Y - 0.5 &&
yval <= FOCUS_Y + 0.5)
1619 else if ((temporaryFocusSession.rect !=
nullptr) &&
1620 (
xval > temporaryFocusSession.start))
1621 focusSessionClicked(temporaryFocusSession,
doubleClick);
1623 else if (
yval >= GUIDE_Y - 0.5 &&
yval <= GUIDE_Y + 0.5)
1628 else if ((temporaryGuideSession.rect !=
nullptr) &&
1629 (
xval > temporaryGuideSession.start))
1630 guideSessionClicked(temporaryGuideSession,
doubleClick);
1632 else if (
yval >= MOUNT_Y - 0.5 &&
yval <= MOUNT_Y + 0.5)
1637 else if ((temporaryMountSession.rect !=
nullptr) &&
1638 (
xval > temporaryMountSession.start))
1639 mountSessionClicked(temporaryMountSession,
doubleClick);
1641 else if (
yval >= ALIGN_Y - 0.5 &&
yval <= ALIGN_Y + 0.5)
1646 else if ((temporaryAlignSession.rect !=
nullptr) &&
1647 (
xval > temporaryAlignSession.start))
1648 alignSessionClicked(temporaryAlignSession,
doubleClick);
1650 else if (
yval >= MERIDIAN_MOUNT_FLIP_Y - 0.5 &&
yval <= MERIDIAN_MOUNT_FLIP_Y + 0.5)
1655 else if ((temporaryMountFlipSession.rect !=
nullptr) &&
1656 (
xval > temporaryMountFlipSession.start))
1657 mountFlipSessionClicked(temporaryMountFlipSession,
doubleClick);
1659 else if (
yval >= SCHEDULER_Y - 0.5 &&
yval <= SCHEDULER_Y + 0.5)
1664 else if ((temporarySchedulerJobSession.rect !=
nullptr) &&
1665 (
xval > temporarySchedulerJobSession.start))
1666 schedulerSessionClicked(temporarySchedulerJobSession,
doubleClick);
1668 setStatsCursor(
xval);
1672void Analyze::nextTimelineItem()
1674 changeTimelineItem(
true);
1677void Analyze::previousTimelineItem()
1679 changeTimelineItem(
false);
1682void Analyze::changeTimelineItem(
bool next)
1684 if (m_selectedSession.start == 0 && m_selectedSession.end == 0)
return;
1685 switch(m_selectedSession.offset)
1694 while (nextSession && nextSession->aborted)
1701 captureSessionClicked(*nextSession,
true);
1702 setStatsCursor((nextSession->end + nextSession->start) / 2);
1712 focusSessionClicked(*nextSession,
true);
1713 setStatsCursor((nextSession->end + nextSession->start) / 2);
1723 alignSessionClicked(*nextSession,
true);
1724 setStatsCursor((nextSession->end + nextSession->start) / 2);
1734 guideSessionClicked(*nextSession,
true);
1735 setStatsCursor((nextSession->end + nextSession->start) / 2);
1745 mountSessionClicked(*nextSession,
true);
1746 setStatsCursor((nextSession->end + nextSession->start) / 2);
1756 schedulerSessionClicked(*nextSession,
true);
1757 setStatsCursor((nextSession->end + nextSession->start) / 2);
1763 if (!isVisible(m_selectedSession) && !isVisible(m_selectedSession))
1764 adjustView((m_selectedSession.start + m_selectedSession.end) / 2.0);
1768bool Analyze::isVisible(
const Session &s)
const
1772 return !((s.start < plotStart && s.end < plotStart) ||
1773 (s.start > (plotStart + plotWidth) && s.end > (plotStart + plotWidth)));
1776void Analyze::adjustView(
double time)
1780 plotStart = time - plotWidth / 2;
1784void Analyze::setStatsCursor(
double time)
1786 removeStatsCursor();
1791 const double top =
statsPlot->yAxis->range().upper;
1792 const double bottom =
statsPlot->yAxis->range().lower;
1804 timelineCursor =
line2;
1808 .arg(clockTime(time).
toString(
"hh:mm:ss")));
1809 statsCursorTime = time;
1813void Analyze::removeStatsCursor()
1815 if (statsCursor !=
nullptr)
1817 statsCursor =
nullptr;
1819 if (timelineCursor !=
nullptr)
1821 timelineCursor =
nullptr;
1825 statsCursorTime = -1;
1833 setStatsCursor(
xval);
1837void Analyze::timelineMousePress(
QMouseEvent *event)
1839 processTimelineClick(event,
false);
1842void Analyze::timelineMouseDoubleClick(
QMouseEvent *event)
1844 processTimelineClick(event,
true);
1858 processStatsClick(event,
false);
1861void Analyze::statsMouseDoubleClick(
QMouseEvent *event)
1863 processStatsClick(event,
true);
1875 auto range = yAxis->range();
1879 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == yAxis)
1880 m_YAxisTool.replot(
true);
1883 processStatsClick(event,
false);
1887void Analyze::scroll(
int value)
1890 plotStart = std::max(0.0, maxXValue *
pct - plotWidth / 2.0);
1896void Analyze::scrollRight()
1898 plotStart = std::min(maxXValue - plotWidth / 5, plotStart + plotWidth / 5);
1903void Analyze::scrollLeft()
1905 plotStart = std::max(0.0, plotStart - plotWidth / 5);
1912 adjustTemporarySessions();
1916 plotWidth = std::max(10.0, maxXValue);
1920 plotStart = std::max(0.0, maxXValue - plotWidth);
1928 .arg(clockTime(maxXValue).
toString(
"hh:mm:ss")));
1939 timelinePlot->xAxis->setRange(plotStart, plotStart + plotWidth);
1942 statsPlot->xAxis->setRange(plotStart, plotStart + plotWidth);
1947 for (
auto &
pairs : yAxisMap)
1950 if (
statsPlot->graph(info.graphIndex)->visible() && info.rescale)
1954 axis->
scaleRange(1.1, axis->range().center());
1959 dateTicker->setOffset(displayStartTime.toMSecsSinceEpoch() / 1000.0);
1965 if (activeYAxis !=
nullptr)
1969 const int paddingSize = activeYAxis->padding();
1973 if (
newPad != paddingSize)
1975 activeYAxis->setPadding(
newPad);
1979 updateStatsValues();
1984 auto axis = activeYAxis;
1986 auto range = axis->range();
1987 const double halfDiff = (range.upper - range.lower) / 2.0;
1988 const double middle = (range.upper + range.lower) / 2.0;
1990 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == axis)
1991 m_YAxisTool.replot(
true);
1993void Analyze::statsYZoomIn()
1998void Analyze::statsYZoomOut()
2008template<
typename Func>
2011 auto begin = graph->
data()->findBegin(time);
2013 if ((begin != graph->
data()->constEnd()) &&
2024 const double val = graph->
data()->at(index)->mainValue();
2025 const double t = graph->
data()->at(index)->mainKey();
2046void Analyze::updateStatsValues()
2048 const double time = statsCursorTime < 0 ? maxXValue : statsCursorTime;
2086 if (ha.
Hours() > 12.0)
2106 return d == 0.0 ?
"W->E" : d == 1.0 ?
"E->W" :
"?";
2111void Analyze::initStatsCheckboxes()
2113 hfrCB->setChecked(Options::analyzeHFR());
2115 medianCB->setChecked(Options::analyzeMedian());
2117 numStarsCB->setChecked(Options::analyzeNumStars());
2118 skyBgCB->setChecked(Options::analyzeSkyBg());
2119 snrCB->setChecked(Options::analyzeSNR());
2123 raCB->setChecked(Options::analyzeRA());
2124 decCB->setChecked(Options::analyzeDEC());
2125 raPulseCB->setChecked(Options::analyzeRAp());
2126 decPulseCB->setChecked(Options::analyzeDECp());
2127 driftCB->setChecked(Options::analyzeDrift());
2128 rmsCB->setChecked(Options::analyzeRMS());
2129 rmsCCB->setChecked(Options::analyzeRMSC());
2130 mountRaCB->setChecked(Options::analyzeMountRA());
2131 mountDecCB->setChecked(Options::analyzeMountDEC());
2132 mountHaCB->setChecked(Options::analyzeMountHA());
2133 azCB->setChecked(Options::analyzeAz());
2134 altCB->setChecked(Options::analyzeAlt());
2135 pierSideCB->setChecked(Options::analyzePierSide());
2138void Analyze::zoomIn()
2140 if (plotWidth > 0.5)
2144 plotStart = std::max(0.0, maxXValue - plotWidth / 4.0);
2145 else if (statsCursorTime >= 0)
2147 plotStart = std::max(0.0, statsCursorTime - plotWidth / 4.0);
2150 plotStart += plotWidth / 4.0;
2151 plotWidth = plotWidth / 2.0;
2157void Analyze::zoomOut()
2159 if (plotWidth < maxXValue)
2161 plotStart = std::max(0.0, plotStart - plotWidth / 2.0);
2162 plotWidth = plotWidth * 2;
2195void Analyze::initTimelinePlot()
2216void Analyze::toggleGraph(
int graph_id,
bool show)
2239 if (key ==
nullptr)
return;
2242 yAxisMap.insert(std::make_pair(key,
axisInfo));
2247template <
typename Func>
2252 const int num = initGraph(plot, yAxis, lineStyle, color, shortName);
2255 const bool autoAxis = YAxisInfo::isRescale(yAxis->range());
2256 updateYAxisMap(
out,
YAxisInfo(yAxis, yAxis->range(),
autoAxis, num, plot, cb, name, shortName, color));
2271 this->toggleGraph(num, show);
2283 Options::setAnalyzeStatsYAxis(serializeYAxes());
2287void Analyze::userSetLeftAxis(
QCPAxis *axis)
2290 Options::setAnalyzeStatsYAxis(serializeYAxes());
2297 Options::setAnalyzeStatsYAxis(serializeYAxes());
2305 if (m_YAxisTool.isVisible() && m_YAxisTool.getAxis() == activeYAxis)
2306 m_YAxisTool.replot(
true);
2309void Analyze::setLeftAxis(
QCPAxis *axis)
2311 if (axis !=
nullptr && axis != activeYAxis)
2313 for (
const auto &pair : yAxisMap)
2317 pair.second.axis->setVisible(
false);
2321 statsPlot->axisRect()->setRangeZoomAxes(0, axis);
2329 if (info.checkBox && !info.checkBox->isChecked())
2332 info.checkBox->setChecked(
true);
2333 statsPlot->graph(info.graphIndex)->setVisible(
true);
2334 statsPlot->graph(info.graphIndex)->addToLegend();
2337 m_YAxisTool.reset(key, info, info.axis == activeYAxis);
2341QCPAxis *Analyze::newStatsYAxis(
const QString &label,
double lower,
double upper)
2351bool Analyze::restoreYAxes(
const QString &encoding)
2354 constexpr int itemSize = 5;
2356 if (items.size() <=
headerSize)
return false;
2357 if ((items.size() -
headerSize) % itemSize != 0)
return false;
2358 if (items[0] !=
"AnalyzeStatsYAxis1.0")
return false;
2362 if (!items[1].startsWith(
leftID))
return false;
2364 if (
left.size() <= 0)
return false;
2365 for (
const auto &pair : yAxisMap)
2367 if (pair.second.axis->label() == left)
2369 setLeftAxis(pair.second.axis);
2375 for (
int i =
headerSize; i < items.size(); i += itemSize)
2377 const QString shortName = items[i].toString();
2378 const double lower = items[i + 1].toDouble();
2379 const double upper = items[i + 2].toDouble();
2380 const bool rescale = items[i + 3] ==
"T";
2381 const QColor color(items[i + 4]);
2382 for (
auto &pair : yAxisMap)
2384 auto &info = pair.second;
2385 if (info.axis->label() == shortName)
2389 info.rescale = rescale;
2391 info.axis->setRange(
2393 YAxisInfo::UPPER_RESCALE));
2395 info.axis->setRange(
QCPRange(lower, upper));
2404QString Analyze::serializeYAxes()
2406 QString encoding =
QString(
"AnalyzeStatsYAxis1.0,left=%1").
arg(activeYAxis->label());
2408 for (
const auto &pair : yAxisMap)
2411 const bool rescale = info.rescale;
2415 (rescale != YAxisInfo::isRescale(info.initialRange)) ||
2416 (!rescale && info.axis->range() != info.initialRange);
2421 if (
savedAxes.contains(info.axis->label()))
continue;
2423 double lower = rescale ? YAxisInfo::LOWER_RESCALE : info.axis->range().lower;
2424 double upper = rescale ? YAxisInfo::UPPER_RESCALE : info.axis->range().upper;
2426 .
arg(info.axis->label()).
arg(lower).
arg(upper)
2427 .
arg(info.rescale ?
"T" :
"F").arg(info.color.
name()));
2433void Analyze::initStatsPlot()
2459 Options::setAnalyzeHFR,
hfrOut);
2463 if (show && !Options::autoHFR())
2464 KSNotification::info(
2465 i18n(
"The \"Auto Compute HFR\" option in the KStars "
2466 "FITS options menu is not set. You won't get HFR values "
2467 "without it. Once you set it, newly captured images "
2468 "will have their HFRs computed."));
2471 shortName =
"#SubStars";
2474 "#Stars in Capture", shortName,
2479 if (show && !Options::autoHFR())
2480 KSNotification::info(
2481 i18n(
"The \"Auto Compute HFR\" option in the KStars "
2482 "FITS options menu is not set. You won't get # stars in capture image values "
2483 "without it. Once you set it, newly captured images "
2484 "will have their stars detected."));
2487 shortName =
"median";
2496 shortName =
"#Stars";
2500 shortName =
"SkyBG";
2509 shortName =
"focus";
2513 shortName =
"tDist";
2519 QCPAxis *snrAxis = newStatsYAxis(shortName, -100, 100);
2521 Options::setAnalyzeSNR,
snrOut);
2523 auto raColor = KStarsData::Instance()->colorScheme()->colorNamed(
"RAGuideError");
2525 Options::setAnalyzeRA,
raOut);
2527 auto decColor = KStarsData::Instance()->colorScheme()->colorNamed(
"DEGuideError");
2529 Options::setAnalyzeDEC,
decOut);
2531 auto raPulseColor = KStarsData::Instance()->colorScheme()->colorNamed(
"RAGuideError");
2539 auto decPulseColor = KStarsData::Instance()->colorScheme()->colorNamed(
"DEGuideError");
2545 shortName =
"Drift";
2550 Options::setAnalyzeRMS,
rmsOut);
2553 "Guider RMS Drift (during capture)", shortName,
rmsCCB,
2554 Options::setAnalyzeRMSC,
rmsCOut);
2555 shortName =
"MOUNT_RA";
2560 shortName =
"MOUNT_DEC";
2563 shortName =
"MOUNT_HA";
2569 Options::setAnalyzeAz,
azOut);
2573 Options::setAnalyzeAlt,
altOut);
2574 shortName =
"PierSide";
2583 dateTicker.reset(
new OffsetDateTimeTicker);
2584 dateTicker->setDateTimeFormat(
"hh:mm:ss");
2585 statsPlot->xAxis->setTicker(dateTicker);
2590 restoreYAxes(Options::analyzeStatsYAxis());
2594void Analyze::reset()
2600 guiderRms->resetFilter();
2601 captureRms->resetFilter();
2603 unhighlightTimelineItem();
2605 for (
int i = 0; i <
statsPlot->graphCount(); ++i)
2613 resetGraphicsPlot();
2647 removeStatsCursor();
2648 removeTemporarySessions();
2650 resetCaptureState();
2651 resetAutofocusState();
2657 resetMountFlipState();
2658 resetSchedulerJob();
2663void Analyze::initGraphicsPlot()
2671 errorBars->setAntialiased(
false);
2673 errorBars->setPen(
QPen(
QColor(180, 180, 180)));
2680 finalErrorBars->setAntialiased(
false);
2682 finalErrorBars->setPen(
QPen(
QColor(180, 180, 180)));
2698 resetGraphicsPlot();
2703 for (
int i = 0; i < positions.size(); ++i)
2706 if (success && i == positions.size() - 1)
2712 double sd = (weights[i] <= 0.0) ? 0.0 :
std::
pow(weights[i], -0.5);
2718 graph->
addData(positions[i], hfrs[i]);
2721 double sd = (weights[i] <= 0.0) ? 0.0 :
std::
pow(weights[i], -0.5);
2731 for (
int i = 0; i < positions.size(); ++i)
2736 textLabel->position->setCoords(positions[i], hfrs[i]);
2753 errorBars->setVisible(useWeights);
2754 finalErrorBars->setVisible(useWeights);
2762 const double xPadding = hfrs.size() > 1 ? xRange / (hfrs.size() - 1.0) : 10;
2765 if (curve.
size() > 0)
2767 CurveFitting curveFitting(curve);
2768 const double interval = xRange / 20.0;
2775 plotTitle->setColor(
QColor(255, 255, 255));
2778 plotTitle->position->setCoords(0.5, 0);
2779 plotTitle->setFont(
QFont(font().family(), 10));
2780 plotTitle->setVisible(
true);
2781 plotTitle->setText(title);
2784 const double upper = 1.5 *
maxHfr;
2785 const double lower =
minHfr - (0.25 * (upper -
minHfr));
2791void Analyze::resetGraphicsPlot()
2796 errorBars->data().clear();
2797 finalErrorBars->data().clear();
2800void Analyze::displayFITS(
const QString &filename)
2804 if (fitsViewer.isNull())
2807 fitsViewer->loadFile(url);
2808 connect(fitsViewer.get(), &FITSViewer::terminated,
this, [
this]()
2814 fitsViewer->updateFile(url, 0);
2819void Analyze::helpMessage()
2831double Analyze::logTime(
const QDateTime &time)
2833 if (!logInitialized)
2835 return (time.
toMSecsSinceEpoch() - analyzeStartTime.toMSecsSinceEpoch()) / 1000.0;
2841double Analyze::logTime()
2855void Analyze::saveMessage(
const QString &type,
const QString &message)
2860 .arg(message.
size() > 0 ?
"," :
"", message));
2865void Analyze::startLog()
2868 startTimeInitialized =
true;
2870 displayStartTime = analyzeStartTime;
2877 logFile.setFileName(logFilename);
2881 logInitialized =
true;
2883 appendToLog(
QString(
"#KStars version %1. Analyze log version 1.0.\n\n")
2885 appendToLog(
QString(
"%1,%2,%3\n")
2886 .arg(
"AnalyzeStartTime", analyzeStartTime.toString(timeFormat), analyzeStartTime.timeZoneAbbreviation()));
2889void Analyze::appendToLog(
const QString &lines)
2891 if (!logInitialized)
2899void Analyze::updateMaxX(
double time)
2901 maxXValue = std::max(time, maxXValue);
2909void Analyze::removeTemporarySession(
Session * session)
2911 if (session->rect !=
nullptr)
2913 session->rect =
nullptr;
2919void Analyze::removeTemporarySessions()
2921 removeTemporarySession(&temporaryCaptureSession);
2922 removeTemporarySession(&temporaryMountFlipSession);
2923 removeTemporarySession(&temporaryFocusSession);
2924 removeTemporarySession(&temporaryGuideSession);
2925 removeTemporarySession(&temporaryMountSession);
2926 removeTemporarySession(&temporaryAlignSession);
2927 removeTemporarySession(&temporarySchedulerJobSession);
2931void Analyze::addTemporarySession(
Session * session,
double time,
double duration,
2934 removeTemporarySession(session);
2935 session->rect = addSession(time, time + duration,
y_offset, brush);
2936 session->start = time;
2937 session->end = time + duration;
2939 session->temporaryBrush = brush;
2940 updateMaxX(time + duration);
2946void Analyze::adjustTemporarySession(
Session * session)
2948 if (session->rect !=
nullptr && session->end < maxXValue)
2950 QBrush brush = session->temporaryBrush;
2951 double start = session->start;
2952 int offset = session->offset;
2953 addTemporarySession(session, start, maxXValue - start, offset, brush);
2958void Analyze::adjustTemporarySessions()
2960 adjustTemporarySession(&temporaryCaptureSession);
2961 adjustTemporarySession(&temporaryMountFlipSession);
2962 adjustTemporarySession(&temporaryFocusSession);
2963 adjustTemporarySession(&temporaryGuideSession);
2964 adjustTemporarySession(&temporaryMountSession);
2965 adjustTemporarySession(&temporaryAlignSession);
2966 adjustTemporarySession(&temporarySchedulerJobSession);
2973 saveMessage(
"CaptureStarting",
2982 captureStartedTime = time;
2983 captureStartedFilter =
filter;
2986 addTemporarySession(&temporaryCaptureSession, time, 1, CAPTURE_Y, temporaryBrush);
2988 temporaryCaptureSession.filter =
filter;
2992void Analyze::captureComplete(
const QVariantMap &metadata)
2994 auto filename = metadata[
"filename"].toString();
2995 auto exposure = metadata[
"exposure"].toDouble();
2996 auto filter = metadata[
"filter"].toString();
2997 auto hfr = metadata[
"hfr"].toDouble();
2998 auto starCount = metadata[
"starCount"].toInt();
2999 auto median = metadata[
"median"].toDouble();
3000 auto eccentricity = metadata[
"eccentricity"].toDouble();
3002 saveMessage(
"CaptureComplete",
3003 QString(
"%1,%2,%3,%4,%5,%6,%7")
3008 if (runtimeDisplay && captureStartedTime >= 0)
3009 processCaptureComplete(logTime(), filename, exposure, filter, hfr,
starCount, median,
eccentricity);
3012void Analyze::processCaptureComplete(
double time,
const QString &filename,
3016 removeTemporarySession(&temporaryCaptureSession);
3021 addSession(captureStartedTime, time, CAPTURE_Y,
successBrush,
nullptr);
3022 auto session = CaptureSession(captureStartedTime, time,
nullptr,
false,
3029 if (runtimeDisplay &&
keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3030 captureSessionClicked(session,
false);
3033 previousCaptureStartedTime = captureStartedTime;
3034 previousCaptureCompletedTime = time;
3035 captureStartedTime = -1;
3040 saveMessage(
"CaptureAborted",
3042 if (runtimeDisplay && captureStartedTime >= 0)
3046void Analyze::processCaptureAborted(
double time,
double exposureSeconds,
bool batchMode)
3048 removeTemporarySession(&temporaryCaptureSession);
3049 double duration = time - captureStartedTime;
3050 if (captureStartedTime >= 0 &&
3056 addSession(captureStartedTime, time, CAPTURE_Y,
failureBrush);
3057 auto session = CaptureSession(captureStartedTime, time,
nullptr,
true,
"",
3063 if (runtimeDisplay &&
keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3064 captureSessionClicked(session,
false);
3067 captureStartedTime = -1;
3069 previousCaptureStartedTime = -1;
3070 previousCaptureCompletedTime = -1;
3073void Analyze::resetCaptureState()
3075 captureStartedTime = -1;
3076 captureStartedFilter =
"";
3078 numCaptureStarsMax = 1;
3079 previousCaptureStartedTime = -1;
3080 previousCaptureCompletedTime = -1;
3083void Analyze::autofocusStarting(
double temperature,
const QString &filter,
const AutofocusReason reason,
3086 saveMessage(
"AutofocusStarting",
3092 processAutofocusStarting(logTime(), temperature, filter, reason, reasonInfo);
3095void Analyze::processAutofocusStarting(
double time,
double temperature,
const QString &filter,
const AutofocusReason reason,
3098 autofocusStartedTime = time;
3099 autofocusStartedFilter =
filter;
3100 autofocusStartedTemperature = temperature;
3101 autofocusStartedReason = reason;
3102 autofocusStartedReasonInfo = reasonInfo;
3104 addTemperature(temperature, time);
3107 addTemporarySession(&temporaryFocusSession, time, 1, FOCUS_Y, temporaryBrush);
3108 temporaryFocusSession.temperature = temperature;
3109 temporaryFocusSession.filter =
filter;
3110 temporaryFocusSession.reason = reason;
3113void Analyze::adaptiveFocusComplete(
const QString &filter,
double temperature,
double tempTicks,
3114 double altitude,
double altTicks,
int prevPosError,
int thisPosError,
3117 saveMessage(
"AdaptiveFocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8,%9,%10").arg(filter).arg(temperature, 0,
'f', 2)
3118 .arg(tempTicks, 0,
'f', 2).arg(altitude, 0,
'f', 2).arg(altTicks, 0,
'f', 2).arg(prevPosError)
3119 .arg(thisPosError).arg(totalTicks).arg(position).arg(
focuserMoved ? 1 : 0));
3122 processAdaptiveFocusComplete(logTime(), filter, temperature, tempTicks, altitude, altTicks, prevPosError, thisPosError,
3126void Analyze::processAdaptiveFocusComplete(
double time,
const QString &filter,
double temperature,
double tempTicks,
3127 double altitude,
double altTicks,
int prevPosError,
int thisPosError,
int totalTicks,
int position,
3130 removeTemporarySession(&temporaryFocusSession);
3132 addFocusPosition(position, time);
3137 if (!
focuserMoved || (
abs(tempTicks) < 1.00 &&
abs(altTicks) < 1.0 && prevPosError == 0 && thisPosError == 0))
3146 filter, temperature, tempTicks, altitude, altTicks, prevPosError, thisPosError, totalTicks,
3153 autofocusStartedTime = -1;
3156void Analyze::autofocusComplete(
const double temperature,
const QString &filter,
const QString &points,
3174 QString reasonInfo = autofocusStartedReasonInfo;
3176 if (curve.
size() == 0)
3177 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6").arg(temp, reason, reasonInfo, filter, points, weights));
3178 else if (title.
size() == 0)
3179 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7").arg(temp, reason, reasonInfo, filter, points, weights,
3182 saveMessage(
"AutofocusComplete",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8").arg(temp, reason, reasonInfo, filter, points, weights,
3185 if (runtimeDisplay && autofocusStartedTime >= 0)
3186 processAutofocusCompleteV2(logTime(), temperature, filter, autofocusStartedReason, reasonInfo, points, useWeights, curve,
3191void Analyze::processAutofocusCompleteV2(
double time,
const double temperature,
const QString &filter,
3192 const AutofocusReason reason,
const QString &reasonInfo,
3193 const QString &points,
const bool useWeights,
const QString &curve,
const QString &title,
bool batchMode)
3195 removeTemporarySession(&temporaryFocusSession);
3200 addSession(autofocusStartedTime, time, FOCUS_Y,
successBrush,
nullptr);
3202 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
true, temperature, filter, reason, reasonInfo, points,
3203 useWeights, curve, title, AutofocusFailReason::FOCUS_FAIL_NONE,
"");
3205 addFocusPosition(session.focusPosition(), autofocusStartedTime);
3209 if (runtimeDisplay &&
keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3210 focusSessionClicked(session,
false);
3213 autofocusStartedTime = -1;
3217void Analyze::processAutofocusComplete(
double time,
const QString &filter,
const QString &points,
3220 removeTemporarySession(&temporaryFocusSession);
3225 addSession(autofocusStartedTime, time, FOCUS_Y,
successBrush,
nullptr);
3226 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
true,
3227 autofocusStartedTemperature, filter, points, curve, title);
3229 addFocusPosition(session.focusPosition(), autofocusStartedTime);
3233 if (runtimeDisplay &&
keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3234 focusSessionClicked(session,
false);
3237 autofocusStartedTime = -1;
3240void Analyze::autofocusAborted(
const QString &filter,
const QString &points,
const bool useWeights,
3241 const AutofocusFailReason failCode,
const QString failCodeInfo)
3246 QString reasonInfo = autofocusStartedReasonInfo;
3250 saveMessage(
"AutofocusAborted",
QString(
"%1,%2,%3,%4,%5,%6,%7,%8").arg(temperature, reason, reasonInfo, filter, points,
3252 if (runtimeDisplay && autofocusStartedTime >= 0)
3253 processAutofocusAbortedV2(logTime(), autofocusStartedTemperature, filter, autofocusStartedReason, reasonInfo, points,
3254 useWeights, failCode, failCodeInfo);
3258void Analyze::processAutofocusAbortedV2(
double time,
double temperature,
const QString &filter,
3259 const AutofocusReason reason,
const QString &reasonInfo,
const QString &points,
const bool useWeights,
3260 const AutofocusFailReason failCode,
const QString failCodeInfo,
bool batchMode)
3262 removeTemporarySession(&temporaryFocusSession);
3263 double duration = time - autofocusStartedTime;
3264 if (autofocusStartedTime >= 0 && duration < 1000)
3267 addSession(autofocusStartedTime, time, FOCUS_Y,
failureBrush);
3268 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
false, autofocusStartedTemperature, filter, reason,
3269 reasonInfo, points, useWeights,
"",
"", failCode, failCodeInfo);
3274 if (runtimeDisplay &&
keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3275 focusSessionClicked(session,
false);
3278 autofocusStartedTime = -1;
3283void Analyze::processAutofocusAborted(
double time,
const QString &filter,
const QString &points,
bool batchMode)
3285 removeTemporarySession(&temporaryFocusSession);
3286 double duration = time - autofocusStartedTime;
3287 if (autofocusStartedTime >= 0 && duration < 1000)
3290 addSession(autofocusStartedTime, time, FOCUS_Y,
failureBrush);
3291 auto session = FocusSession(autofocusStartedTime, time,
nullptr,
false,
3292 autofocusStartedTemperature, filter, points,
"",
"");
3297 if (runtimeDisplay &&
keepCurrentCB->isChecked() && statsCursor ==
nullptr)
3298 focusSessionClicked(session,
false);
3301 autofocusStartedTime = -1;
3305void Analyze::resetAutofocusState()
3307 autofocusStartedTime = -1;
3308 autofocusStartedFilter =
"";
3309 autofocusStartedTemperature = 0;
3310 autofocusStartedReason = AutofocusReason::FOCUS_NONE;
3311 autofocusStartedReasonInfo =
"";
3320 if (str ==
i18n(
"Idle"))
3322 else if (str ==
i18n(
"Aborted"))
3323 return GUIDE_ABORTED;
3324 else if (str ==
i18n(
"Connected"))
3325 return GUIDE_CONNECTED;
3326 else if (str ==
i18n(
"Disconnected"))
3327 return GUIDE_DISCONNECTED;
3328 else if (str ==
i18n(
"Capturing"))
3329 return GUIDE_CAPTURE;
3330 else if (str ==
i18n(
"Looping"))
3331 return GUIDE_LOOPING;
3332 else if (str ==
i18n(
"Subtracting"))
3334 else if (str ==
i18n(
"Subframing"))
3335 return GUIDE_SUBFRAME;
3336 else if (str ==
i18n(
"Selecting star"))
3337 return GUIDE_STAR_SELECT;
3338 else if (str ==
i18n(
"Calibrating"))
3339 return GUIDE_CALIBRATING;
3340 else if (str ==
i18n(
"Calibration error"))
3341 return GUIDE_CALIBRATION_ERROR;
3342 else if (str ==
i18n(
"Calibrated"))
3343 return GUIDE_CALIBRATION_SUCCESS;
3344 else if (str ==
i18n(
"Guiding"))
3345 return GUIDE_GUIDING;
3346 else if (str ==
i18n(
"Suspended"))
3347 return GUIDE_SUSPENDED;
3348 else if (str ==
i18n(
"Reacquiring"))
3349 return GUIDE_REACQUIRE;
3350 else if (str ==
i18n(
"Dithering"))
3351 return GUIDE_DITHERING;
3352 else if (str ==
i18n(
"Manual Dithering"))
3353 return GUIDE_MANUAL_DITHERING;
3354 else if (str ==
i18n(
"Dithering error"))
3355 return GUIDE_DITHERING_ERROR;
3356 else if (str ==
i18n(
"Dithering successful"))
3357 return GUIDE_DITHERING_SUCCESS;
3358 else if (str ==
i18n(
"Settling"))
3359 return GUIDE_DITHERING_SETTLE;
3370 case GUIDE_CONNECTED:
3371 case GUIDE_DISCONNECTED:
3373 return Analyze::G_IDLE;
3375 return Analyze::G_GUIDING;
3378 case GUIDE_SUBFRAME:
3379 case GUIDE_STAR_SELECT:
3380 return Analyze::G_IGNORE;
3381 case GUIDE_CALIBRATING:
3382 case GUIDE_CALIBRATION_ERROR:
3383 case GUIDE_CALIBRATION_SUCCESS:
3384 return Analyze::G_CALIBRATING;
3385 case GUIDE_SUSPENDED:
3386 case GUIDE_REACQUIRE:
3387 return Analyze::G_SUSPENDED;
3388 case GUIDE_DITHERING:
3389 case GUIDE_MANUAL_DITHERING:
3390 case GUIDE_DITHERING_ERROR:
3391 case GUIDE_DITHERING_SUCCESS:
3392 case GUIDE_DITHERING_SETTLE:
3393 return Analyze::G_DITHERING;
3396 return Analyze::G_IDLE;
3401 switch (simpleState)
3403 case Analyze::G_IDLE:
3404 case Analyze::G_IGNORE:
3407 case Analyze::G_GUIDING:
3409 case Analyze::G_CALIBRATING:
3411 case Analyze::G_SUSPENDED:
3413 case Analyze::G_DITHERING:
3422void Analyze::guideState(Ekos::GuideState state)
3424 QString str = getGuideStatusString(state);
3425 saveMessage(
"GuideState", str);
3427 processGuideState(logTime(), str);
3430void Analyze::processGuideState(
double time,
const QString &
stateStr,
bool batchMode)
3434 if (state == G_IGNORE)
3436 if (state == lastGuideStateStarted)
3439 if (guideStateStartedTime >= 0)
3441 if (lastGuideStateStarted != G_IDLE)
3444 addSession(guideStateStartedTime, time, GUIDE_Y,
guideBrush(lastGuideStateStarted));
3445 guideSessions.add(GuideSession(guideStateStartedTime, time,
nullptr, lastGuideStateStarted));
3448 if (state == G_GUIDING)
3450 addTemporarySession(&temporaryGuideSession, time, 1, GUIDE_Y,
successBrush);
3451 temporaryGuideSession.simpleState = state;
3454 removeTemporarySession(&temporaryGuideSession);
3456 guideStateStartedTime = time;
3457 lastGuideStateStarted = state;
3463void Analyze::resetGuideState()
3465 lastGuideStateStarted = G_IDLE;
3466 guideStateStartedTime = -1;
3472 if (temperature > -200 && temperature != lastTemperature)
3475 lastTemperature = temperature;
3477 processTemperature(logTime(), temperature);
3481void Analyze::processTemperature(
double time,
double temperature,
bool batchMode)
3483 addTemperature(temperature, time);
3489void Analyze::resetTemperature()
3491 lastTemperature = -1000;
3494void Analyze::newTargetDistance(
double targetDistance)
3498 processTargetDistance(logTime(), targetDistance);
3501void Analyze::processTargetDistance(
double time,
double targetDistance,
bool batchMode)
3503 addTargetDistance(targetDistance, time);
3509void Analyze::guideStats(
double raError,
double decError,
int raPulse,
int decPulse,
3512 saveMessage(
"GuideStats",
QString(
"%1,%2,%3,%4,%5,%6,%7")
3523void Analyze::processGuideStats(
double time,
double raError,
double decError,
3532void Analyze::resetGuideStats()
3534 lastGuideStatsTime = -1;
3535 lastCaptureRmsTime = -1;
3547 for (
int i = 0; i < alignStates.size(); ++i)
3549 if (str ==
i18n(alignStates[i]))
3550 return static_cast<AlignState
>(i);
3584void Analyze::alignState(AlignState state)
3586 if (state == lastAlignStateReceived)
3588 lastAlignStateReceived = state;
3591 saveMessage(
"AlignState",
stateStr);
3593 processAlignState(logTime(),
stateStr);
3597void Analyze::processAlignState(
double time,
const QString &statusString,
bool batchMode)
3601 if (state == lastAlignStateStarted)
3609 if (state == ALIGN_COMPLETE || state == ALIGN_FAILED || state == ALIGN_ABORTED)
3612 addSession(lastAlignStateStartedTime, time, ALIGN_Y,
alignBrush(state));
3613 alignSessions.add(AlignSession(lastAlignStateStartedTime, time,
nullptr, state));
3617 addSession(lastAlignStateStartedTime, time, ALIGN_Y,
alignBrush(lastAlignStateStarted));
3618 alignSessions.add(AlignSession(lastAlignStateStartedTime, time,
nullptr, lastAlignStateStarted));
3625 addTemporarySession(&temporaryAlignSession, time, 1, ALIGN_Y, temporaryBrush);
3626 temporaryAlignSession.state = state;
3629 removeTemporarySession(&temporaryAlignSession);
3631 lastAlignStateStartedTime = time;
3632 lastAlignStateStarted = state;
3639void Analyze::resetAlignState()
3643 lastAlignStateStartedTime = -1;
3653 case ISD::Mount::MOUNT_IDLE:
3655 case ISD::Mount::MOUNT_ERROR:
3657 case ISD::Mount::MOUNT_MOVING:
3658 case ISD::Mount::MOUNT_SLEWING:
3660 case ISD::Mount::MOUNT_TRACKING:
3662 case ISD::Mount::MOUNT_PARKING:
3664 case ISD::Mount::MOUNT_PARKED:
3675void Analyze::mountState(ISD::Mount::Status state)
3678 saveMessage(
"MountState", statusString);
3680 processMountState(logTime(), statusString);
3683void Analyze::processMountState(
double time,
const QString &statusString,
bool batchMode)
3686 if (mountStateStartedTime >= 0 && lastMountState != ISD::Mount::MOUNT_IDLE)
3688 addSession(mountStateStartedTime, time, MOUNT_Y,
mountBrush(lastMountState));
3689 mountSessions.add(MountSession(mountStateStartedTime, time,
nullptr, lastMountState));
3692 if (state != ISD::Mount::MOUNT_IDLE)
3694 addTemporarySession(&temporaryMountSession, time, 1, MOUNT_Y,
3695 (state == ISD::Mount::MOUNT_TRACKING) ?
successBrush : temporaryBrush);
3696 temporaryMountSession.state = state;
3699 removeTemporarySession(&temporaryMountSession);
3701 mountStateStartedTime = time;
3702 lastMountState = state;
3708void Analyze::resetMountState()
3710 mountStateStartedTime = -1;
3711 lastMountState = ISD::Mount::Status::MOUNT_IDLE;
3715void Analyze::mountCoords(
const SkyPoint &position, ISD::Mount::PierSide pierSide,
const dms &haValue)
3717 double ra = position.
ra().Degrees();
3718 double dec = position.
dec().Degrees();
3719 double ha = haValue.
Degrees();
3720 double az = position.
az().Degrees();
3721 double alt = position.
alt().Degrees();
3730 (pierSide != lastMountPierSide))
3732 saveMessage(
"MountCoords",
QString(
"%1,%2,%3,%4,%5,%6")
3739 processMountCoords(logTime(), ra, dec, az, alt, pierSide, ha);
3746 lastMountPierSide = pierSide;
3750void Analyze::processMountCoords(
double time,
double ra,
double dec,
double az,
3751 double alt,
int pierSide,
double ha,
bool batchMode)
3753 addMountCoords(ra, dec, az, alt, pierSide, ha, time);
3759void Analyze::resetMountCoords()
3766 lastMountPierSide = -1;
3775 if (statusStr ==
"MOUNT_FLIP_NONE")
3776 return MeridianFlipState::MOUNT_FLIP_NONE;
3777 else if (statusStr ==
"MOUNT_FLIP_PLANNED")
3778 return MeridianFlipState::MOUNT_FLIP_PLANNED;
3779 else if (statusStr ==
"MOUNT_FLIP_WAITING")
3780 return MeridianFlipState::MOUNT_FLIP_WAITING;
3781 else if (statusStr ==
"MOUNT_FLIP_ACCEPTED")
3782 return MeridianFlipState::MOUNT_FLIP_ACCEPTED;
3783 else if (statusStr ==
"MOUNT_FLIP_RUNNING")
3784 return MeridianFlipState::MOUNT_FLIP_RUNNING;
3785 else if (statusStr ==
"MOUNT_FLIP_COMPLETED")
3786 return MeridianFlipState::MOUNT_FLIP_COMPLETED;
3787 else if (statusStr ==
"MOUNT_FLIP_ERROR")
3788 return MeridianFlipState::MOUNT_FLIP_ERROR;
3789 return MeridianFlipState::MOUNT_FLIP_ERROR;
3796 case MeridianFlipState::MOUNT_FLIP_NONE:
3798 case MeridianFlipState::MOUNT_FLIP_PLANNED:
3800 case MeridianFlipState::MOUNT_FLIP_WAITING:
3802 case MeridianFlipState::MOUNT_FLIP_ACCEPTED:
3804 case MeridianFlipState::MOUNT_FLIP_RUNNING:
3806 case MeridianFlipState::MOUNT_FLIP_COMPLETED:
3808 case MeridianFlipState::MOUNT_FLIP_ERROR:
3816void Analyze::mountFlipStatus(MeridianFlipState::MeridianFlipMountState state)
3818 if (state == lastMountFlipStateReceived)
3820 lastMountFlipStateReceived = state;
3822 QString stateStr = MeridianFlipState::meridianFlipStatusString(state);
3823 saveMessage(
"MeridianFlipState",
stateStr);
3825 processMountFlipState(logTime(),
stateStr);
3830void Analyze::processMountFlipState(
double time,
const QString &statusString,
bool batchMode)
3833 if (state == lastMountFlipStateStarted)
3837 (lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_PLANNED ||
3838 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_WAITING ||
3839 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_ACCEPTED ||
3840 lastMountFlipStateStarted == MeridianFlipState::MOUNT_FLIP_RUNNING);
3843 if (state == MeridianFlipState::MOUNT_FLIP_COMPLETED || state == MeridianFlipState::MOUNT_FLIP_ERROR)
3846 addSession(mountFlipStateStartedTime, time, MERIDIAN_MOUNT_FLIP_Y,
mountFlipStateBrush(state));
3847 mountFlipSessions.add(MountFlipSession(mountFlipStateStartedTime, time,
nullptr, state));
3851 addSession(mountFlipStateStartedTime, time, MERIDIAN_MOUNT_FLIP_Y,
mountFlipStateBrush(lastMountFlipStateStarted));
3852 mountFlipSessions.add(MountFlipSession(mountFlipStateStartedTime, time,
nullptr, lastMountFlipStateStarted));
3856 (state == MeridianFlipState::MOUNT_FLIP_PLANNED ||
3857 state == MeridianFlipState::MOUNT_FLIP_WAITING ||
3858 state == MeridianFlipState::MOUNT_FLIP_ACCEPTED ||
3859 state == MeridianFlipState::MOUNT_FLIP_RUNNING);
3862 addTemporarySession(&temporaryMountFlipSession, time, 1, MERIDIAN_MOUNT_FLIP_Y, temporaryBrush);
3863 temporaryMountFlipSession.state = state;
3866 removeTemporarySession(&temporaryMountFlipSession);
3868 mountFlipStateStartedTime = time;
3869 lastMountFlipStateStarted = state;
3875void Analyze::resetMountFlipState()
3877 lastMountFlipStateReceived = MeridianFlipState::MOUNT_FLIP_NONE;
3878 lastMountFlipStateStarted = MeridianFlipState::MOUNT_FLIP_NONE;
3879 mountFlipStateStartedTime = -1;
3886 {110, 120, 150}, {150, 180, 180}, {180, 165, 130}, {180, 200, 140}, {250, 180, 130},
3887 {190, 170, 160}, {140, 110, 160}, {250, 240, 190}, {250, 200, 220}, {150, 125, 175}
3891 auto it = schedulerJobColors.constFind(jobName);
3892 if (
it == schedulerJobColors.constEnd())
3894 const int numSoFar = schedulerJobColors.size();
3895 auto color = colors[
numSoFar % colors.size()];
3896 schedulerJobColors[jobName] = color;
3897 return QBrush(color, pattern);
3905void Analyze::schedulerJobStarted(
const QString &jobName)
3907 saveMessage(
"SchedulerJobStart", jobName);
3909 processSchedulerJobStarted(logTime(), jobName);
3913void Analyze::schedulerJobEnded(
const QString &jobName,
const QString &reason)
3915 saveMessage(
"SchedulerJobEnd",
QString(
"%1,%2").arg(jobName, reason));
3917 processSchedulerJobEnded(logTime(), jobName, reason);
3923void Analyze::processSchedulerJobStarted(
double time,
const QString &jobName)
3925 checkForMissingSchedulerJobEnd(time - 1);
3926 schedulerJobStartedTime = time;
3927 schedulerJobStartedJobName = jobName;
3930 addTemporarySession(&temporarySchedulerJobSession, time, 1, SCHEDULER_Y, schedulerJobBrush(jobName,
true));
3931 temporarySchedulerJobSession.jobName = jobName;
3935void Analyze::processSchedulerJobEnded(
double time,
const QString &jobName,
const QString &reason,
bool batchMode)
3937 removeTemporarySession(&temporarySchedulerJobSession);
3939 if (schedulerJobStartedTime < 0)
3945 addSession(schedulerJobStartedTime, time, SCHEDULER_Y, schedulerJobBrush(jobName,
false));
3946 auto session = SchedulerJobSession(schedulerJobStartedTime, time,
nullptr, jobName, reason);
3949 resetSchedulerJob();
3955void Analyze::checkForMissingSchedulerJobEnd(
double time)
3957 if (schedulerJobStartedTime < 0)
3959 removeTemporarySession(&temporarySchedulerJobSession);
3960 addSession(schedulerJobStartedTime, time, SCHEDULER_Y, schedulerJobBrush(schedulerJobStartedJobName,
false));
3961 auto session = SchedulerJobSession(schedulerJobStartedTime, time,
nullptr, schedulerJobStartedJobName,
"missing job end");
3964 resetSchedulerJob();
3967void Analyze::resetSchedulerJob()
3969 schedulerJobStartedTime = -1;
3970 schedulerJobStartedJobName =
"";
3973void Analyze::appendLogText(
const QString &text)
3975 m_LogText.insert(0,
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
3976 KStarsData::Instance()->lt().
toString(
"yyyy-MM-ddThh:mm:ss"), text));
3983void Analyze::clearLog()
static KStars * Instance()
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)
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)
A line from one point to another.
void setPen(const QPen &pen)
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)
@ foRowsFirst
Rows are filled first, and a new element is wrapped to the next column if the row count would exceed ...
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 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 number(KIO::filesize_t size)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KIOCORE_EXPORT void add(const QString &fileClass, const QString &directory)
const QList< QKeySequence > & begin()
const QList< QKeySequence > & zoomIn()
const QList< QKeySequence > & zoomOut()
const QList< QKeySequence > & next()
const QList< QKeySequence > & find()
const QList< QKeySequence > & end()
QString name(StandardShortcut id)
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 getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, Options options)
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QString toString(QDate date, FormatType format) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QString writableLocation(StandardLocation type)
QString & append(QChar ch)
QString arg(Args &&... args) 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)
QUrl fromLocalFile(const QString &localFile)
Used to keep track of the various Y-axes and connect them to the QLineEdits.