Kstars

linearguider.cpp
1/*
2 SPDX-FileCopyrightText: 2025 Hy Murveit <hy@murveit.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "linearguider.h"
8
9#include <cmath>
10#include "ekos_guide_debug.h"
11
12LinearGuider::LinearGuider(const QString &id) : m_ID(id)
13{
14 reset();
15}
16
17void LinearGuider::reset()
18{
19 m_Samples.clear();
20 m_SumTime = 0.0;
21 m_SumOffset = 0.0;
22 m_SumTimeSq = 0.0;
23 m_OffsetSq = 0.0;
24 m_SumTimeOffset = 0.0;
25 m_NumRejects = 0;
26 m_LastGuideTime = QDateTime();
27 m_GuiderIteration = 0;
28}
29double LinearGuider::guide(double offset)
30{
31 QString comment;
32
33 // Reset the guider if we haven't guided recently.
34 const QDateTime now = QDateTime::currentDateTime();
35 if (m_LastGuideTime.isValid())
36 {
37 constexpr int MAX_GUIDE_LAG = 30;
38 const int interval = m_LastGuideTime.secsTo(now);
39 if (interval < 0 || interval > MAX_GUIDE_LAG)
40 {
41 reset();
42 comment = QString("Reset: guide lag %1s").arg(interval);
43 }
44 }
45 else
46 reset();
47 m_LastGuideTime = now;
48
49 const double time = ++m_GuiderIteration;
50 const Sample s(time, offset);
51 double guideVal = m_Gain * offset;
52
53 addSample(s);
54 const int size = m_Samples.size();
55 if (size >= 4)
56 {
57 // Seems extreme, should at least min the m_MinMove with 1 or 2 a-s
58 constexpr double MIN_BAD_OFFSET = 2.0;
59 if (fabs(offset) > std::min(MIN_BAD_OFFSET, 4 * m_MinMove))
60 {
61 comment = QString("%1Reset: offset > 4*minMove %2").arg(!comment.isEmpty() ? ", " : "").arg(m_MinMove, 0, 'f', 2);
62 reset();
63 }
64 else
65 {
66 const double slope = getSlope();
67 if (std::isfinite(slope))
68 {
69 guideVal = slope * size * m_Gain;
70 // Don't allow the guide pulse to be the opposite direction of the offset.
71 if (guideVal * offset < 0)
72 {
73 guideVal = 0;
74 comment = QString("%1slope %2 opposite to offset").arg(!comment.isEmpty() ? ", " : "").arg(slope, 0, 'f', 2);
75 }
76 else
77 comment = QString("%1slope %2").arg(!comment.isEmpty() ? ", " : "").arg(slope, 0, 'f', 2);
78 }
79 else
80 comment = QString("%1bad slope").arg(!comment.isEmpty() ? ", " : "");
81 }
82 }
83 else
84 comment.append(QString("%1Starting").arg(!comment.isEmpty() ? ", " : ""));
85
86 if (fabs(guideVal) > fabs(offset))
87 {
88 comment.append(QString("%1Guideval %2 > offset").arg(!comment.isEmpty() ? ", " : "").arg(guideVal, 0, 'f', 2));
89 guideVal = m_Gain * offset;
90 m_NumRejects++;
91 if (m_NumRejects >= 3)
92 {
93 comment.append(QString("%1Reset: rejects %2").arg(!comment.isEmpty() ? ", " : "").arg(m_NumRejects));
94 reset();
95 }
96 }
97 else
98 m_NumRejects = 0;
99
100 if (fabs(guideVal) > 0 && fabs(guideVal) < m_MinMove)
101 {
102
103 comment.append(QString("%1%2 < minMove %3").arg(!comment.isEmpty() ? ", " : "")
104 .arg(guideVal, 0, 'f', 2).arg(m_MinMove, 0, 'f', 2));
105 guideVal = 0;
106 }
107
108 qCDebug(KSTARS_EKOS_GUIDE) << QString("LinearGuide(%1,%2) %3 * %4 --> %5: %6")
109 .arg(m_ID, 3).arg(time, 3, 'f', 0).arg(offset, 6, 'f', 2).arg(m_Gain, 4, 'f', 2)
110 .arg(guideVal, 5, 'f', 2).arg(comment);
111 return guideVal;
112}
113
114void LinearGuider::addSample(const Sample &sample)
115{
116 m_Samples.enqueue(sample);
117 updateStats(sample);
118 if (m_Samples.size() > m_Length)
119 removeLastSample();
120}
121
122void LinearGuider::updateStats(const Sample &sample)
123{
124 m_SumTime += sample.time;
125 m_SumOffset += sample.offset;
126 m_SumTimeSq += sample.time * sample.time;
127 m_OffsetSq += sample.offset * sample.offset;
128 m_SumTimeOffset += sample.time * sample.offset;
129}
130
131void LinearGuider::removeLastSample()
132{
133 if (m_Samples.size() <= 0)
134 return;
135 const Sample &sample = m_Samples.head();
136 m_SumTime -= sample.time;
137 m_SumOffset -= sample.offset;
138 m_SumTimeSq -= sample.time * sample.time;
139 m_OffsetSq -= sample.offset * sample.offset;
140 m_SumTimeOffset -= sample.time * sample.offset;
141 m_Samples.dequeue();
142}
143
144double LinearGuider::getSlope()
145{
146 const double sumTime = m_SumTime;
147 const double denom = (m_Samples.size() * m_SumTimeSq) - (sumTime * sumTime);
148 if (denom == 0) return 0;
149 const double slope = ((m_Samples.size() * m_SumTimeOffset) - (sumTime * m_SumOffset)) / denom;
150 return slope;
151}
152
KGuiItem reset()
QDateTime currentDateTime()
QString & append(QChar ch)
QString arg(Args &&... args) const const
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 11:58:35 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.