Kstars

scheduleraltitudegraph.cpp
1/*
2 SPDX-FileCopyrightText: 2024 Hy Murveit <hy@murveit.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "scheduleraltitudegraph.h"
8#include "ui_scheduleraltitudegraph.h"
9#include "kplotwidget.h"
10#include "kplotobject.h"
11#include "kplotaxis.h"
12#include "ksalmanac.h"
13#include "schedulerjob.h"
14#include "schedulerutils.h"
15
16// This is just to get the time (including simulated time).
17#include "schedulermodulestate.h"
18
19
20namespace Ekos
21{
22
23SchedulerAltitudeGraph::SchedulerAltitudeGraph(QWidget * parent) : QFrame(parent)
24{
25 setupUi(this);
26 setup();
27}
28
29void SchedulerAltitudeGraph::setState(QSharedPointer<SchedulerModuleState> state)
30{
31 m_State = state;
32}
33
34// The job table gets updated constantly (e.g. every second), so this
35// reduces the rate that the altitude graphs get regenerated.
36void SchedulerAltitudeGraph::tickle()
37{
38 if (m_State->jobs().isEmpty() ||
39 !m_AltitudeGraphUpdateTime.isValid() ||
40 m_AltitudeGraphUpdateTime.secsTo(SchedulerModuleState::getLocalTime()) > 120)
41 {
42 m_AltGraphDay = 0;
43 plot();
44 }
45}
46
47void SchedulerAltitudeGraph::next()
48{
49 m_AltGraphDay++;
50 if (m_AltGraphDay > 2)
51 m_AltGraphDay = 2;
52 plot();
53}
54void SchedulerAltitudeGraph::prev()
55{
56 m_AltGraphDay--;
57 if (m_AltGraphDay < 0)
58 m_AltGraphDay = 0;
59 plot();
60}
61
62void SchedulerAltitudeGraph::setup()
63{
64 // Hide the buttons and disable them
65 altMoveLeftB->setStyleSheet("background-color: black;");
66 altMoveRightB->setStyleSheet("background-color: black;");
67 altMoveLeftB->setIcon(QIcon());
68 altMoveRightB->setIcon(QIcon());
69 altMoveLeftB->setEnabled(false);
70 altMoveRightB->setEnabled(false);
71 altMoveLeftB->setFixedWidth(16);
72 altMoveRightB->setFixedWidth(16);
73 altMoveLeftB->setFixedHeight(16);
74 altMoveRightB->setFixedHeight(16);
75
76 altGraph->setAltitudeAxis(-20.0, 90.0);
77 altGraph->setTopPadding(0);
78 altGraph->setBottomPadding(25);
79 altGraph->setLeftPadding(25);
80 altGraph->setRightPadding(10);
81 altGraph->disableAxis(KPlotWidget::TopAxis);
82
83 connect(altMoveRightB, &QPushButton::clicked, this, &SchedulerAltitudeGraph::next);
84 connect(altMoveLeftB, &QPushButton::clicked, this, &SchedulerAltitudeGraph::prev);
85}
86
87void SchedulerAltitudeGraph::handleButtons(bool disable)
88{
89
90 if (disable)
91 {
92 m_AltGraphDay = 0;
93 altMoveLeftB->setEnabled(false);
94 altMoveLeftB->setIcon(QIcon());
95 altMoveRightB->setEnabled(false);
96 altMoveRightB->setIcon(QIcon());
97 }
98 else if (m_AltGraphDay == 1)
99 {
100 altMoveLeftB->setEnabled(true);
101 altMoveRightB->setEnabled(true);
102 altMoveLeftB->setIcon(QIcon::fromTheme("arrow-left"));
103 altMoveRightB->setIcon(QIcon::fromTheme("arrow-right"));
104 }
105 else if (m_AltGraphDay == 2)
106 {
107 altMoveLeftB->setEnabled(true);
108 altMoveLeftB->setIcon(QIcon::fromTheme("arrow-left"));
109 altMoveRightB->setEnabled(false);
110 altMoveRightB->setIcon(QIcon());
111 }
112 else
113 {
114 m_AltGraphDay = 0;
115 altMoveLeftB->setEnabled(false);
116 altMoveLeftB->setIcon(QIcon());
117 altMoveRightB->setEnabled(true);
118 altMoveRightB->setIcon(QIcon::fromTheme("arrow-right"));
119 }
120}
121
122namespace
123{
124int additionalOffset(const QList<SchedulerJob *> &jobs,
125 const QDateTime &now, const QDateTime &normalStart)
126{
127 QDateTime newStartTime;
128 for (int index = 0; index < jobs.size(); index++)
129 {
130 const auto job = jobs.at(index);
131 for (const auto &jobSchedule : job->getSimulatedSchedule())
132 {
133 const auto startTime = jobSchedule.startTime;
134 const auto stopTime = jobSchedule.stopTime;
135 if (!startTime.isValid())
136 continue;
137
138 if (startTime >= now && startTime < normalStart)
139 {
140 if (!newStartTime.isValid() || newStartTime > startTime)
141 newStartTime = startTime.addSecs(-1800);
142 }
143 else if (startTime < now && (!stopTime.isValid() || (stopTime > now)))
144 {
145 if (!newStartTime.isValid() || newStartTime > now)
146 newStartTime = now.addSecs(-1800);
147 }
148 }
149 }
150 if (newStartTime.isValid())
151 return static_cast<int>(0.99 + newStartTime.secsTo(normalStart) / 3600.0);
152 else return 0;
153}
154} // namespace
155
156void SchedulerAltitudeGraph::plot()
157{
158 if (m_State->jobs().size() == 0)
159 {
160 altGraph->removeAllPlotObjects();
161 altGraph->update();
162 m_AltGraphDay = 0;
163 altGraphLabel->setText("");
164 handleButtons(true);
165 return;
166 }
167 m_AltitudeGraphUpdateTime = SchedulerModuleState::getLocalTime();
168 const QDateTime now = SchedulerModuleState::getLocalTime().addDays(m_AltGraphDay), start, end;
169 QDateTime nextDawn, nextDusk;
170 SchedulerModuleState::calculateDawnDusk(now, nextDawn, nextDusk);
171 QDateTime plotStart = (nextDusk < nextDawn) ? nextDusk : nextDusk.addDays(-1);
172
173 // Normally start the plot 1 hour before dusk and end it an hour after dawn.
174 int startOffset = 1;
175
176 KStarsDateTime midnight = KStarsDateTime(now.date().addDays(1), QTime(0, 1), Qt::LocalTime);
177 // Midnight not quite right if it's in the wee hours before dawn.
178 // Then we use the midnight before now.
179 if (now.secsTo(nextDawn) < now.secsTo(nextDusk) && now.date() == nextDawn.date())
180 midnight = KStarsDateTime(now.date(), QTime(0, 1), Qt::LocalTime);
181 else if (now < nextDusk.addSecs(-startOffset * 3600))
182 {
183 // It's in the (day)time between dawn and dusk. If there is a job scheduled to run
184 // during this (day)time, then extend the startOffset to cover this run time.
185 startOffset += additionalOffset(m_State->jobs(), now,
186 plotStart.addSecs(-startOffset * 3600));
187 }
188
189 plotStart = plotStart.addSecs(-startOffset * 3600);
190 auto plotEnd = nextDawn.addSecs(startOffset * 3600);
191
192 const QString dayName = m_AltGraphDay == 1 ? i18n("Tomorrow") : (m_AltGraphDay == 2 ? i18n("Day After Tomorrow") :
193 i18n("Tonight"));
194 const QString plotTitle = QString("%1 -- %2 -- %3")
195 .arg(midnight.addSecs(-6 * 3600).date().toString("MMM d"))
196 .arg(dayName)
197 .arg(midnight.date().toString("MMM d"));
198 altGraphLabel->setText(plotTitle);
199
200 const KStarsDateTime ut = SchedulerModuleState::getGeo()->LTtoUT(KStarsDateTime(midnight));
201 KSAlmanac ksal(ut, SchedulerModuleState::getGeo());
202
203 handleButtons(false);
204
205 const int currentPosition = m_State->currentPosition();
206
207 for (int index = 0; index < m_State->jobs().size(); index++)
208 {
209 auto t = plotStart;
210 QVector<double> times, alts;
211 auto job = m_State->jobs().at(index);
212 while (t.secsTo(plotEnd) > 0)
213 {
214 double alt = SchedulerUtils::findAltitude(job->getTargetCoords(), t);
215 alts.push_back(alt);
216 double hour = midnight.secsTo(t) / 3600.0;
217 times.push_back(hour);
218 t = t.addSecs(60 * 10);
219 }
220
221 const int lineWidth = (index == currentPosition) ? 2 : 1;
222 if (index == 0)
223 altGraph->plot(SchedulerModuleState::getGeo(), &ksal, times, alts, lineWidth, Qt::white, job->getName());
224 else
225 altGraph->plotOverlay(times, alts, lineWidth, Qt::white, job->getName());
226 }
227
228 altGraph->setCurrentLine(currentPosition);
229
230 // The below isn't part of the above loop because we want the altitude plot objects created above
231 // to have the same indexes as the jobs to simplify the connection between clicking a line
232 // and knowing what job it is.
233 //
234 // Create additional plots overlaying the above, that are the intervals that the job is scheduled to run.
235 for (int index = 0; index < m_State->jobs().size(); index++)
236 {
237 auto job = m_State->jobs().at(index);
238 for (const auto &jobSchedule : job->getSimulatedSchedule())
239 {
240 auto startTime = jobSchedule.startTime;
241 auto stopTime = jobSchedule.stopTime;
242 if (!startTime.isValid())
243 continue;
244 if (startTime < plotStart)
245 startTime = plotStart;
246 if (!stopTime.isValid() || stopTime > plotEnd)
247 stopTime = plotEnd;
248 if (startTime.isValid() && startTime < plotEnd && stopTime.isValid() && stopTime > plotStart)
249 {
250 QVector<double> runTimes, runAlts;
251 auto t = startTime;
252 while (t.secsTo(stopTime) >= 0)
253 {
254 double alt = SchedulerUtils::findAltitude(job->getTargetCoords(), t);
255 runAlts.push_back(alt);
256 double hour = midnight.secsTo(t) / 3600.0;
257 runTimes.push_back(hour);
258 int secsToStop = t.secsTo(stopTime);
259 if (secsToStop <= 0) break;
260 t = t.addSecs(std::min(60 * 1, secsToStop));
261 }
262
263 altGraph->plotOverlay(runTimes, runAlts, 4, Qt::green);
264 }
265 }
266 }
267}
268}
A class that implements methods to find sun rise, sun set, twilight begin / end times,...
Definition ksalmanac.h:27
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
KStarsDateTime addSecs(double s) const
QString i18n(const char *text, const TYPE &arg...)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:83
const QList< QKeySequence > & end()
void clicked(bool checked)
QDate addDays(qint64 ndays) const const
QString toString(QStringView format, QCalendar cal) const const
QDateTime addDays(qint64 ndays) const const
QDateTime addSecs(qint64 s) const const
QDate date() const const
bool isValid() const const
qint64 secsTo(const QDateTime &other) const const
QIcon fromTheme(const QString &name)
const_reference at(qsizetype i) const const
void push_back(parameter_type value)
qsizetype size() const const
QString arg(Args &&... args) const const
LocalTime
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 11:58:36 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.